Repository navigation
Expand file tree
/
Copy pathMpscLinkedQueue.java
More file actions
139 lines (131 loc) · 5.61 KB
/
Copy pathMpscLinkedQueue.java
File metadata and controls
139 lines (131 loc) · 5.61 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/*
* 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.
*
* Original License: https://github.com/JCTools/JCTools/blob/master/LICENSE
* Original location: https://github.com/JCTools/JCTools/blob/master/jctools-core/src/main/java/org/jctools/queues/MpscLinkedQueue.java
*/
package rx.internal.util.unsafe;
import static rx.internal.util.unsafe.UnsafeAccess.UNSAFE;
import rx.internal.util.SuppressAnimalSniffer;
import rx.internal.util.atomic.LinkedQueueNode;
/**
* This is a direct Java port of the MPSC algorithm as presented <a
* href="http://www.1024cores.net/home/lock-free-algorithms/queues/non-intrusive-mpsc-node-based-queue"> on 1024
* Cores</a> by D. Vyukov. The original has been adapted to Java and it's quirks with regards to memory model and
* layout:
* <ol>
* <li>Use inheritance to ensure no false sharing occurs between producer/consumer node reference fields.
* <li>Use XCHG functionality to the best of the JDK ability (see differences in JDK7/8 impls).
* </ol>
* 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 <E>
*/
@SuppressAnimalSniffer
public final class MpscLinkedQueue<E> extends BaseLinkedQueue<E> {
public MpscLinkedQueue() {
consumerNode = new LinkedQueueNode<E>();
xchgProducerNode(consumerNode);// this ensures correct construction: StoreLoad
}
@SuppressWarnings("unchecked")
protected LinkedQueueNode<E> xchgProducerNode(LinkedQueueNode<E> newVal) {
Object oldVal;
do {
oldVal = producerNode;
} while(!UNSAFE.compareAndSwapObject(this, P_NODE_OFFSET, oldVal, newVal));
return (LinkedQueueNode<E>) oldVal;
}
/**
* {@inheritDoc} <br>
* <p>
* IMPLEMENTATION NOTES:<br>
* Offer is allowed from multiple threads.<br>
* Offer allocates a new node and:
* <ol>
* <li>Swaps it atomically with current producer node (only one producer 'wins')
* <li>Sets the new node as the node following from the swapped producer node
* </ol>
* 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 MessagePassingQueue#offer(Object)
* @see java.util.Queue#offer(java.lang.Object)
*/
@Override
public boolean offer(final E nextValue) {
if (nextValue == null) {
throw new NullPointerException("null elements not allowed");
}
final LinkedQueueNode<E> nextNode = new LinkedQueueNode<E>(nextValue);
final LinkedQueueNode<E> 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} <br>
* <p>
* IMPLEMENTATION NOTES:<br>
* Poll is allowed from a SINGLE thread.<br>
* Poll reads the next node from the consumerNode and:
* <ol>
* <li>If it is null, the queue is assumed empty (though it might not be).
* <li>If it is not null set it as the consumer node and return it's now evacuated value.
* </ol>
* 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 MessagePassingQueue#poll()
* @see java.util.Queue#poll()
*/
@Override
public E poll() {
LinkedQueueNode<E> currConsumerNode = lpConsumerNode(); // don't load twice, it's alright
LinkedQueueNode<E> nextNode = currConsumerNode.lvNext();
if (nextNode != null) {
// we have to null out the value because we are going to hang on to the node
final E nextValue = nextNode.getAndNullValue();
spConsumerNode(nextNode);
return nextValue;
}
else if (currConsumerNode != lvProducerNode()) {
// spin, we are no longer wait free
while((nextNode = currConsumerNode.lvNext()) == null); // NOPMD
// got the next node...
// we have to null out the value because we are going to hang on to the node
final E nextValue = nextNode.getAndNullValue();
consumerNode = nextNode;
return nextValue;
}
return null;
}
@Override
public E peek() {
LinkedQueueNode<E> currConsumerNode = consumerNode; // don't load twice, it's alright
LinkedQueueNode<E> 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); // NOPMD
// got the next node...
return nextNode.lpValue();
}
return null;
}
}