@@ -382,7 +382,10 @@ internal abstract class AbstractSendChannel<E> : SendChannel<E> {
382
382
select.disposeOnSelect(node)
383
383
return
384
384
}
385
- enqueueResult is Closed <* > -> throw recoverStackTrace(helpCloseAndGetSendException(enqueueResult))
385
+ enqueueResult is Closed <* > -> {
386
+ node.dispose()
387
+ throw recoverStackTrace(helpCloseAndGetSendException(enqueueResult))
388
+ }
386
389
enqueueResult == = ENQUEUE_FAILED -> {} // try to offer
387
390
enqueueResult is Receive <* > -> {} // try to offer
388
391
else -> error(" enqueueSend returned $enqueueResult " )
@@ -448,16 +451,18 @@ internal abstract class AbstractSendChannel<E> : SendChannel<E> {
448
451
override val pollResult : Any? ,
449
452
@JvmField val channel : AbstractSendChannel <E >,
450
453
@JvmField val select : SelectInstance <R >,
451
- @JvmField val block : suspend (SendChannel <E >) -> R
454
+ block : suspend (SendChannel <E >) -> R
452
455
) : Send(), DisposableHandle {
456
+ @JvmField val block: suspend (SendChannel <E >) -> R = block.asShareable()
457
+
453
458
override fun tryResumeSend (otherOp : PrepareOp ? ): Symbol ? =
454
- select.trySelectOther(otherOp) as Symbol ? // must return symbol
459
+ select.trySelectOther(otherOp, onSelect = block::shareableWillBeUsed ) as Symbol ? // must return symbol
455
460
456
- override fun completeResumeSend () {
457
- block.startCoroutine(receiver = channel, completion = select.completion)
458
- }
461
+ override fun completeResumeSend () =
462
+ startCoroutine(CoroutineStart .ATOMIC , channel, select.completion, block)
459
463
460
464
override fun dispose () { // invoked on select completion
465
+ block.shareableDispose(useIt = false )
461
466
remove()
462
467
}
463
468
@@ -773,7 +778,11 @@ internal abstract class AbstractChannel<E> : AbstractSendChannel<E>(), Channel<E
773
778
): Boolean {
774
779
val node = ReceiveSelect (this , select, block, receiveMode)
775
780
val result = enqueueReceive(node)
776
- if (result) select.disposeOnSelect(node)
781
+ if (result) {
782
+ select.disposeOnSelect(node)
783
+ } else {
784
+ node.dispose()
785
+ }
777
786
return result
778
787
}
779
788
@@ -871,41 +880,53 @@ internal abstract class AbstractChannel<E> : AbstractSendChannel<E>(), Channel<E
871
880
}
872
881
873
882
private class ReceiveElement <in E >(
874
- @JvmField val cont : CancellableContinuation <Any ?>,
883
+ cont : CancellableContinuation <Any ?>,
875
884
@JvmField val receiveMode : Int
876
885
) : Receive<E>() {
886
+ private val _cont = atomic<CancellableContinuation <Any ?>? > (cont)
887
+ private fun useCont () = _cont .getAndSet(null )
888
+
877
889
fun resumeValue (value : E ): Any? = when (receiveMode) {
878
890
RECEIVE_RESULT -> ValueOrClosed .value(value)
879
891
else -> value
880
892
}
881
893
882
894
@Suppress(" IMPLICIT_CAST_TO_ANY" )
883
895
override fun tryResumeReceive (value : E , otherOp : PrepareOp ? ): Symbol ? {
884
- val token = cont.tryResume(resumeValue(value), otherOp?.desc) ? : return null
896
+ val token = _cont .value?.tryResume(resumeValue(value), otherOp?.desc) ? : run {
897
+ _cont .value = null
898
+ return null
899
+ }
885
900
assert { token == = RESUME_TOKEN } // the only other possible result
886
901
// We can call finishPrepare only after successful tryResume, so that only good affected node is saved
887
902
otherOp?.finishPrepare()
888
903
return RESUME_TOKEN
889
904
}
890
905
891
- override fun completeResumeReceive (value : E ) = cont .completeResume(RESUME_TOKEN )
906
+ override fun completeResumeReceive (value : E ) { useCont()? .completeResume(RESUME_TOKEN ) }
892
907
893
908
override fun resumeReceiveClosed (closed : Closed <* >) {
894
909
when {
895
- receiveMode == RECEIVE_NULL_ON_CLOSE && closed.closeCause == null -> cont .resume(null )
896
- receiveMode == RECEIVE_RESULT -> cont .resume(closed.toResult<Any >())
897
- else -> cont .resumeWithException(closed.receiveException)
910
+ receiveMode == RECEIVE_NULL_ON_CLOSE && closed.closeCause == null -> useCont()? .resume(null )
911
+ receiveMode == RECEIVE_RESULT -> useCont()? .resume(closed.toResult<Any >())
912
+ else -> useCont()? .resumeWithException(closed.receiveException)
898
913
}
899
914
}
900
915
override fun toString (): String = " ReceiveElement@$hexAddress [receiveMode=$receiveMode ]"
901
916
}
902
917
903
918
private class ReceiveHasNext <E >(
904
919
@JvmField val iterator : Itr <E >,
905
- @JvmField val cont : CancellableContinuation <Boolean >
920
+ cont : CancellableContinuation <Boolean >
906
921
) : Receive<E>() {
922
+ private val _cont = atomic<CancellableContinuation <Boolean >? > (cont)
923
+ private fun useCont () = _cont .getAndSet(null )
924
+
907
925
override fun tryResumeReceive (value : E , otherOp : PrepareOp ? ): Symbol ? {
908
- val token = cont.tryResume(true , otherOp?.desc) ? : return null
926
+ val token = _cont .value?.tryResume(true , otherOp?.desc) ? : run {
927
+ _cont .value = null
928
+ return null
929
+ }
909
930
assert { token == = RESUME_TOKEN } // the only other possible result
910
931
// We can call finishPrepare only after successful tryResume, so that only good affected node is saved
911
932
otherOp?.finishPrepare()
@@ -918,51 +939,61 @@ internal abstract class AbstractChannel<E> : AbstractSendChannel<E>(), Channel<E
918
939
but completeResumeReceive is called once so we set iterator result here.
919
940
*/
920
941
iterator.result = value
921
- cont .completeResume(RESUME_TOKEN )
942
+ useCont()? .completeResume(RESUME_TOKEN )
922
943
}
923
944
924
945
override fun resumeReceiveClosed (closed : Closed <* >) {
925
946
val token = if (closed.closeCause == null ) {
926
- cont .tryResume(false )
947
+ _cont .value? .tryResume(false )
927
948
} else {
928
- cont.tryResumeWithException(recoverStackTrace(closed.receiveException, cont))
949
+ _cont .value?.let { cont ->
950
+ cont.tryResumeWithException(recoverStackTrace(closed.receiveException, cont))
951
+ }
929
952
}
930
953
if (token != null ) {
931
954
iterator.result = closed
932
- cont .completeResume(token)
955
+ _cont .value? .completeResume(token)
933
956
}
957
+ _cont .value = null
934
958
}
935
959
override fun toString (): String = " ReceiveHasNext@$hexAddress "
936
960
}
937
961
938
962
private class ReceiveSelect <R , E >(
939
963
@JvmField val channel : AbstractChannel <E >,
940
964
@JvmField val select : SelectInstance <R >,
941
- @JvmField val block : suspend (Any? ) -> R ,
965
+ block : suspend (Any? ) -> R ,
942
966
@JvmField val receiveMode : Int
943
967
) : Receive<E>(), DisposableHandle {
968
+ @JvmField val block: suspend (Any? ) -> R = block.asShareable() // captured variables in this block need screening
969
+
944
970
override fun tryResumeReceive (value : E , otherOp : PrepareOp ? ): Symbol ? =
945
- select.trySelectOther(otherOp) as Symbol ?
971
+ select.trySelectOther(otherOp, onSelect = block::shareableWillBeUsed ) as Symbol ?
946
972
947
973
@Suppress(" UNCHECKED_CAST" )
948
974
override fun completeResumeReceive (value : E ) {
949
- block. startCoroutine(if (receiveMode == RECEIVE_RESULT ) ValueOrClosed .value(value) else value, select.completion)
975
+ startCoroutine(CoroutineStart . ATOMIC , if (receiveMode == RECEIVE_RESULT ) ValueOrClosed .value(value) else value, select.completion, block )
950
976
}
951
977
952
978
override fun resumeReceiveClosed (closed : Closed <* >) {
953
- if (! select.trySelect()) return
979
+ if (! select.trySelect(onSelect = block::shareableWillBeUsed )) return
954
980
when (receiveMode) {
955
- RECEIVE_THROWS_ON_CLOSE -> select.resumeSelectWithException(closed.receiveException)
956
- RECEIVE_RESULT -> block.startCoroutine(ValueOrClosed .closed<R >(closed.closeCause), select.completion)
981
+ RECEIVE_THROWS_ON_CLOSE -> {
982
+ block.shareableDispose(useIt = true )
983
+ select.resumeSelectWithException(closed.receiveException)
984
+ }
985
+ RECEIVE_RESULT -> startCoroutine(CoroutineStart .ATOMIC , ValueOrClosed .closed<R >(closed.closeCause), select.completion, block)
957
986
RECEIVE_NULL_ON_CLOSE -> if (closed.closeCause == null ) {
958
- block. startCoroutine(null , select.completion)
987
+ startCoroutine(CoroutineStart . ATOMIC , null , select.completion, block )
959
988
} else {
989
+ block.shareableDispose(useIt = true )
960
990
select.resumeSelectWithException(closed.receiveException)
961
991
}
962
992
}
963
993
}
964
994
965
995
override fun dispose () { // invoked on select completion
996
+ block.shareableDispose(useIt = false )
966
997
if (remove())
967
998
channel.onReceiveDequeued() // notify cancellation of receive
968
999
}
@@ -1031,17 +1062,23 @@ internal interface ReceiveOrClosed<in E> {
1031
1062
@Suppress(" UNCHECKED_CAST" )
1032
1063
internal class SendElement (
1033
1064
override val pollResult : Any? ,
1034
- @JvmField val cont : CancellableContinuation <Unit >
1065
+ cont : CancellableContinuation <Unit >
1035
1066
) : Send() {
1067
+ private val _cont = atomic<CancellableContinuation <Unit >? > (cont)
1068
+ private fun useCont () = _cont .getAndSet(null )
1069
+
1036
1070
override fun tryResumeSend (otherOp : PrepareOp ? ): Symbol ? {
1037
- val token = cont.tryResume(Unit , otherOp?.desc) ? : return null
1071
+ val token = _cont .value?.tryResume(Unit , otherOp?.desc) ? : run {
1072
+ _cont .value = null
1073
+ return null
1074
+ }
1038
1075
assert { token == = RESUME_TOKEN } // the only other possible result
1039
1076
// We can call finishPrepare only after successful tryResume, so that only good affected node is saved
1040
1077
otherOp?.finishPrepare() // finish preparations
1041
1078
return RESUME_TOKEN
1042
1079
}
1043
- override fun completeResumeSend () = cont .completeResume(RESUME_TOKEN )
1044
- override fun resumeSendClosed (closed : Closed <* >) = cont .resumeWithException(closed.sendException)
1080
+ override fun completeResumeSend () { useCont()? .completeResume(RESUME_TOKEN ) }
1081
+ override fun resumeSendClosed (closed : Closed <* >) { useCont()? .resumeWithException(closed.sendException) }
1045
1082
override fun toString (): String = " SendElement@$hexAddress ($pollResult )"
1046
1083
}
1047
1084
0 commit comments