commonMain.dev.inmo.micro_utils.fsm.common.UpdatableStatesMachine.kt Maven / Gradle / Ivy
package dev.inmo.micro_utils.fsm.common
import dev.inmo.micro_utils.common.*
import dev.inmo.micro_utils.fsm.common.utils.StateHandlingErrorHandler
import dev.inmo.micro_utils.fsm.common.utils.defaultStateHandlingErrorHandler
import kotlinx.coroutines.*
import kotlinx.coroutines.sync.withLock
/**
* This extender of [StatesMachine] interface declare one new function [updateChain]. Realizations of this interface
* must be able to perform update of chain in internal [StatesManager]
*/
interface UpdatableStatesMachine : StatesMachine {
/**
* Update chain with current state equal to [currentState] with [newState]. Behaviour of this update preforming
* in cases when [currentState] does not exist in [StatesManager] must be declared inside of realization of
* [StatesManager.update] function
*/
suspend fun updateChain(currentState: T, newState: T)
}
open class DefaultUpdatableStatesMachine(
statesManager: StatesManager,
handlers: List>,
onStateHandlingErrorHandler: StateHandlingErrorHandler = defaultStateHandlingErrorHandler()
) : DefaultStatesMachine(
statesManager,
handlers,
onStateHandlingErrorHandler
), UpdatableStatesMachine {
protected val jobsStates = mutableMapOf()
/**
* Realization of this update will use the [Job] of [previousState] in [statesJobs] and [jobsStates] if
* [previousState] is [Optional.presented] and [shouldReplaceJob] has returned true for [previousState] and [actualState]. In
* other words, [Job] of [previousState] WILL NOT be replaced with the new one if they are "equal". Equality of
* states is solved in [shouldReplaceJob] and can be rewritten in subclasses
*/
override suspend fun performStateUpdate(previousState: Optional, actualState: T, scope: CoroutineScope) {
statesJobsMutex.withLock {
if (shouldReplaceJob(previousState, actualState)) {
statesJobs[actualState] ?.cancel()
}
val job = previousState.mapOnPresented {
statesJobs.remove(it)
} ?.takeIf { it.isActive } ?: scope.launch {
performUpdate(actualState)
}.also { job ->
job.invokeOnCompletion { _ ->
scope.launch {
statesJobsMutex.withLock {
statesJobs.remove(
jobsStates[job] ?: return@withLock
)
jobsStates.remove(job)
}
}
}
}
jobsStates.remove(job)
statesJobs[actualState] = job
jobsStates[job] = actualState
}
}
/**
* Compare if [previous] potentially lead to the same behaviour with [new]
*/
protected open suspend fun shouldReplaceJob(previous: Optional, new: T): Boolean = previous.dataOrNull() != new
override suspend fun updateChain(currentState: T, newState: T) {
statesManager.update(currentState, newState)
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy