Skip to content

Commit 439ed26

Browse files
committed
Remove counters
1 parent be5be56 commit 439ed26

File tree

4 files changed

+123
-122
lines changed

4 files changed

+123
-122
lines changed

kotlinx-coroutines-core/common/src/channels/BufferedChannel.kt

Lines changed: 41 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,12 @@ internal open class BufferedChannel<E>(
9090
private val receiveSegment: AtomicRef<ChannelSegment<E>>
9191
private val bufferEndSegment: AtomicRef<ChannelSegment<E>>
9292

93+
internal val sendSegmentId: Long get() = sendSegment.value.id
94+
internal val receiveSegmentId: Long get() = receiveSegment.value.id
95+
9396
init {
9497
@Suppress("LeakingThis")
95-
val firstSegment = ChannelSegment(id = 0, prev = null, channel = this, pointers = 3)
98+
val firstSegment = ChannelSegment(id = 0, prev = null, channel = this)
9699
sendSegment = atomic(firstSegment)
97100
receiveSegment = atomic(firstSegment)
98101
// If this channel is rendezvous or has unlimited capacity, the algorithm never
@@ -301,10 +304,6 @@ internal open class BufferedChannel<E>(
301304
when (updateCellSend(segment, i, element, s, waiter, closed)) {
302305
RESULT_RENDEZVOUS -> {
303306
// A rendezvous with a receiver has happened.
304-
// The previous segments are no longer needed
305-
// for the upcoming requests, so the algorithm
306-
// resets the link to the previous segment.
307-
segment.cleanPrev()
308307
return onRendezvousOrBuffered()
309308
}
310309
RESULT_BUFFERED -> {
@@ -325,17 +324,11 @@ internal open class BufferedChannel<E>(
325324
}
326325
RESULT_CLOSED -> {
327326
// This channel is closed.
328-
// In case this segment is already or going to be
329-
// processed by a receiver, ensure that all the
330-
// previous segments are unreachable.
331-
if (s < receiversCounter) segment.cleanPrev()
332327
return onClosed()
333328
}
334329
RESULT_FAILED -> {
335330
// Either the cell stores an interrupted receiver,
336331
// or it was poisoned by a concurrent receiver.
337-
// In both cases, all the previous segments are already processed,
338-
segment.cleanPrev()
339332
continue
340333
}
341334
RESULT_SUSPEND_NO_WAITER -> {
@@ -392,22 +385,16 @@ internal open class BufferedChannel<E>(
392385
// restarting the operation from the beginning on failure.
393386
// Check the `sendImpl(..)` function for the comments.
394387
when (updateCellSend(segment, index, element, s, waiter, false)) {
395-
RESULT_RENDEZVOUS -> {
396-
segment.cleanPrev()
397-
onRendezvousOrBuffered()
398-
}
399-
RESULT_BUFFERED -> {
388+
RESULT_RENDEZVOUS, RESULT_BUFFERED -> {
400389
onRendezvousOrBuffered()
401390
}
402391
RESULT_SUSPEND -> {
403392
waiter.prepareSenderForSuspension(segment, index)
404393
}
405394
RESULT_CLOSED -> {
406-
if (s < receiversCounter) segment.cleanPrev()
407395
onClosed()
408396
}
409397
RESULT_FAILED -> {
410-
segment.cleanPrev()
411398
sendImpl(
412399
element = element,
413400
waiter = waiter,
@@ -857,14 +844,9 @@ internal open class BufferedChannel<E>(
857844
when {
858845
updCellResult === FAILED -> {
859846
// The cell is poisoned; restart from the beginning.
860-
// To avoid memory leaks, we also need to reset
861-
// the `prev` pointer of the working segment.
862-
if (r < sendersCounter) segment.cleanPrev()
863847
}
864848
else -> { // element
865849
// A buffered element was retrieved from the cell.
866-
// Clean the reference to the previous segment.
867-
segment.cleanPrev()
868850
@Suppress("UNCHECKED_CAST")
869851
onUndeliveredElement?.callUndeliveredElementCatchingException(updCellResult as E)?.let { throw it }
870852
}
@@ -938,9 +920,6 @@ internal open class BufferedChannel<E>(
938920
// but failed: either the opposite request has
939921
// already been cancelled or the cell is poisoned.
940922
// Restart from the beginning in this case.
941-
// To avoid memory leaks, we also need to reset
942-
// the `prev` pointer of the working segment.
943-
if (r < sendersCounter) segment.cleanPrev()
944923
continue
945924
}
946925
updCellResult === SUSPEND_NO_WAITER -> {
@@ -951,8 +930,6 @@ internal open class BufferedChannel<E>(
951930
else -> { // element
952931
// Either a buffered element was retrieved from the cell
953932
// or a rendezvous with a waiting sender has happened.
954-
// Clean the reference to the previous segment before finishing.
955-
segment.cleanPrev()
956933
@Suppress("UNCHECKED_CAST")
957934
onElementRetrieved(updCellResult as E)
958935
}
@@ -987,7 +964,6 @@ internal open class BufferedChannel<E>(
987964
waiter.prepareReceiverForSuspension(segment, index)
988965
}
989966
updCellResult === FAILED -> {
990-
if (r < sendersCounter) segment.cleanPrev()
991967
receiveImpl(
992968
waiter = waiter,
993969
onElementRetrieved = onElementRetrieved,
@@ -996,7 +972,6 @@ internal open class BufferedChannel<E>(
996972
)
997973
}
998974
else -> {
999-
segment.cleanPrev()
1000975
@Suppress("UNCHECKED_CAST")
1001976
onElementRetrieved(updCellResult as E)
1002977
}
@@ -1211,7 +1186,7 @@ internal open class BufferedChannel<E>(
12111186
if (s <= b) {
12121187
// Should `bufferEndSegment` be moved forward to avoid memory leaks?
12131188
if (segment.id < id && segment.next != null)
1214-
moveSegmentBufferEndToSpecifiedOrLast(id, segment)
1189+
bufferEndSegment.moveToSpecifiedOrLast(id, segment)
12151190
// Increment the number of completed `expandBuffer()`-s and finish.
12161191
incCompletedExpandBufferAttempts()
12171192
return
@@ -2299,7 +2274,6 @@ internal open class BufferedChannel<E>(
22992274
// Otherwise, if the required segment is removed, the operation restarts.
23002275
if (receiveSegment.value.id < id) return false else continue
23012276
}
2302-
segment.cleanPrev() // all the previous segments are no longer needed.
23032277
// Does the `r`-th cell contain waiting sender or buffered element?
23042278
val i = (r % SEGMENT_SIZE).toInt()
23052279
if (isCellNonEmpty(segment, i, r)) return true
@@ -2398,12 +2372,6 @@ internal open class BufferedChannel<E>(
23982372
// This channel is already closed or cancelled; help to complete
23992373
// the closing or cancellation procedure.
24002374
completeCloseOrCancel()
2401-
// Clean the `prev` reference of the provided segment
2402-
// if all the previous cells are already covered by senders.
2403-
// It is important to clean the `prev` reference only in
2404-
// this case, as the closing/cancellation procedure may
2405-
// need correct value to traverse the linked list from right to left.
2406-
if (startFrom.id * SEGMENT_SIZE < receiversCounter) startFrom.cleanPrev()
24072375
// As the required segment is not found and cannot be allocated, return `null`.
24082376
null
24092377
} else {
@@ -2415,12 +2383,6 @@ internal open class BufferedChannel<E>(
24152383
// segment with `id` not lower than the required one.
24162384
// Skip the sequence of removed cells in O(1).
24172385
updateSendersCounterIfLower(segment.id * SEGMENT_SIZE)
2418-
// Clean the `prev` reference of the provided segment
2419-
// if all the previous cells are already covered by senders.
2420-
// It is important to clean the `prev` reference only in
2421-
// this case, as the closing/cancellation procedure may
2422-
// need correct value to traverse the linked list from right to left.
2423-
if (segment.id * SEGMENT_SIZE < receiversCounter) segment.cleanPrev()
24242386
// As the required segment is not found and cannot be allocated, return `null`.
24252387
null
24262388
} else {
@@ -2453,12 +2415,6 @@ internal open class BufferedChannel<E>(
24532415
// This channel is already closed or cancelled; help to complete
24542416
// the closing or cancellation procedure.
24552417
completeCloseOrCancel()
2456-
// Clean the `prev` reference of the provided segment
2457-
// if all the previous cells are already covered by senders.
2458-
// It is important to clean the `prev` reference only in
2459-
// this case, as the closing/cancellation procedure may
2460-
// need correct value to traverse the linked list from right to left.
2461-
if (startFrom.id * SEGMENT_SIZE < sendersCounter) startFrom.cleanPrev()
24622418
// As the required segment is not found and cannot be allocated, return `null`.
24632419
null
24642420
} else {
@@ -2474,12 +2430,6 @@ internal open class BufferedChannel<E>(
24742430
// segment with `id` not lower than the required one.
24752431
// Skip the sequence of removed cells in O(1).
24762432
updateReceiversCounterIfLower(segment.id * SEGMENT_SIZE)
2477-
// Clean the `prev` reference of the provided segment
2478-
// if all the previous cells are already covered by senders.
2479-
// It is important to clean the `prev` reference only in
2480-
// this case, as the closing/cancellation procedure may
2481-
// need correct value to traverse the linked list from right to left.
2482-
if (segment.id * SEGMENT_SIZE < sendersCounter) segment.cleanPrev()
24832433
// As the required segment is already removed, return `null`.
24842434
null
24852435
} else {
@@ -2504,7 +2454,7 @@ internal open class BufferedChannel<E>(
25042454
completeCloseOrCancel()
25052455
// Update `bufferEndSegment` to the last segment
25062456
// in the linked list to avoid memory leaks.
2507-
moveSegmentBufferEndToSpecifiedOrLast(id, startFrom)
2457+
bufferEndSegment.moveToSpecifiedOrLast(id, startFrom)
25082458
// When this function does not find the requested segment,
25092459
// it should update the number of completed `expandBuffer()` attempts.
25102460
incCompletedExpandBufferAttempts()
@@ -2534,32 +2484,6 @@ internal open class BufferedChannel<E>(
25342484
}
25352485
}
25362486

2537-
/**
2538-
* Updates [bufferEndSegment] to the one with the specified [id] or
2539-
* to the last existing segment, if the required segment is not yet created.
2540-
*
2541-
* Unlike [findSegmentBufferEnd], this function does not allocate new segments.
2542-
*/
2543-
private fun moveSegmentBufferEndToSpecifiedOrLast(id: Long, startFrom: ChannelSegment<E>) {
2544-
// Start searching the required segment from the specified one.
2545-
var segment: ChannelSegment<E> = startFrom
2546-
while (segment.id < id) {
2547-
segment = segment.next ?: break
2548-
}
2549-
// Skip all removed segments and try to update `bufferEndSegment`
2550-
// to the first non-removed one. This part should succeed eventually,
2551-
// as the tail segment is never removed.
2552-
while (true) {
2553-
while (segment.isRemoved) {
2554-
segment = segment.next ?: break
2555-
}
2556-
// Try to update `bufferEndSegment`. On failure,
2557-
// the found segment is already removed, so it
2558-
// should be skipped.
2559-
if (bufferEndSegment.moveForward(segment)) return
2560-
}
2561-
}
2562-
25632487
/**
25642488
* Updates the `senders` counter if its value
25652489
* is lower that the specified one.
@@ -2588,6 +2512,17 @@ internal open class BufferedChannel<E>(
25882512
if (receivers.compareAndSet(cur, value)) return
25892513
}
25902514

2515+
/**
2516+
This method is used in the physical removal of the segment. It helps to move pointers forward from
2517+
the segment which was physically removed.
2518+
*/
2519+
internal fun movePointersForwardFrom(from: ChannelSegment<E>) {
2520+
check(from.isRemoved) { "Trying to move channel pointers from the alive segment." }
2521+
if (from == sendSegment.value) sendSegment.moveToSpecifiedOrLast(from.id, from)
2522+
if (from == receiveSegment.value) receiveSegment.moveToSpecifiedOrLast(from.id, from)
2523+
if (from == bufferEndSegment.value) bufferEndSegment.moveToSpecifiedOrLast(from.id, from)
2524+
}
2525+
25912526
// ###################
25922527
// # Debug Functions #
25932528
// ###################
@@ -2799,7 +2734,7 @@ internal open class BufferedChannel<E>(
27992734
* to update [BufferedChannel.sendSegment], [BufferedChannel.receiveSegment],
28002735
* and [BufferedChannel.bufferEndSegment] correctly.
28012736
*/
2802-
internal class ChannelSegment<E>(id: Long, prev: ChannelSegment<E>?, channel: BufferedChannel<E>?, pointers: Int) : Segment<ChannelSegment<E>>(id, prev, pointers) {
2737+
internal class ChannelSegment<E>(id: Long, prev: ChannelSegment<E>?, channel: BufferedChannel<E>?) : Segment<ChannelSegment<E>>(id, prev) {
28032738
private val _channel: BufferedChannel<E>? = channel
28042739
val channel get() = _channel!! // always non-null except for `NULL_SEGMENT`
28052740

@@ -2841,6 +2776,26 @@ internal class ChannelSegment<E>(id: Long, prev: ChannelSegment<E>?, channel: Bu
28412776

28422777
internal fun getAndSetState(index: Int, update: Any?) = data[index * 2 + 1].getAndSet(update)
28432778

2779+
// ###################################################
2780+
// # Manipulation with the structure of segment list #
2781+
// ###################################################
2782+
2783+
/**
2784+
* Shows if all segments going before this segment have been processed.
2785+
* When the value is true, the [prev] reference of the segment should be `null`.
2786+
*/
2787+
override val isLeftmostOrProcessed: Boolean get() = id <= channel.sendSegmentId && id <= channel.receiveSegmentId
2788+
2789+
/**
2790+
* Removes the segment physically from the segment list.
2791+
*
2792+
* If the physical removal was successful and there are channel pointers pointing on this segment,
2793+
* the [BufferedChannel.movePointersForwardFrom] method is invoked to move them further on the segment list.
2794+
*/
2795+
override fun remove(): Boolean =
2796+
super.remove().also {
2797+
if (it) channel.movePointersForwardFrom(this)
2798+
}
28442799

28452800
// ########################
28462801
// # Cancellation Support #
@@ -2926,10 +2881,9 @@ internal fun <E> createSegmentFunction(): KFunction2<Long, ChannelSegment<E>, Ch
29262881
private fun <E> createSegment(id: Long, prev: ChannelSegment<E>) = ChannelSegment(
29272882
id = id,
29282883
prev = prev,
2929-
channel = prev.channel,
2930-
pointers = 0
2884+
channel = prev.channel
29312885
)
2932-
private val NULL_SEGMENT = ChannelSegment<Any?>(id = -1, prev = null, channel = null, pointers = 0)
2886+
private val NULL_SEGMENT = ChannelSegment<Any?>(id = -1, prev = null, channel = null)
29332887

29342888
/**
29352889
* Number of cells in each segment.

0 commit comments

Comments
 (0)