@@ -27,7 +27,7 @@ import kotlin.coroutines.*
27
27
* see its documentation for additional details.
28
28
*/
29
29
public fun <T : Any > Publisher<T>.asFlow (): Flow <T > =
30
- PublisherAsFlow (this , 1 )
30
+ PublisherAsFlow (this )
31
31
32
32
/* *
33
33
* Transforms the given flow to a reactive specification compliant [Publisher].
@@ -39,30 +39,11 @@ public fun <T : Any> Flow<T>.asPublisher(): Publisher<T> = FlowAsPublisher(this)
39
39
40
40
private class PublisherAsFlow <T : Any >(
41
41
private val publisher : Publisher <T >,
42
- capacity : Int
43
- ) : ChannelFlow<T>(EmptyCoroutineContext , capacity) {
42
+ context : CoroutineContext = EmptyCoroutineContext ,
43
+ capacity : Int = 1
44
+ ) : ChannelFlow<T>(context, capacity) {
44
45
override fun create (context : CoroutineContext , capacity : Int ): ChannelFlow <T > =
45
- PublisherAsFlow (publisher, capacity)
46
-
47
- override fun produceImpl (scope : CoroutineScope ): ReceiveChannel <T > {
48
- // use another channel for conflation (cannot do openSubscription)
49
- if (capacity < 0 ) return super .produceImpl(scope)
50
- // Open subscription channel directly
51
- val channel = publisher
52
- .injectCoroutineContext(scope.coroutineContext)
53
- .openSubscription(capacity)
54
- val handle = scope.coroutineContext[Job ]?.invokeOnCompletion(onCancelling = true ) { cause ->
55
- channel.cancel(cause?.let {
56
- it as ? CancellationException ? : CancellationException (" Job was cancelled" , it)
57
- })
58
- }
59
- if (handle != null && handle != = NonDisposableHandle ) {
60
- (channel as SendChannel <* >).invokeOnClose {
61
- handle.dispose()
62
- }
63
- }
64
- return channel
65
- }
46
+ PublisherAsFlow (publisher, context, capacity)
66
47
67
48
private val requestSize: Long
68
49
get() = when (capacity) {
@@ -73,8 +54,26 @@ private class PublisherAsFlow<T : Any>(
73
54
}
74
55
75
56
override suspend fun collect (collector : FlowCollector <T >) {
57
+ val collectContext = coroutineContext
58
+ val newDispatcher = context[ContinuationInterceptor ]
59
+ if (newDispatcher == null || newDispatcher == collectContext[ContinuationInterceptor ]) {
60
+ // fast path -- subscribe directly in this dispatcher
61
+ return collectImpl(collectContext + context, collector)
62
+ }
63
+ // slow path -- produce in a separate dispatcher
64
+ collectSlowPath(collector)
65
+ }
66
+
67
+ private suspend fun collectSlowPath (collector : FlowCollector <T >) {
68
+ coroutineScope {
69
+ collector.emitAll(produceImpl(this + context))
70
+ }
71
+ }
72
+
73
+ private suspend fun collectImpl (injectContext : CoroutineContext , collector : FlowCollector <T >) {
76
74
val subscriber = ReactiveSubscriber <T >(capacity, requestSize)
77
- publisher.injectCoroutineContext(coroutineContext).subscribe(subscriber)
75
+ // inject subscribe context into publisher
76
+ publisher.injectCoroutineContext(injectContext).subscribe(subscriber)
78
77
try {
79
78
var consumed = 0L
80
79
while (true ) {
@@ -90,9 +89,9 @@ private class PublisherAsFlow<T : Any>(
90
89
}
91
90
}
92
91
93
- // The second channel here is used only for broadcast
92
+ // The second channel here is used for produceIn/broadcastIn and slow-path (dispatcher change)
94
93
override suspend fun collectTo (scope : ProducerScope <T >) =
95
- collect( SendingCollector (scope.channel))
94
+ collectImpl(scope.coroutineContext, SendingCollector (scope.channel))
96
95
}
97
96
98
97
@Suppress(" SubscriberImplementation" )
0 commit comments