1919
2020package org .apache .iotdb .db .pipe .source .dataregion .realtime .disruptor ;
2121
22+ import java .util .concurrent .atomic .AtomicIntegerArray ;
2223import java .util .concurrent .locks .LockSupport ;
2324
2425public final class MultiProducerSequencer {
2526
27+ /** Ring buffer size (must be power of 2) - immutable after construction */
2628 private final int bufferSize ;
29+
30+ /**
31+ * Producer cursor tracking highest claimed sequence Updated via CAS in next() method Volatile
32+ * reads/writes handled by Sequence class
33+ */
2734 private final Sequence cursor = new Sequence ();
35+
36+ /**
37+ * Array of consumer sequences for backpressure control MUST be volatile for safe publication when
38+ * modified by SequenceGroups Array reference is replaced atomically via
39+ * AtomicReferenceFieldUpdater
40+ */
2841 volatile Sequence [] gatingSequences ;
2942
30- // CRITICAL: Cache to avoid repeated getMinimumSequence calls
43+ /**
44+ * Cached minimum gating sequence to reduce contention Updated opportunistically in next() to
45+ * avoid expensive array scan Does not need to be perfectly accurate (conservative is safe)
46+ */
3147 private final Sequence gatingSequenceCache = new Sequence ();
3248
33- // CRITICAL: Available buffer tracks published sequences
34- private final int [] availableBuffer ;
49+ /**
50+ * CRITICAL: Availability flags for tracking published sequences
51+ *
52+ * <p>Handles out-of-order publishing in multi-producer scenario: - Thread A claims seq 10, still
53+ * writing - Thread B claims seq 11, finishes and publishes - Consumer MUST wait for seq 10 before
54+ * reading seq 11
55+ *
56+ * <p>Memory visibility guarantees: - Writers use lazySet() for store-store barrier (cheaper than
57+ * volatile write) - Readers use get() for volatile read (ensures visibility across threads)
58+ *
59+ * <p>AtomicIntegerArray provides same semantics as Unsafe without reflection
60+ */
61+ private final AtomicIntegerArray availableBuffer ;
62+
63+ /** Mask for fast modulo: sequence & indexMask == sequence % bufferSize */
3564 private final int indexMask ;
65+
66+ /** Shift for calculating wrap count: sequence >>> indexShift */
3667 private final int indexShift ;
3768
3869 public MultiProducerSequencer (int bufferSize , Sequence [] gatingSequences ) {
@@ -45,7 +76,7 @@ public MultiProducerSequencer(int bufferSize, Sequence[] gatingSequences) {
4576
4677 this .bufferSize = bufferSize ;
4778 this .gatingSequences = gatingSequences != null ? gatingSequences : new Sequence [0 ];
48- this .availableBuffer = new int [ bufferSize ] ;
79+ this .availableBuffer = new AtomicIntegerArray ( bufferSize ) ;
4980 this .indexMask = bufferSize - 1 ;
5081 this .indexShift = log2 (bufferSize );
5182
@@ -73,8 +104,8 @@ public long next(int n) {
73104 current = cursor .get ();
74105 next = current + n ;
75106
76- long wrapPoint = next - bufferSize ;
77- long cachedGatingSequence = gatingSequenceCache .get ();
107+ final long wrapPoint = next - bufferSize ;
108+ final long cachedGatingSequence = gatingSequenceCache .get ();
78109
79110 if (wrapPoint > cachedGatingSequence || cachedGatingSequence > current ) {
80111 long gatingSequence = Sequence .getMinimumSequence (gatingSequences , current );
@@ -105,11 +136,14 @@ public void publish(long lo, long hi) {
105136 }
106137 }
107138
108- /** CORE: Check if available - MUST use Unsafe.getIntVolatile */
139+ /**
140+ * CORE: Check if sequence is available for consumption Uses volatile read to ensure visibility of
141+ * published sequences
142+ */
109143 public boolean isAvailable (long sequence ) {
110144 int index = calculateIndex (sequence );
111145 int flag = calculateAvailabilityFlag (sequence );
112- return availableBuffer [ index ] == flag ;
146+ return availableBuffer . get ( index ) == flag ;
113147 }
114148
115149 /** CORE: Get highest published - exact same algorithm */
@@ -178,25 +212,29 @@ public SequenceBarrier newBarrier(Sequence... sequencesToTrack) {
178212
179213 /** Initialize available buffer */
180214 private void initialiseAvailableBuffer () {
181- for (int i = availableBuffer .length - 1 ; i != 0 ; i --) {
215+ for (int i = availableBuffer .length () - 1 ; i != 0 ; i --) {
182216 setAvailableBufferValue (i , -1 );
183217 }
184218 setAvailableBufferValue (0 , -1 );
185219 }
186220
187221 /**
188- * CORE: Set available - MUST use Unsafe.putOrderedInt
222+ * CORE: Mark sequence as available for consumption
189223 *
190- * <p>putOrderedInt provides: - Store-store barrier (not full fence) - Cheaper than volatile write
191- * - Sufficient for this use case
224+ * <p>Uses lazySet() which provides: - Store-store barrier (ensures all prior writes are visible)
225+ * - Cheaper than full volatile write (no store-load barrier) - Sufficient for this use case
226+ * (readers use volatile get)
192227 */
193228 private void setAvailable (final long sequence ) {
194229 setAvailableBufferValue (calculateIndex (sequence ), calculateAvailabilityFlag (sequence ));
195230 }
196231
197- /** CRITICAL: Use Unsafe.putOrderedInt for correct memory semantics */
232+ /**
233+ * Set availability flag with release semantics lazySet() ensures previous event writes are
234+ * visible before flag update
235+ */
198236 private void setAvailableBufferValue (int index , int flag ) {
199- availableBuffer [ index ] = flag ;
237+ availableBuffer . lazySet ( index , flag ) ;
200238 }
201239
202240 /** Calculate availability flag */
0 commit comments