in kotlinx-coroutines-core/common/src/channels/BufferedChannel.kt [2869:2931]
override fun onCancellation(index: Int, cause: Throwable?, context: CoroutineContext) {
// To distinguish cancelled senders and receivers, senders equip the index value with
// an additional marker, adding `SEGMENT_SIZE` to the value.
val isSender = index >= SEGMENT_SIZE
// Unwrap the index.
@Suppress("NAME_SHADOWING") val index = if (isSender) index - SEGMENT_SIZE else index
// Read the element, which may be needed further to call `onUndeliveredElement`.
val element = getElement(index)
// Update the cell state.
while (true) {
// CAS-loop
// Read the current state of the cell.
val cur = getState(index)
when {
// The cell stores a waiter.
cur is Waiter || cur is WaiterEB -> {
// The cancelled request is either send or receive.
// Update the cell state correspondingly.
val update = if (isSender) INTERRUPTED_SEND else INTERRUPTED_RCV
if (casState(index, cur, update)) {
// The waiter has been successfully cancelled.
// Clean the element slot and invoke `onSlotCleaned()`,
// which may cause deleting the whole segment from the linked list.
// In case the cancelled request is receiver, it is critical to ensure
// that the `expandBuffer()` attempt that processes this cell is completed,
// so `onCancelledRequest(..)` waits for its completion before invoking `onSlotCleaned()`.
cleanElement(index)
onCancelledRequest(index, !isSender)
// Call `onUndeliveredElement` if needed.
if (isSender) {
channel.onUndeliveredElement?.callUndeliveredElement(element, context)
}
return
}
}
// The cell already indicates that the operation is cancelled.
cur === INTERRUPTED_SEND || cur === INTERRUPTED_RCV -> {
// Clean the element slot to avoid memory leaks,
// invoke `onUndeliveredElement` if needed, and finish
cleanElement(index)
// Call `onUndeliveredElement` if needed.
if (isSender) {
channel.onUndeliveredElement?.callUndeliveredElement(element, context)
}
return
}
// An opposite operation is resuming this request;
// wait until the cell state updates.
// It is possible that an opposite operation has already
// resumed this request, which will result in updating
// the cell state to `DONE_RCV` or `BUFFERED`, while the
// current cancellation is caused by prompt cancellation.
cur === RESUMING_BY_EB || cur === RESUMING_BY_RCV -> continue
// This request was successfully resumed, so this cancellation
// is caused by the prompt cancellation feature and should be ignored.
cur === DONE_RCV || cur === BUFFERED -> return
// The cell state indicates that the channel is closed;
// this cancellation should be ignored.
cur === CHANNEL_CLOSED -> return
else -> error("unexpected state: $cur")
}
}
}