@@ -26,7 +26,7 @@ import com.squareup.workflow1.trace
2626import kotlinx.coroutines.CancellationException
2727import kotlinx.coroutines.CoroutineName
2828import kotlinx.coroutines.CoroutineScope
29- import kotlinx.coroutines.CoroutineStart.LAZY
29+ import kotlinx.coroutines.CoroutineStart.DEFAULT
3030import kotlinx.coroutines.DelicateCoroutinesApi
3131import kotlinx.coroutines.ExperimentalCoroutinesApi
3232import kotlinx.coroutines.Job
@@ -36,7 +36,10 @@ import kotlinx.coroutines.channels.Channel.Factory.UNLIMITED
3636import kotlinx.coroutines.launch
3737import kotlinx.coroutines.plus
3838import kotlinx.coroutines.selects.SelectBuilder
39+ import kotlin.coroutines.Continuation
40+ import kotlin.coroutines.ContinuationInterceptor
3941import kotlin.coroutines.CoroutineContext
42+ import kotlin.coroutines.CoroutineContext.Key
4043import kotlin.reflect.KType
4144
4245/* *
@@ -279,9 +282,6 @@ internal class WorkflowNode<PropsT, StateT, OutputT, RenderingT>(
279282 workflowTracer.trace(" UpdateRuntimeTree" ) {
280283 // Tear down workflows and workers that are obsolete.
281284 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() }
285285 sideEffects.commitStaging { it.job.cancel() }
286286 remembered.commitStaging { /* Nothing to clean up. */ }
287287 }
@@ -351,13 +351,36 @@ internal class WorkflowNode<PropsT, StateT, OutputT, RenderingT>(
351351 }
352352 }
353353
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+
354375 private fun createSideEffectNode (
355376 key : String ,
356377 sideEffect : suspend CoroutineScope .() -> Unit
357378 ): SideEffectNode {
358379 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)
361384 SideEffectNode (key, job)
362385 }
363386 }
0 commit comments