@@ -17,7 +17,7 @@ import java.util.concurrent.atomic.AtomicReferenceFieldUpdater
17
17
class RendezvousChannelKoval <E >(
18
18
private val segmentSize : Int = 64 ,
19
19
private val spinThreshold : Int = 300 ,
20
- private val elemSpinThreshold : Int = 15
20
+ private val elemSpinThreshold : Int = 20
21
21
): ChannelKoval<E> {
22
22
// Waiting queue node
23
23
private class Node (segmentSize : Int , @JvmField val id : Int ) {
@@ -105,7 +105,6 @@ class RendezvousChannelKoval<E>(
105
105
// Main function in this chanel, which implements both `#send` and `#receive` operations.
106
106
// Note that `#offer` and `#poll` functions are just simplified versions of this one.
107
107
private suspend fun <T > sendOrReceiveSuspend (element : Any ) = suspendAtomicCancellableCoroutine<T >(holdCancellability = true ) sc@ { curCont ->
108
- var localCounterUpdate = 0
109
108
try_again@ while (true ) { // CAS loop
110
109
// Read the tail and its enqueue index at first, then the head and its indexes.
111
110
// It is important to read tail and its index at first. If algorithm
@@ -129,16 +128,16 @@ class RendezvousChannelKoval<E>(
129
128
}
130
129
// Queue is empty, try to add a new node with the current continuation.
131
130
if (addNewNode(head, curCont, element)) {
132
- if (localCounterUpdate > 0 ) { incElimSenderArraySize( 1 ); incElimReceiverArraySize( 1 ) }
131
+ incElimArraySizes( 1 )
133
132
return @sc
134
- } else { localCounterUpdate ++ }
133
+ }
135
134
} else {
136
135
// The `head` node is not full, therefore the waiting queue
137
136
// is empty. Try to add the current continuation to the queue.
138
137
if (storeContinuation(head, headEnqIdx, curCont, element)) {
139
- if (localCounterUpdate > 0 ) { incElimSenderArraySize( 1 ); incElimReceiverArraySize( 1 ) }
138
+ incElimArraySizes( 1 )
140
139
return @sc
141
- } else { localCounterUpdate ++ }
140
+ }
142
141
}
143
142
} else {
144
143
// The waiting queue is not empty and it is guaranteed that `headDeqIdx < headEnqIdx`.
@@ -156,8 +155,8 @@ class RendezvousChannelKoval<E>(
156
155
var firstElement = readElement(head, headDeqIdx)
157
156
if (firstElement == TAKEN_ELEMENT ) {
158
157
// Try to move the deque index in the `head` node
159
- localCounterUpdate++
160
158
deqIdxUpdater.compareAndSet(head, headDeqIdx, headDeqIdx + 1 )
159
+ incElimArraySizes(1 )
161
160
continue @try_again
162
161
}
163
162
// The `firstElement` is either sender or receiver. Check if a rendezvous is possible
@@ -179,7 +178,7 @@ class RendezvousChannelKoval<E>(
179
178
// Resume the current continuation
180
179
val result = (if (element == RECEIVER_ELEMENT ) firstElement else Unit ) as T
181
180
curCont.resume(result)
182
- if (localCounterUpdate > 0 ) { incElimSenderArraySize( 1 ); incElimReceiverArraySize( 1 ) }
181
+ incElimArraySizes( 1 )
183
182
return @sc
184
183
}
185
184
// Re-read the required pointers
@@ -193,14 +192,12 @@ class RendezvousChannelKoval<E>(
193
192
}
194
193
// Check that `(head.id, headDeqIdx) < (headIdLimit, headDeqIdxLimit)`
195
194
// and re-start the whole operation if needed
196
- if (head.id > headIdLimit || (head.id == headIdLimit && headDeqIdx >= headDeqIdxLimit)) {
197
- localCounterUpdate++
198
- continue @try_again
199
- }
195
+ if (head.id > headIdLimit || (head.id == headIdLimit && headDeqIdx >= headDeqIdxLimit)) continue @try_again
200
196
// Re-read the first element
201
197
firstElement = readElement(head, headDeqIdx)
202
198
if (firstElement == TAKEN_ELEMENT ) {
203
199
deqIdxUpdater.compareAndSet(head, headDeqIdx, headDeqIdx + 1 )
200
+ incElimArraySizes(1 )
204
201
continue @read_state
205
202
}
206
203
break @read_state
@@ -212,12 +209,12 @@ class RendezvousChannelKoval<E>(
212
209
// if the tail is full, otherwise try to store it at the `tailEnqIdx` index.
213
210
if (tailEnqIdx == segmentSize) {
214
211
if (addNewNode(tail, curCont, element)) {
215
- if (localCounterUpdate > 0 ) { incElimSenderArraySize( 1 ); incElimReceiverArraySize( 1 ) }
212
+ incElimArraySizes( 1 )
216
213
return @sc
217
214
}
218
215
} else {
219
216
if (storeContinuation(tail, tailEnqIdx, curCont, element)) {
220
- if (localCounterUpdate > 0 ) { incElimSenderArraySize( 1 ); incElimReceiverArraySize( 1 ) }
217
+ incElimArraySizes( 1 )
221
218
return @sc
222
219
}
223
220
}
@@ -227,10 +224,7 @@ class RendezvousChannelKoval<E>(
227
224
tailEnqIdx = tail._enqIdx
228
225
head = _head
229
226
headDeqIdx = head._deqIdx
230
- if (head.id > headIdLimit || (head.id == headIdLimit && headDeqIdx >= headDeqIdxLimit)) {
231
- localCounterUpdate++
232
- continue @try_again
233
- }
227
+ if (head.id > headIdLimit || (head.id == headIdLimit && headDeqIdx >= headDeqIdxLimit)) continue @try_again
234
228
}
235
229
}
236
230
}
@@ -240,8 +234,8 @@ class RendezvousChannelKoval<E>(
240
234
@JvmField var _elimSenderArraySize = 0
241
235
@JvmField var _elimReceiverArraySize = 0
242
236
243
- @JvmField var _elimsTotal = 0
244
- @JvmField var _elimsSucc = 0
237
+ // @JvmField var _elimsTotal = 0
238
+ // @JvmField var _elimsSucc = 0
245
239
246
240
private val _elimSenderArray = AtomicReferenceArray <Any >(ELIM_MAX_ARR_SIZE )
247
241
private val _elimReceiverArray = AtomicReferenceArray <Any >(ELIM_MAX_ARR_SIZE )
@@ -252,6 +246,11 @@ class RendezvousChannelKoval<E>(
252
246
return value
253
247
}
254
248
249
+ private fun incElimArraySizes (value : Int ) {
250
+ incElimSenderArraySize(value)
251
+ incElimReceiverArraySize(value)
252
+ }
253
+
255
254
private fun incElimSenderArraySize (value : Int ) {
256
255
val newVal = limitElimCounterValue(_elimSenderArraySize + value)
257
256
_elimSenderArraySize = newVal
@@ -277,28 +276,27 @@ class RendezvousChannelKoval<E>(
277
276
private fun tryEliminateSender (element : Any ): Unit? {
278
277
val elimReceiverArraySize = _elimReceiverArraySize
279
278
if (elimReceiverArraySize > 0 ) {
280
- _elimsTotal ++
279
+ // _elimsTotal++
281
280
val position = ThreadLocalRandom .current().nextInt(elimReceiverArraySize)
282
281
attempt@ for (i in max(0 , position - 1 ) .. min(position + 1 , elimReceiverArraySize - 1 )) {
283
282
val x = _elimReceiverArray [i]
284
283
when (x) {
285
- null -> { continue @attempt }
284
+ null -> { decElimReceiverArraySize( 1 ); continue @attempt }
286
285
ELIM_RECEIVER_ELEMENT -> {
287
286
if (_elimReceiverArray .compareAndSet(i, x, Done (element))) {
288
- _elimsSucc ++
287
+ // _elimsSucc++
289
288
return Unit
290
289
} else incElimReceiverArraySize(1 )
291
290
}
292
291
else -> incElimReceiverArraySize(1 )
293
292
}
294
293
}
295
294
// Elimination was unsuccessful :(
296
- decElimReceiverArraySize(1 )
297
295
}
298
296
299
297
val elimSenderArraySize = _elimSenderArraySize
300
298
if (elimSenderArraySize > 0 ) {
301
- _elimsTotal ++
299
+ // _elimsTotal++
302
300
val position = ThreadLocalRandom .current().nextInt(elimSenderArraySize)
303
301
attempt@ for (i in max(0 , position - 1 ) .. min(position + 1 , elimSenderArraySize - 1 )) {
304
302
val x = _elimSenderArray [i]
@@ -311,23 +309,23 @@ class RendezvousChannelKoval<E>(
311
309
val probablyDone = _elimSenderArray [i]
312
310
if (probablyDone == ELIM_SENDER_DONE ) {
313
311
_elimSenderArray [i] = null
314
- _elimsSucc ++
312
+ incElimSenderArraySize(1 )
313
+ // _elimsSucc++
315
314
return Unit
316
315
}
317
316
}
317
+ decElimSenderArraySize(1 )
318
318
if (! _elimSenderArray .compareAndSet(i, box, null )) {
319
319
// _elimSenderArray[i] == ELIM_SENDER_DONE
320
- incElimSenderArraySize(1 )
321
320
_elimSenderArray [i] = null
322
- _elimsSucc ++
321
+ // _elimsSucc++
323
322
return Unit
324
323
}
325
324
} else incElimSenderArraySize(1 )
326
325
}
327
326
else -> incElimSenderArraySize(1 )
328
327
}
329
328
}
330
- decElimSenderArraySize(1 )
331
329
}
332
330
333
331
return null
@@ -336,28 +334,27 @@ class RendezvousChannelKoval<E>(
336
334
private fun tryEliminateReceiver (): Any? {
337
335
val elimSenderArraySize = _elimSenderArraySize
338
336
if (elimSenderArraySize > 0 ) {
339
- _elimsTotal ++
337
+ // _elimsTotal++
340
338
val position = ThreadLocalRandom .current().nextInt(elimSenderArraySize)
341
339
attempt@ for (i in max(0 , position - 1 ) .. min(position + 1 , elimSenderArraySize - 1 )) {
342
340
val x = _elimSenderArray [i]
343
341
when (x) {
344
- null -> continue @attempt
342
+ null -> { decElimSenderArraySize( 1 ); continue @attempt }
345
343
is ElementBox -> {
346
344
if (_elimSenderArray .compareAndSet(i, x, ELIM_SENDER_DONE )) {
347
- _elimsSucc ++
345
+ // _elimsSucc++
348
346
return x.value
349
347
} else incElimSenderArraySize(1 )
350
348
}
351
349
else -> incElimSenderArraySize(1 )
352
350
}
353
351
}
354
352
// Elimination was unsuccessful :(
355
- decElimSenderArraySize(1 )
356
353
}
357
354
358
355
val elimReceiverArraySize = _elimReceiverArraySize
359
356
if (elimReceiverArraySize > 0 ) {
360
- _elimsTotal ++
357
+ // _elimsTotal++
361
358
val position = ThreadLocalRandom .current().nextInt(elimReceiverArraySize)
362
359
attempt@ for (i in max(0 , position - 1 ) .. min(position + 1 , elimReceiverArraySize - 1 )) {
363
360
val x = _elimReceiverArray [i]
@@ -369,15 +366,16 @@ class RendezvousChannelKoval<E>(
369
366
if (probablyDone is Done ) {
370
367
val res = probablyDone.value
371
368
_elimReceiverArray [i] = null
372
- _elimsSucc ++
369
+ incElimReceiverArraySize(1 )
370
+ // _elimsSucc++
373
371
return res
374
372
}
375
373
}
374
+ decElimReceiverArraySize(1 )
376
375
if (! _elimReceiverArray .compareAndSet(i, ELIM_RECEIVER_ELEMENT , null )) {
377
376
val done = _elimReceiverArray [i] as Done
378
377
_elimReceiverArray [i] = null
379
- incElimReceiverArraySize(1 )
380
- _elimsSucc ++
378
+ // _elimsSucc++
381
379
return done.value
382
380
}
383
381
} else incElimReceiverArraySize(1 )
@@ -386,7 +384,6 @@ class RendezvousChannelKoval<E>(
386
384
}
387
385
}
388
386
// Elimination was unsuccessful :(
389
- decElimSenderArraySize(1 )
390
387
}
391
388
392
389
return null
@@ -403,7 +400,6 @@ class RendezvousChannelKoval<E>(
403
400
404
401
// This method is based on `#sendOrReceiveSuspend`. Returns `null` if fails.
405
402
private fun <T > sendOrReceiveNonSuspend (element : Any ): T ? {
406
- var localCounterUpdate = 0
407
403
try_again@ while (true ) { // CAS loop
408
404
// Read the tail and its enqueue index at first, then the head and its indexes.
409
405
val tail = _tail
@@ -414,19 +410,19 @@ class RendezvousChannelKoval<E>(
414
410
// If the waiting queue is empty, `headDeqIdx == headEnqIdx`.
415
411
// This can also happen if the `head` node is full (`headDeqIdx == segmentSize`).
416
412
if (headDeqIdx == headEnqIdx) {
413
+ val res = tryEliminate(head, headEnqIdx, element)
414
+ if (res != null ) return res as T
417
415
if (headDeqIdx == segmentSize) {
418
416
// The `head` node is full. Try to move `_head`
419
417
// pointer forward and start the operation again.
420
418
if (adjustHead(head)) continue @try_again
421
419
// Queue is empty, try to do elimination
422
420
// and return `null` if it fails.
423
- if (localCounterUpdate > 0 ) { incElimSenderArraySize(1 ); incElimReceiverArraySize(1 ) }
424
- return tryEliminate(head, headEnqIdx, element) as T
421
+ return null
425
422
} else {
426
423
// Queue is empty, try to do elimination
427
424
// and return `null` if it fails.
428
- if (localCounterUpdate > 0 ) { incElimSenderArraySize(1 ); incElimReceiverArraySize(1 ) }
429
- return tryEliminate(head, headEnqIdx, element) as T
425
+ return null
430
426
}
431
427
} else {
432
428
// The waiting queue is not empty and it is guaranteed that `headDeqIdx < headEnqIdx`.
@@ -436,7 +432,7 @@ class RendezvousChannelKoval<E>(
436
432
if (firstElement == TAKEN_ELEMENT ) {
437
433
// Try to move the deque index in the `head` node
438
434
deqIdxUpdater.compareAndSet(head, headDeqIdx, headDeqIdx + 1 )
439
- localCounterUpdate ++
435
+ incElimArraySizes( 1 )
440
436
continue @try_again
441
437
}
442
438
val makeRendezvous = if (element == RECEIVER_ELEMENT ) firstElement != RECEIVER_ELEMENT else firstElement == RECEIVER_ELEMENT
@@ -446,7 +442,7 @@ class RendezvousChannelKoval<E>(
446
442
while (true ) {
447
443
if (tryResumeContinuation(head, headDeqIdx, element)) {
448
444
// The rendezvous is happened, congratulations!
449
- if (localCounterUpdate > 0 ) { incElimSenderArraySize( 1 ); incElimReceiverArraySize( 1 ) }
445
+ incElimArraySizes( 1 )
450
446
return (if (element == RECEIVER_ELEMENT ) firstElement else Unit ) as T
451
447
}
452
448
// Re-read the required pointers
@@ -455,26 +451,23 @@ class RendezvousChannelKoval<E>(
455
451
head = _head
456
452
headDeqIdx = head._deqIdx
457
453
if (headDeqIdx == segmentSize) {
458
- if (! adjustHead(head)) { localCounterUpdate ++ ; continue @try_again }
454
+ if (! adjustHead(head)) continue @try_again
459
455
continue @read_state
460
456
}
461
457
// Check that `(head.id, headDeqIdx) < (headIdLimit, headDeqIdxLimit)`
462
458
// and re-start the whole operation if needed
463
- if (head.id > headIdLimit || (head.id == headIdLimit && headDeqIdx >= headDeqIdxLimit)) {
464
- localCounterUpdate++
465
- continue @try_again
466
- }
459
+ if (head.id > headIdLimit || (head.id == headIdLimit && headDeqIdx >= headDeqIdxLimit)) continue @try_again
467
460
// Re-read the first element
468
461
firstElement = readElement(head, headDeqIdx)
469
462
if (firstElement == TAKEN_ELEMENT ) {
463
+ incElimArraySizes(1 )
470
464
deqIdxUpdater.compareAndSet(head, headDeqIdx, headDeqIdx + 1 )
471
465
continue @read_state
472
466
}
473
467
break @read_state
474
468
}
475
469
}
476
470
} else {
477
- if (localCounterUpdate > 0 ) { incElimSenderArraySize(1 ); incElimReceiverArraySize(1 ) }
478
471
return null
479
472
}
480
473
}
0 commit comments