org.jctools_voltpatches.queues.atomic.MpscLinkedAtomicQueue Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of voltdbclient Show documentation
Show all versions of voltdbclient Show documentation
VoltDB client interface libraries
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jctools_voltpatches.queues.atomic;
/**
* This is a direct Java port of the MPSC algorithm as presented on 1024
* Cores by D. Vyukov. The original has been adapted to Java and it's quirks with regards to memory model and
* layout:
*
* - Use XCHG functionality provided by AtomicReference (which is better in JDK 8+).
*
* The queue is initialized with a stub node which is set to both the producer and consumer node references. From this
* point follow the notes on offer/poll.
*
* @author nitsanw
*
* @param
*/
public final class MpscLinkedAtomicQueue extends BaseLinkedAtomicQueue {
public MpscLinkedAtomicQueue() {
super();
LinkedQueueAtomicNode node = new LinkedQueueAtomicNode();
spConsumerNode(node);
xchgProducerNode(node);// this ensures correct construction: StoreLoad
}
/**
* {@inheritDoc}
*
* IMPLEMENTATION NOTES:
* Offer is allowed from multiple threads.
* Offer allocates a new node and:
*
* - Swaps it atomically with current producer node (only one producer 'wins')
*
- Sets the new node as the node following from the swapped producer node
*
* This works because each producer is guaranteed to 'plant' a new node and link the old node. No 2 producers can
* get the same producer node as part of XCHG guarantee.
*
* @see org.jctools_voltpatches.queues.MessagePassingQueue#offer(Object)
* @see java.util.Queue#offer(java.lang.Object)
*/
@Override
public final boolean offer(final E e) {
if (null == e) {
throw new NullPointerException();
}
final LinkedQueueAtomicNode nextNode = new LinkedQueueAtomicNode(e);
final LinkedQueueAtomicNode prevProducerNode = xchgProducerNode(nextNode);
// Should a producer thread get interrupted here the chain WILL be broken until that thread is resumed
// and completes the store in prev.next.
prevProducerNode.soNext(nextNode); // StoreStore
return true;
}
/**
* {@inheritDoc}
*
* IMPLEMENTATION NOTES:
* Poll is allowed from a SINGLE thread.
* Poll reads the next node from the consumerNode and:
*
* - If it is null, the queue is assumed empty (though it might not be).
*
- If it is not null set it as the consumer node and return it's now evacuated value.
*
* This means the consumerNode.value is always null, which is also the starting point for the queue. Because null
* values are not allowed to be offered this is the only node with it's value set to null at any one time.
*
* @see org.jctools_voltpatches.queues.MessagePassingQueue#poll()
* @see java.util.Queue#poll()
*/
@Override
public final E poll() {
LinkedQueueAtomicNode currConsumerNode = lpConsumerNode(); // don't load twice, it's alright
LinkedQueueAtomicNode nextNode = currConsumerNode.lvNext();
if (nextNode != null) {
return getSingleConsumerNodeValue(currConsumerNode, nextNode);
}
else if (currConsumerNode != lvProducerNode()) {
// spin, we are no longer wait free
while((nextNode = currConsumerNode.lvNext()) == null);
// got the next node...
return getSingleConsumerNodeValue(currConsumerNode, nextNode);
}
return null;
}
@Override
public final E peek() {
LinkedQueueAtomicNode currConsumerNode = lpConsumerNode(); // don't load twice, it's alright
LinkedQueueAtomicNode nextNode = currConsumerNode.lvNext();
if (nextNode != null) {
return nextNode.lpValue();
}
else if (currConsumerNode != lvProducerNode()) {
// spin, we are no longer wait free
while((nextNode = currConsumerNode.lvNext()) == null);
// got the next node...
return nextNode.lpValue();
}
return null;
}
}