@@ -14,7 +14,6 @@ import kotlin.jvm.*
14
14
15
15
/* *
16
16
* Abstract send channel. It is a base class for all send channel implementations.
17
- *
18
17
*/
19
18
internal abstract class AbstractSendChannel <E > : SendChannel <E > {
20
19
/* * @suppress **This is unstable API and it is subject to change.** */
@@ -580,7 +579,7 @@ internal abstract class AbstractChannel<E> : AbstractSendChannel<E>(), Channel<E
580
579
581
580
@Suppress(" UNCHECKED_CAST" )
582
581
private suspend fun receiveSuspend (): E = suspendAtomicCancellableCoroutine(holdCancellability = true ) sc@ { cont ->
583
- val receive = ReceiveElement (cont as CancellableContinuation <E ?>, nullOnClose = false )
582
+ val receive = ReceiveElement < E > (cont as CancellableContinuation <Any ?>, ResumeMode . THROW_ON_CLOSE )
584
583
while (true ) {
585
584
if (enqueueReceive(receive)) {
586
585
cont.initCancellability() // make it properly cancellable
@@ -628,7 +627,7 @@ internal abstract class AbstractChannel<E> : AbstractSendChannel<E>(), Channel<E
628
627
629
628
@Suppress(" UNCHECKED_CAST" )
630
629
private suspend fun receiveOrNullSuspend (): E ? = suspendAtomicCancellableCoroutine(holdCancellability = true ) sc@ { cont ->
631
- val receive = ReceiveElement (cont, nullOnClose = true )
630
+ val receive = ReceiveElement < E > (cont as CancellableContinuation < Any ?>, ResumeMode . NULL_ON_CLOSE )
632
631
while (true ) {
633
632
if (enqueueReceive(receive)) {
634
633
cont.initCancellability() // make it properly cancellable
@@ -651,13 +650,42 @@ internal abstract class AbstractChannel<E> : AbstractSendChannel<E>(), Channel<E
651
650
}
652
651
}
653
652
653
+ @Suppress(" UNCHECKED_CAST" )
654
+ public final override suspend fun receiveOrClosed (): ReceiveResult <E > {
655
+ // fast path -- try poll non-blocking
656
+ val result = pollInternal()
657
+ if (result != = POLL_FAILED ) {
658
+ return result.toResult()
659
+ }
660
+ // slow-path does suspend
661
+ return receiveOrClosedSuspend()
662
+ }
663
+
664
+ @Suppress(" UNCHECKED_CAST" )
665
+ private suspend fun receiveOrClosedSuspend (): ReceiveResult <E > = suspendAtomicCancellableCoroutine(holdCancellability = true ) sc@{ cont ->
666
+ val receive = ReceiveElement <E >(cont as CancellableContinuation <Any ?>, ResumeMode .RECEIVE_RESULT )
667
+ while (true ) {
668
+ if (enqueueReceive(receive)) {
669
+ cont.initCancellability()
670
+ removeReceiveOnCancel(cont, receive)
671
+ return @sc
672
+ }
673
+
674
+ val result = pollInternal()
675
+ if (result is Closed <* > || result != = POLL_FAILED ) {
676
+ cont.resume(result.toResult<E >())
677
+ return @sc
678
+ }
679
+ }
680
+ }
681
+
654
682
@Suppress(" UNCHECKED_CAST" )
655
683
public final override fun poll (): E ? {
656
684
val result = pollInternal()
657
685
return if (result == = POLL_FAILED ) null else receiveOrNullResult(result)
658
686
}
659
687
660
- override fun cancel (): Unit {
688
+ override fun cancel () {
661
689
cancel(null )
662
690
}
663
691
@@ -712,9 +740,9 @@ internal abstract class AbstractChannel<E> : AbstractSendChannel<E>(), Channel<E
712
740
713
741
private inner class TryEnqueueReceiveDesc <E , R >(
714
742
select : SelectInstance <R >,
715
- block : suspend (E ? ) -> R ,
716
- nullOnClose : Boolean
717
- ) : AddLastDesc<ReceiveSelect<R, E>>(queue, ReceiveSelect (select, block, nullOnClose )) {
743
+ block : suspend (Any ? ) -> R ,
744
+ mode : ResumeMode
745
+ ) : AddLastDesc<ReceiveSelect<R, E>>(queue, ReceiveSelect (select, block, mode )) {
718
746
override fun failure (affected : LockFreeLinkedListNode , next : Any ): Any? {
719
747
if (affected is Send ) return ENQUEUE_FAILED
720
748
return null
@@ -746,7 +774,7 @@ internal abstract class AbstractChannel<E> : AbstractSendChannel<E>(), Channel<E
746
774
while (true ) {
747
775
if (select.isSelected) return
748
776
if (isEmpty) {
749
- val enqueueOp = TryEnqueueReceiveDesc (select, block as (suspend (E ? ) -> R ), nullOnClose = false )
777
+ val enqueueOp = TryEnqueueReceiveDesc < E , R > (select, block as (suspend (Any ? ) -> R ), ResumeMode . THROW_ON_CLOSE )
750
778
val enqueueResult = select.performAtomicIfNotSelected(enqueueOp) ? : return
751
779
when {
752
780
enqueueResult == = ALREADY_SELECTED -> return
@@ -780,7 +808,7 @@ internal abstract class AbstractChannel<E> : AbstractSendChannel<E>(), Channel<E
780
808
while (true ) {
781
809
if (select.isSelected) return
782
810
if (isEmpty) {
783
- val enqueueOp = TryEnqueueReceiveDesc (select, block, nullOnClose = true )
811
+ val enqueueOp = TryEnqueueReceiveDesc < E , R > (select, block as suspend ( Any? ) -> R , ResumeMode . NULL_ON_CLOSE )
784
812
val enqueueResult = select.performAtomicIfNotSelected(enqueueOp) ? : return
785
813
when {
786
814
enqueueResult == = ALREADY_SELECTED -> return
@@ -810,6 +838,43 @@ internal abstract class AbstractChannel<E> : AbstractSendChannel<E>(), Channel<E
810
838
}
811
839
}
812
840
841
+ override val onReceiveOrClosed: SelectClause1 <ReceiveResult <E >>
842
+ get() = object : SelectClause1 <ReceiveResult <E >> {
843
+ override fun <R > registerSelectClause1 (select : SelectInstance <R >, block : suspend (ReceiveResult <E >) -> R ) {
844
+ registerSelectReceiveOrClosed(select, block)
845
+ }
846
+ }
847
+
848
+ @Suppress(" UNCHECKED_CAST" )
849
+ private fun <R > registerSelectReceiveOrClosed (select : SelectInstance <R >, block : suspend (ReceiveResult <E >) -> R ) {
850
+ while (true ) {
851
+ if (select.isSelected) return
852
+ if (isEmpty) {
853
+ val enqueueOp = TryEnqueueReceiveDesc <E , R >(select, block as suspend (Any? ) -> R , ResumeMode .RECEIVE_RESULT )
854
+ val enqueueResult = select.performAtomicIfNotSelected(enqueueOp) ? : return
855
+ when {
856
+ enqueueResult == = ALREADY_SELECTED -> return
857
+ enqueueResult == = ENQUEUE_FAILED -> {} // retry
858
+ else -> error(" performAtomicIfNotSelected(TryEnqueueReceiveDesc) returned $enqueueResult " )
859
+ }
860
+ } else {
861
+ val pollResult = pollSelectInternal(select)
862
+ when {
863
+ pollResult == = ALREADY_SELECTED -> return
864
+ pollResult == = POLL_FAILED -> {} // retry
865
+ pollResult is Closed <* > -> {
866
+ block.startCoroutineUnintercepted(ReceiveResult .closed(pollResult.receiveException), select.completion)
867
+ }
868
+ else -> {
869
+ // selected successfully
870
+ block.startCoroutineUnintercepted(ReceiveResult .value(pollResult as E ), select.completion)
871
+ return
872
+ }
873
+ }
874
+ }
875
+ }
876
+ }
877
+
813
878
// ------ protected ------
814
879
815
880
override fun takeFirstReceiveOrPeekClosed (): ReceiveOrClosed <E >? =
@@ -902,18 +967,31 @@ internal abstract class AbstractChannel<E> : AbstractSendChannel<E>(), Channel<E
902
967
}
903
968
904
969
private class ReceiveElement <in E >(
905
- @JvmField val cont : CancellableContinuation <E ?>,
906
- @JvmField val nullOnClose : Boolean
970
+ @JvmField val cont : CancellableContinuation <Any ?>,
971
+ @JvmField val resumeMode : ResumeMode
907
972
) : Receive<E>() {
908
- override fun tryResumeReceive (value : E , idempotent : Any? ): Any? = cont.tryResume(value, idempotent)
973
+ @Suppress(" IMPLICIT_CAST_TO_ANY" )
974
+ override fun tryResumeReceive (value : E , idempotent : Any? ): Any? {
975
+ val resumeValue = when (resumeMode) {
976
+ ResumeMode .RECEIVE_RESULT -> ReceiveResult .value(value)
977
+ else -> value
978
+ }
979
+ return cont.tryResume(resumeValue, idempotent)
980
+ }
981
+
909
982
override fun completeResumeReceive (token : Any ) = cont.completeResume(token)
910
983
override fun resumeReceiveClosed (closed : Closed <* >) {
911
- if (closed.closeCause == null && nullOnClose)
912
- cont.resume(null )
913
- else
914
- cont.resumeWithException(closed.receiveException)
984
+ when (resumeMode) {
985
+ ResumeMode .THROW_ON_CLOSE -> cont.resumeWithException(closed.receiveException)
986
+ ResumeMode .RECEIVE_RESULT -> cont.resume(closed.toResult<Any >())
987
+ ResumeMode .NULL_ON_CLOSE -> if (closed.closeCause == null ) {
988
+ cont.resume(null )
989
+ } else {
990
+ cont.resumeWithException(closed.receiveException)
991
+ }
992
+ }
915
993
}
916
- override fun toString (): String = " ReceiveElement[$cont ,nullOnClose= $nullOnClose ]"
994
+ override fun toString (): String = " ReceiveElement[$cont ,mode= $resumeMode ]"
917
995
}
918
996
919
997
private class ReceiveHasNext <E >(
@@ -957,26 +1035,27 @@ internal abstract class AbstractChannel<E> : AbstractSendChannel<E>(), Channel<E
957
1035
958
1036
private inner class ReceiveSelect <R , in E >(
959
1037
@JvmField val select : SelectInstance <R >,
960
- @JvmField val block : suspend (E ? ) -> R ,
961
- @JvmField val nullOnClose : Boolean
1038
+ @JvmField val block : suspend (Any ? ) -> R ,
1039
+ @JvmField val mode : ResumeMode
962
1040
) : Receive<E>(), DisposableHandle {
963
1041
override fun tryResumeReceive (value : E , idempotent : Any? ): Any? =
964
1042
if (select.trySelect(idempotent)) (value ? : NULL_VALUE ) else null
965
1043
966
1044
@Suppress(" UNCHECKED_CAST" )
967
1045
override fun completeResumeReceive (token : Any ) {
968
1046
val value: E = (if (token == = NULL_VALUE ) null else token) as E
969
- block.startCoroutine(value, select.completion)
1047
+ block.startCoroutine(if (mode == ResumeMode . RECEIVE_RESULT ) ReceiveResult .value(value) else value, select.completion)
970
1048
}
971
1049
972
1050
override fun resumeReceiveClosed (closed : Closed <* >) {
973
- if (select.trySelect(null )) {
974
- if (closed.closeCause == null && nullOnClose) {
1051
+ if (! select.trySelect(null )) return
1052
+
1053
+ when (mode) {
1054
+ ResumeMode .THROW_ON_CLOSE -> select.resumeSelectCancellableWithException(closed.receiveException)
1055
+ ResumeMode .RECEIVE_RESULT -> block.startCoroutine(ReceiveResult .closed<R >(closed.receiveException), select.completion)
1056
+ ResumeMode .NULL_ON_CLOSE -> if (closed.closeCause == null ) {
975
1057
block.startCoroutine(null , select.completion)
976
1058
} else {
977
- // even though we are dispatching coroutine to process channel close on receive,
978
- // which is an atomically cancellable suspending function,
979
- // close is a final state, so we can use a cancellable resume mode
980
1059
select.resumeSelectCancellableWithException(closed.receiveException)
981
1060
}
982
1061
}
@@ -991,7 +1070,7 @@ internal abstract class AbstractChannel<E> : AbstractSendChannel<E>(), Channel<E
991
1070
onReceiveDequeued() // notify cancellation of receive
992
1071
}
993
1072
994
- override fun toString (): String = " ReceiveSelect[$select ,nullOnClose= $nullOnClose ]"
1073
+ override fun toString (): String = " ReceiveSelect[$select ,mode= $mode ]"
995
1074
}
996
1075
997
1076
private class IdempotentTokenValue <out E >(
@@ -1083,3 +1162,17 @@ private abstract class Receive<in E> : LockFreeLinkedListNode(), ReceiveOrClosed
1083
1162
override val offerResult get() = OFFER_SUCCESS
1084
1163
abstract fun resumeReceiveClosed (closed : Closed <* >)
1085
1164
}
1165
+
1166
+ @Suppress(" NOTHING_TO_INLINE" , " UNCHECKED_CAST" )
1167
+ private inline fun <E > Any?.toResult (): ReceiveResult <E > =
1168
+ if (this is Closed <* >) ReceiveResult .closed(receiveException) else ReceiveResult .value(this as E )
1169
+
1170
+ @Suppress(" NOTHING_TO_INLINE" )
1171
+ private inline fun <E > Closed <* >.toResult (): ReceiveResult <E > = ReceiveResult .closed(receiveException)
1172
+
1173
+ // Marker for receive, receiveOrNull and receiveOrClosed
1174
+ private enum class ResumeMode {
1175
+ THROW_ON_CLOSE ,
1176
+ NULL_ON_CLOSE ,
1177
+ RECEIVE_RESULT
1178
+ }
0 commit comments