@@ -172,34 +172,10 @@ internal open class BufferedChannel<E>(
172
172
segment : ChannelSegment <E >,
173
173
index : Int
174
174
) {
175
- if (onUndeliveredElement == null ) {
176
- invokeOnCancellation(segment, index)
177
- } else {
178
- when (this ) {
179
- is CancellableContinuation <* > -> {
180
- invokeOnCancellation(SenderWithOnUndeliveredElementCancellationHandler (segment, index, context).asHandler)
181
- }
182
- is SelectInstance <* > -> {
183
- disposeOnCompletion(SenderWithOnUndeliveredElementCancellationHandler (segment, index, context))
184
- }
185
- is SendBroadcast -> {
186
- cont.invokeOnCancellation(SenderWithOnUndeliveredElementCancellationHandler (segment, index, cont.context).asHandler)
187
- }
188
- else -> error(" unexpected sender: $this " )
189
- }
190
- }
191
- }
192
-
193
- private inner class SenderWithOnUndeliveredElementCancellationHandler (
194
- private val segment : ChannelSegment <E >,
195
- private val index : Int ,
196
- private val context : CoroutineContext
197
- ) : BeforeResumeCancelHandler(), DisposableHandle {
198
- override fun dispose () {
199
- segment.onSenderCancellationWithOnUndeliveredElement(index, context)
200
- }
201
-
202
- override fun invoke (cause : Throwable ? ) = dispose()
175
+ // To distinguish cancelled senders and receivers,
176
+ // senders equip the index value with an additional marker,
177
+ // adding `SEGMENT_SIZE` to the value.
178
+ invokeOnCancellation(segment, index + SEGMENT_SIZE )
203
179
}
204
180
205
181
private fun onClosedSendOnNoWaiterSuspend (element : E , cont : CancellableContinuation <Unit >) {
@@ -1594,7 +1570,7 @@ internal open class BufferedChannel<E>(
1594
1570
* and [SelectInstance.trySelect]. When the channel becomes closed,
1595
1571
* [tryResumeHasNextOnClosedChannel] should be used instead.
1596
1572
*/
1597
- private inner class BufferedChannelIterator : ChannelIterator <E >, BeforeResumeCancelHandler (), Waiter {
1573
+ private inner class BufferedChannelIterator : ChannelIterator <E >, Waiter {
1598
1574
/* *
1599
1575
* Stores the element retrieved by [hasNext] or
1600
1576
* a special [CHANNEL_CLOSED] token if this channel is closed.
@@ -1607,20 +1583,7 @@ internal open class BufferedChannel<E>(
1607
1583
* continuation. The [tryResumeHasNext] and [tryResumeHasNextOnClosedChannel]
1608
1584
* function resume this continuation when the [hasNext] invocation should complete.
1609
1585
*/
1610
- private var continuation: CancellableContinuation <Boolean >? = null
1611
-
1612
- // When `hasNext()` suspends, the location where the continuation
1613
- // is stored is specified via the segment and the index in it.
1614
- // We need this information in the cancellation handler below.
1615
- private var segment: Segment <* >? = null
1616
- private var index = - 1
1617
-
1618
- /* *
1619
- * Invoked on cancellation, [BeforeResumeCancelHandler] implementation.
1620
- */
1621
- override fun invoke (cause : Throwable ? ) {
1622
- segment?.onCancellation(index, null )
1623
- }
1586
+ private var continuation: CancellableContinuationImpl <Boolean >? = null
1624
1587
1625
1588
// `hasNext()` is just a special receive operation.
1626
1589
override suspend fun hasNext (): Boolean =
@@ -1680,11 +1643,7 @@ internal open class BufferedChannel<E>(
1680
1643
}
1681
1644
1682
1645
override fun invokeOnCancellation (segment : Segment <* >, index : Int ) {
1683
- this .segment = segment
1684
- this .index = index
1685
- // It is possible that this `hasNext()` invocation is already
1686
- // resumed, and the `continuation` field is already updated to `null`.
1687
- this .continuation?.invokeOnCancellation(this .asHandler)
1646
+ this .continuation?.invokeOnCancellation(segment, index)
1688
1647
}
1689
1648
1690
1649
private fun onClosedHasNextNoWaiterSuspend () {
@@ -2826,67 +2785,51 @@ internal class ChannelSegment<E>(id: Long, prev: ChannelSegment<E>?, channel: Bu
2826
2785
// # Cancellation Support #
2827
2786
// ########################
2828
2787
2829
- override fun onCancellation (index : Int , cause : Throwable ? ) {
2830
- onCancellation(index)
2831
- }
2832
-
2833
- fun onSenderCancellationWithOnUndeliveredElement (index : Int , context : CoroutineContext ) {
2834
- // Read the element first. If the operation has not been successfully resumed
2835
- // (this cancellation may be caused by prompt cancellation during dispatching),
2836
- // it is guaranteed that the element is presented.
2788
+ override fun onCancellation (index : Int , cause : Throwable ? , context : CoroutineContext ) {
2789
+ // To distinguish cancelled senders and receivers, senders equip the index value with
2790
+ // an additional marker, adding `SEGMENT_SIZE` to the value.
2791
+ val isSender = index >= SEGMENT_SIZE
2792
+ // Unwrap the index.
2793
+ @Suppress(" NAME_SHADOWING" ) val index = if (isSender) index - SEGMENT_SIZE else index
2794
+ // Read the element, which may be needed further to call `onUndeliveredElement`.
2837
2795
val element = getElement(index)
2838
- // Perform the cancellation; `onCancellationImpl(..)` return `true` if the
2839
- // cancelled operation had not been resumed. In this case, the `onUndeliveredElement`
2840
- // lambda should be called.
2841
- if (onCancellation(index)) {
2842
- channel.onUndeliveredElement!! .callUndeliveredElement(element, context)
2843
- }
2844
- }
2845
-
2846
- /* *
2847
- * Returns `true` if the request is successfully cancelled,
2848
- * and no rendezvous has happened. We need this knowledge
2849
- * to keep [BufferedChannel.onUndeliveredElement] correct.
2850
- */
2851
- @Suppress(" ConvertTwoComparisonsToRangeCheck" )
2852
- fun onCancellation (index : Int ): Boolean {
2853
- // Count the global index of this cell and read
2854
- // the current counters of send and receive operations.
2855
- val globalIndex = id * SEGMENT_SIZE + index
2856
- val s = channel.sendersCounter
2857
- val r = channel.receiversCounter
2858
- // Update the cell state trying to distinguish whether
2859
- // the cancelled coroutine is sender or receiver.
2860
- var isSender: Boolean
2861
- var isReceiver: Boolean
2862
- while (true ) { // CAS-loop
2796
+ // Update the cell state.
2797
+ while (true ) {
2798
+ // CAS-loop
2863
2799
// Read the current state of the cell.
2864
- val cur = data[ index * 2 + 1 ].value
2800
+ val cur = getState( index)
2865
2801
when {
2866
2802
// The cell stores a waiter.
2867
2803
cur is Waiter || cur is WaiterEB -> {
2868
- // Is the cancelled request send for sure?
2869
- isSender = globalIndex < s && globalIndex >= r
2870
- // Is the cancelled request receiver for sure?
2871
- isReceiver = globalIndex < r && globalIndex >= s
2872
- // If the cancelled coroutine neither sender
2873
- // nor receiver, clean the element slot and finish.
2874
- // An opposite operation will resume this request
2875
- // and update the cell state eventually.
2876
- if (! isSender && ! isReceiver) {
2877
- cleanElement(index)
2878
- return true
2879
- }
2880
2804
// The cancelled request is either send or receive.
2881
2805
// Update the cell state correspondingly.
2882
2806
val update = if (isSender) INTERRUPTED_SEND else INTERRUPTED_RCV
2883
- if (data[index * 2 + 1 ].compareAndSet(cur, update)) break
2807
+ if (casState(index, cur, update)) {
2808
+ // The waiter has been successfully cancelled.
2809
+ // Clean the element slot and invoke `onSlotCleaned()`,
2810
+ // which may cause deleting the whole segment from the linked list.
2811
+ // In case the cancelled request is receiver, it is critical to ensure
2812
+ // that the `expandBuffer()` attempt that processes this cell is completed,
2813
+ // so `onCancelledRequest(..)` waits for its completion before invoking `onSlotCleaned()`.
2814
+ cleanElement(index)
2815
+ onCancelledRequest(index, ! isSender)
2816
+ // Call `onUndeliveredElement` if needed.
2817
+ if (isSender) {
2818
+ channel.onUndeliveredElement?.callUndeliveredElement(element, context)
2819
+ }
2820
+ return
2821
+ }
2884
2822
}
2885
2823
// The cell already indicates that the operation is cancelled.
2886
2824
cur == = INTERRUPTED_SEND || cur == = INTERRUPTED_RCV -> {
2887
- // Clean the element slot to avoid memory leaks and finish.
2825
+ // Clean the element slot to avoid memory leaks,
2826
+ // invoke `onUndeliveredElement` if needed, and finish
2888
2827
cleanElement(index)
2889
- return true
2828
+ // Call `onUndeliveredElement` if needed.
2829
+ if (isSender) {
2830
+ channel.onUndeliveredElement?.callUndeliveredElement(element, context)
2831
+ }
2832
+ return
2890
2833
}
2891
2834
// An opposite operation is resuming this request;
2892
2835
// wait until the cell state updates.
@@ -2897,23 +2840,13 @@ internal class ChannelSegment<E>(id: Long, prev: ChannelSegment<E>?, channel: Bu
2897
2840
cur == = RESUMING_BY_EB || cur == = RESUMING_BY_RCV -> continue
2898
2841
// This request was successfully resumed, so this cancellation
2899
2842
// is caused by the prompt cancellation feature and should be ignored.
2900
- cur == = DONE_RCV || cur == = BUFFERED -> return false
2843
+ cur == = DONE_RCV || cur == = BUFFERED -> return
2901
2844
// The cell state indicates that the channel is closed;
2902
2845
// this cancellation should be ignored.
2903
- cur == = CHANNEL_CLOSED -> {
2904
- return false
2905
- }
2846
+ cur == = CHANNEL_CLOSED -> return
2906
2847
else -> error(" unexpected state: $cur " )
2907
2848
}
2908
2849
}
2909
- // Clean the element slot and invoke `onSlotCleaned()`,
2910
- // which may cause deleting the whole segment from the linked list.
2911
- // In case the cancelled request is receiver, it is critical to ensure
2912
- // that the `expandBuffer()` attempt that processes this cell is completed,
2913
- // so `onCancelledRequest(..)` waits for its completion before invoking `onSlotCleaned()`.
2914
- cleanElement(index)
2915
- onCancelledRequest(index, isReceiver)
2916
- return true
2917
2850
}
2918
2851
2919
2852
/* *
0 commit comments