Skip to content

Commit ff435f7

Browse files
Merge pull request #1139 from square/sedwards/session-docs
Undeprecate runningWorker for LifecyleWorker; Docs for SessionWorkflow
2 parents b7e37bc + a5ff325 commit ff435f7

File tree

5 files changed

+41
-9
lines changed

5 files changed

+41
-9
lines changed

workflow-core/src/commonMain/kotlin/com/squareup/workflow1/BaseRenderContext.kt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,28 @@ public fun <PropsT, StateT, OutputT, ChildRenderingT>
308308
key: String = ""
309309
): ChildRenderingT = renderChild(child, Unit, key) { noAction() }
310310

311+
/**
312+
* Ensures a [LifecycleWorker] is running. Since [worker] can't emit anything,
313+
* it can't trigger any [WorkflowAction]s.
314+
*
315+
* You may want to consider using [SessionWorkflow]. See note on [LifecycleWorker] and the docs
316+
* for [SessionWorkflow].
317+
*
318+
* @param key An optional string key that is used to distinguish between identical [Worker]s.
319+
*/
320+
public inline fun <reified W : LifecycleWorker, PropsT, StateT, OutputT>
321+
BaseRenderContext<PropsT, StateT, OutputT>.runningWorker(
322+
worker: W,
323+
key: String = ""
324+
) {
325+
runningWorker(worker, key) {
326+
// The compiler thinks this code is unreachable, and it is correct. But we have to pass a lambda
327+
// here so we might as well check at runtime as well.
328+
@Suppress("UNREACHABLE_CODE")
329+
throw AssertionError("Worker<Nothing> emitted $it")
330+
}
331+
}
332+
311333
/**
312334
* Ensures a [Worker] that never emits anything is running. Since [worker] can't emit anything,
313335
* it can't trigger any [WorkflowAction]s.

workflow-core/src/commonMain/kotlin/com/squareup/workflow1/LifecycleWorker.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ import kotlin.jvm.JvmName
1818
* Note that there is currently an [issue](https://github.com/square/workflow-kotlin/issues/1093)
1919
* which can effect whether a [LifecycleWorker] is ever executed.
2020
* See more details at [BaseRenderContext.runningSideEffect].
21+
*
22+
* Also note that [LifecycleWorker] is inherently racy with other Workers. There is no guarantee
23+
* this will run first or last compared to other workers and side effects. Ideally setup and
24+
* teardown is handled by each Worker or sideEffect itself. Consider using a try { } finally { }
25+
* or [Flow.onCompletion][kotlinx.coroutines.flow.onCompletion] to handle that.
2126
*/
2227
public abstract class LifecycleWorker : Worker<Nothing> {
2328

workflow-core/src/commonMain/kotlin/com/squareup/workflow1/SessionWorkflow.kt

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,6 @@ public abstract class SessionWorkflow<
3535
*
3636
* This [CoroutineScope] can be used to:
3737
*
38-
* - set reliable teardown hooks, e.g. via [Job.invokeOnCompletion][kotlinx.coroutines.Job.invokeOnCompletion].
39-
*
4038
* - own the transforms on a [StateFlow][kotlinx.coroutines.flow.StateFlow],
4139
* linking them to the lifetime of a Workflow session. For example,
4240
* here is how you might safely combine two `StateFlow`s:
@@ -60,6 +58,20 @@ public abstract class SessionWorkflow<
6058
* )
6159
* }
6260
*
61+
* - set reliable teardown hooks, e.g. via [Job.invokeOnCompletion][kotlinx.coroutines.Job.invokeOnCompletion].
62+
* Note however, that while these are reliable in the sense of being guaranteed to be executed
63+
* regardless of the lifetime of this workflow session, they are not reliable in that a
64+
* completion handler on the Job is not thread-safe and will be executed on whatever the last
65+
* dispatcher was used when the Job completes. See more on the [Job.invokeOnCompletion][kotlinx.coroutines.Job.invokeOnCompletion]
66+
* kdoc.
67+
*
68+
* So what do you do? Well, cleanup and lifecycle matters should be handled by each individual
69+
* Worker and sideEffect. Consider using a try { } finally { cleanup() }
70+
* or [Flow.onCompletion][kotlinx.coroutines.flow.onCompletion] to handle that.
71+
*
72+
* If you have a general cleanup operation that is fast and thread-safe then you could use
73+
* [Job.invokeOnCompletion][kotlinx.coroutines.Job.invokeOnCompletion].
74+
*
6375
* **Note Carefully**: Neither [workflowScope] nor any of these transformed/computed dependencies
6476
* should be stored by this Workflow instance. This could be re-created, or re-used unexpectedly
6577
* and should not have its own state. Instead, the transformed/computed dependencies must be

workflow-testing/src/main/java/com/squareup/workflow1/testing/RenderTesterWorkers.kt

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ import kotlin.reflect.typeOf
2727
* @param description Optional string that will be used to describe this expectation in error
2828
* messages.
2929
*/
30-
@OptIn(ExperimentalStdlibApi::class)
3130
public inline fun <PropsT, StateT, OutputT, RenderingT>
3231
RenderTester<PropsT, StateT, OutputT, RenderingT>.expectWorkerOutputting(
3332
outputType: KType,
@@ -60,7 +59,6 @@ public inline fun <PropsT, StateT, OutputT, RenderingT>
6059
* @param description Optional string that will be used to describe this expectation in error
6160
* messages.
6261
*/
63-
@OptIn(ExperimentalStdlibApi::class)
6462
public inline fun <
6563
PropsT,
6664
StateT,
@@ -149,7 +147,6 @@ public inline fun <
149147
* @param description Optional string that will be used to describe this expectation in error
150148
* messages.
151149
*/
152-
@OptIn(ExperimentalStdlibApi::class)
153150
public fun <PropsT, StateT, OutputT, RenderingT>
154151
RenderTester<PropsT, StateT, OutputT, RenderingT>.expectWorker(
155152
workerType: KType,

workflow-testing/src/test/java/com/squareup/workflow1/testing/RealRenderTesterTest.kt

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -729,9 +729,6 @@ internal class RealRenderTesterTest {
729729
val stringWorker: Worker<String> = emptyFlow<String>().asWorker()
730730

731731
val workflow = Workflow.stateless<Unit, Nothing, Unit> {
732-
// Suppress usage as we are testing a comparisons of unique workers
733-
// even though they have the same key.
734-
@Suppress("DEPRECATION")
735732
runningWorker(lifecycleWorker)
736733
runningWorker(stringWorker) { noAction() }
737734
}
@@ -745,7 +742,6 @@ internal class RealRenderTesterTest {
745742

746743
// Suppress runningWorker in this test as we are testing the
747744
// uniqueness of workers using similar objects as keys
748-
@Suppress("DEPRECATION")
749745
@Test
750746
fun `runningWorker distinguishes between specific Nothing workers`() {
751747
val workerA = object : LifecycleWorker() {}

0 commit comments

Comments
 (0)