@@ -26,7 +26,7 @@ import com.squareup.workflow1.trace
26
26
import kotlinx.coroutines.CancellationException
27
27
import kotlinx.coroutines.CoroutineName
28
28
import kotlinx.coroutines.CoroutineScope
29
- import kotlinx.coroutines.CoroutineStart.LAZY
29
+ import kotlinx.coroutines.CoroutineStart.DEFAULT
30
30
import kotlinx.coroutines.DelicateCoroutinesApi
31
31
import kotlinx.coroutines.ExperimentalCoroutinesApi
32
32
import kotlinx.coroutines.Job
@@ -36,7 +36,10 @@ import kotlinx.coroutines.channels.Channel.Factory.UNLIMITED
36
36
import kotlinx.coroutines.launch
37
37
import kotlinx.coroutines.plus
38
38
import kotlinx.coroutines.selects.SelectBuilder
39
+ import kotlin.coroutines.Continuation
40
+ import kotlin.coroutines.ContinuationInterceptor
39
41
import kotlin.coroutines.CoroutineContext
42
+ import kotlin.coroutines.CoroutineContext.Key
40
43
import kotlin.reflect.KType
41
44
42
45
/* *
@@ -279,9 +282,6 @@ internal class WorkflowNode<PropsT, StateT, OutputT, RenderingT>(
279
282
workflowTracer.trace(" UpdateRuntimeTree" ) {
280
283
// Tear down workflows and workers that are obsolete.
281
284
subtreeManager.commitRenderedChildren()
282
- // Side effect jobs are launched lazily, since they can send actions to the sink, and can only
283
- // be started after context is frozen.
284
- sideEffects.forEachStaging { it.job.start() }
285
285
sideEffects.commitStaging { it.job.cancel() }
286
286
remembered.commitStaging { /* Nothing to clean up. */ }
287
287
}
@@ -351,13 +351,36 @@ internal class WorkflowNode<PropsT, StateT, OutputT, RenderingT>(
351
351
}
352
352
}
353
353
354
+ inner class FrozenContextContinuationInterceptor : ContinuationInterceptor {
355
+ override val key: Key <* >
356
+ get() = ContinuationInterceptor .Key
357
+
358
+ override fun <T > interceptContinuation (continuation : Continuation <T >): Continuation <T > =
359
+ object : Continuation <T > {
360
+ override val context: CoroutineContext
361
+ get() = continuation.context
362
+
363
+ override fun resumeWith (result : Result <T >) {
364
+ // Freeze the render context each time we are resuming the side effect continuation.
365
+ baseRenderContext.freeze()
366
+ continuation.resumeWith(result)
367
+ }
368
+ }
369
+
370
+ override fun releaseInterceptedContinuation (continuation : Continuation <* >) {
371
+ baseRenderContext.unfreeze()
372
+ }
373
+ }
374
+
354
375
private fun createSideEffectNode (
355
376
key : String ,
356
377
sideEffect : suspend CoroutineScope .() -> Unit
357
378
): SideEffectNode {
358
379
return workflowTracer.trace(" CreateSideEffectNode" ) {
359
- val scope = this + CoroutineName (" sideEffect[$key ] for $id " )
360
- val job = scope.launch(start = LAZY , block = sideEffect)
380
+ val scope = this +
381
+ CoroutineName (" sideEffect[$key ] for $id " ) +
382
+ FrozenContextContinuationInterceptor ()
383
+ val job = scope.launch(start = DEFAULT , block = sideEffect)
361
384
SideEffectNode (key, job)
362
385
}
363
386
}
0 commit comments