@@ -26,7 +26,7 @@ internal class RealWorkflowHost<InputT : Any, StateT : Any, OutputT : Any, Rende
26
26
workflow : StatefulWorkflow <InputT , StateT , OutputT , RenderingT >,
27
27
context : CoroutineContext ,
28
28
initialInput : InputT ,
29
- private val initialSnapshot : Snapshot ? ,
29
+ initialSnapshot : Snapshot ? ,
30
30
private val initialState : StateT ? = null
31
31
) : WorkflowHost<InputT, OutputT, RenderingT> {
32
32
@@ -38,74 +38,80 @@ internal class RealWorkflowHost<InputT : Any, StateT : Any, OutputT : Any, Rende
38
38
scope.produce(capacity = 0 ) {
39
39
runWorkflowTree(
40
40
workflow = workflow,
41
+ inputs = inputs,
42
+ initialSnapshot = initialSnapshot,
43
+ initialState = initialState,
41
44
onUpdate = ::send
42
45
)
43
46
}
44
47
45
48
override fun setInput (input : InputT ) {
46
49
inputs.offer(input)
47
50
}
51
+ }
48
52
49
- /* *
50
- * Loops forever, or until the coroutine is cancelled, processing the workflow tree and emitting
51
- * updates by calling [onUpdate].
52
- */
53
- private suspend fun runWorkflowTree (
54
- workflow : StatefulWorkflow <InputT , StateT , OutputT , RenderingT >,
55
- onUpdate : suspend (Update <OutputT , RenderingT >) -> Unit
56
- ): Nothing {
57
- var output: OutputT ? = null
58
- var input: InputT = inputs.receive()
59
- var inputsClosed = false
60
- val workflowNode = WorkflowNode (
61
- id = workflow.id(),
62
- workflow = workflow,
63
- initialInput = input,
64
- snapshot = initialSnapshot,
65
- baseContext = coroutineContext,
66
- initialState = initialState
67
- )
53
+ /* *
54
+ * Loops forever, or until the coroutine is cancelled, processing the workflow tree and emitting
55
+ * updates by calling [onUpdate].
56
+ */
57
+ internal suspend fun <I : Any , S : Any , O : Any , R : Any > runWorkflowTree (
58
+ workflow : StatefulWorkflow <I , S , O , R >,
59
+ inputs : ReceiveChannel <I >,
60
+ initialSnapshot : Snapshot ? ,
61
+ initialState : S ? ,
62
+ onUpdate : suspend (Update <O , R >) -> Unit
63
+ ): Nothing {
64
+ var output: O ? = null
65
+ var input: I = inputs.receive()
66
+ var inputsClosed = false
67
+ val workflowNode = WorkflowNode (
68
+ id = workflow.id(),
69
+ workflow = workflow,
70
+ initialInput = input,
71
+ snapshot = initialSnapshot,
72
+ baseContext = coroutineContext,
73
+ initialState = initialState
74
+ )
68
75
69
- try {
70
- while (true ) {
71
- coroutineContext.ensureActive()
76
+ try {
77
+ while (true ) {
78
+ coroutineContext.ensureActive()
72
79
73
- val rendering = workflowNode.compose(workflow, input)
74
- val snapshot = workflowNode.snapshot(workflow)
80
+ val rendering = workflowNode.compose(workflow, input)
81
+ val snapshot = workflowNode.snapshot(workflow)
75
82
76
- onUpdate(Update (rendering, snapshot, output))
83
+ onUpdate(Update (rendering, snapshot, output))
77
84
78
- // Tick _might_ return an output, but if it returns null, it means the state or a child
79
- // probably changed, so we should re-compose/snapshot and emit again.
80
- output = select {
81
- // While the inputs channel is still open, select on it so we can detect new inputs.
82
- if (! inputsClosed) {
83
- @Suppress(" EXPERIMENTAL_API_USAGE" )
84
- inputs.onReceiveOrNull { newInput ->
85
- if (newInput == null ) {
86
- inputsClosed = true
87
- } else {
88
- input = newInput
89
- }
90
- // No output.
91
- return @onReceiveOrNull null
85
+ // Tick _might_ return an output, but if it returns null, it means the state or a child
86
+ // probably changed, so we should re-compose/snapshot and emit again.
87
+ output = select {
88
+ // While the inputs channel is still open, select on it so we can detect new inputs.
89
+ if (! inputsClosed) {
90
+ @Suppress(" EXPERIMENTAL_API_USAGE" )
91
+ inputs.onReceiveOrNull { newInput ->
92
+ if (newInput == null ) {
93
+ inputsClosed = true
94
+ } else {
95
+ input = newInput
92
96
}
97
+ // No output.
98
+ return @onReceiveOrNull null
93
99
}
94
-
95
- // Tick the workflow tree.
96
- workflowNode.tick(this ) { it }
97
100
}
101
+
102
+ // Tick the workflow tree.
103
+ workflowNode.tick(this ) { it }
98
104
}
99
- } catch (e: Throwable ) {
100
- // For some reason the exception gets masked if we don't explicitly pass it to cancel the
101
- // producer coroutine ourselves here.
102
- coroutineContext.cancel(if (e is CancellationException ) e else CancellationException (null , e))
103
- throw e
104
- } finally {
105
- // There's a potential race condition if the producer coroutine is cancelled before it has a
106
- // chance to enter the try block, since we can't use CoroutineStart.ATOMIC. However, until we
107
- // actually see this cause problems, I'm not too worried about it.
108
- workflowNode.cancel()
109
105
}
106
+ } catch (e: Throwable ) {
107
+ // For some reason the exception gets masked if we don't explicitly pass it to cancel the
108
+ // producer coroutine ourselves here.
109
+ coroutineContext.cancel(if (e is CancellationException ) e else CancellationException (null , e))
110
+ throw e
111
+ } finally {
112
+ // There's a potential race condition if the producer coroutine is cancelled before it has a
113
+ // chance to enter the try block, since we can't use CoroutineStart.ATOMIC. However, until we
114
+ // actually see this cause problems, I'm not too worried about it.
115
+ workflowNode.cancel()
110
116
}
111
117
}
0 commit comments