0

I saw that changes were tracked and requested invalidation through Recompoer#runRecomposeAndApplyChanges. This function tracks changes and requests invalidation in the while statement and remains suspended until new changes are made through waitWorkAvailable() in the while statement.

awaitWorkAvailable() works with suspendCancellableCoroutine and assigns Continuation to workContinuation if there is no scheduled task. The workContinuation is resumed in the invokeOnCompletion of a Job called effectJob. Therefore, in order for workContinuation to resume, effectJob must be completed. Recomposer#close, which completes it, is used only in the withRunningRecomposer function, but withRunningRecomposer is not used.

So where does workContinuation resume? I had the same concern a month ago, but I still haven’t solved it.

(Job.cancel is also possible to call invokeOnComplete, but cancel is used only for lifecycle handling through Owner)

full-code for

Ji Sungbin
  • 891
  • 1
  • 10
  • 19

1 Answers1

0

I finally solved it. A workContinuation instance was being returned from deriveStateLocked(), and workContinuation is resumed whenever invalidation is required thereafter.

private fun deriveStateLocked(): CancellableContinuation<Unit>? {
    // ...
    return if (newState == State.PendingWork) {
        workContinuation.also {
            workContinuation = null
        }
    } else null
}
@OptIn(ExperimentalComposeApi::class)
private suspend fun recompositionRunner(
    block: suspend CoroutineScope.(parentFrameClock: MonotonicFrameClock) -> Unit
) {
    val parentFrameClock = coroutineContext.monotonicFrameClock
    withContext(broadcastFrameClock) {
        val callingJob = coroutineContext.job
        registerRunnerJob(callingJob)

        val unregisterApplyObserver = Snapshot.registerApplyObserver { changed, _ ->
            synchronized(stateLock) {
                if (_state.value >= State.Idle) {
                    snapshotInvalidations += changed
                    deriveStateLocked()
                } else null
            }?.resume(Unit)
        }
    // ...
}

internal override fun invalidate(composition: ControlledComposition) {
    synchronized(stateLock) {
        if (composition !in compositionInvalidations) {
            compositionInvalidations += composition
            deriveStateLocked()
        } else null
    }?.resume(Unit)
}

internal override fun invalidateScope(scope: RecomposeScopeImpl) {
    synchronized(stateLock) {
        snapshotInvalidations += setOf(scope)
        deriveStateLocked()
    }?.resume(Unit)
}

// ... more invalidation methods are using deriveStateLocked().
Ji Sungbin
  • 891
  • 1
  • 10
  • 19