DefaultStatesManager

This commit is contained in:
InsanusMokrassar 2021-10-06 13:30:25 +06:00
parent fab789d9c0
commit 119a0588cc
4 changed files with 43 additions and 35 deletions

View File

@ -2,6 +2,11 @@
## 0.7.1 ## 0.7.1
* `FSM`:
* `Common`
* New manager `DefaultStatesManager` with `DefaultStatesManagerRepo` for abstraction of manager and storing of
data info
## 0.7.0 ## 0.7.0
**THIS VERSION HAS MIGRATED FROM KOTLINX DATETIME TO KORLIBS KLOCK. CAREFUL** **THIS VERSION HAS MIGRATED FROM KOTLINX DATETIME TO KORLIBS KLOCK. CAREFUL**

View File

@ -6,35 +6,42 @@ import kotlinx.coroutines.flow.*
import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.sync.withLock
interface DefaultStatesManagerStatesRepo { interface DefaultStatesManagerRepo {
/** /**
* Must save [state] as current state of chain with [State.context] of [state] * Must save [state] as current state of chain with [State.context] of [state]
*/ */
suspend fun newState(state: State) suspend fun set(state: State)
/**
* Must save [to] as instead of [from]. In case when they have different [State.context] - [from] must be just
* removed like with [removeState]
*/
suspend fun updateState(from: State, to: State)
/** /**
* Remove exactly [state]. In case if internally [State.context] is busy with different [State], that [State] should * Remove exactly [state]. In case if internally [State.context] is busy with different [State], that [State] should
* NOT be removed * NOT be removed
*/ */
suspend fun removeState(state: State) suspend fun removeState(state: State)
/** /**
* @return current list of available and saved states * @return Current list of available and saved states
*/ */
suspend fun getStates(): List<State> suspend fun getStates(): List<State>
/**
* @return Current state by [context]
*/
suspend fun getContextState(context: Any): State?
/**
* @return Current state by [context]
*/
suspend fun contains(context: Any): Boolean
} }
/** /**
* @param onContextsConflictResolver Receive old [State], new one and the state currently placed on new [State.context] * @param onContextsConflictResolver Receive old [State], new one and the state currently placed on new [State.context]
* key. In case when this callback will returns true, the state placed on [State.context] of new will be replaced by * key. In case when this callback will returns true, the state placed on [State.context] of new will be replaced by
* new state by using [endChain] with that state * new state by using [endChain] with that state
* @param repo This repo will be used as repository for storing states. All operations with this repo will happen BEFORE
* any event will be sent to [onChainStateUpdated], [onStartChain] or [onEndChain].
*/ */
class DefaultStatesManager( class DefaultStatesManager(
private val onContextsConflictResolver: suspend (old: State, new: State, currentNew: State) -> Boolean = { _, _, _ -> true }, private val onContextsConflictResolver: suspend (old: State, new: State, currentNew: State) -> Boolean = { _, _, _ -> true },
private val repo: DefaultStatesManagerStatesRepo private val repo: DefaultStatesManagerRepo
) : StatesManager { ) : StatesManager {
private val _onChainStateUpdated = MutableSharedFlow<Pair<State, State>>(0) private val _onChainStateUpdated = MutableSharedFlow<Pair<State, State>>(0)
override val onChainStateUpdated: Flow<Pair<State, State>> = _onChainStateUpdated.asSharedFlow() override val onChainStateUpdated: Flow<Pair<State, State>> = _onChainStateUpdated.asSharedFlow()
@ -43,22 +50,22 @@ class DefaultStatesManager(
private val _onEndChain = MutableSharedFlow<State>(0) private val _onEndChain = MutableSharedFlow<State>(0)
override val onEndChain: Flow<State> = _onEndChain.asSharedFlow() override val onEndChain: Flow<State> = _onEndChain.asSharedFlow()
private val contextsToStates = mutableMapOf<Any, State>()
private val mapMutex = Mutex() private val mapMutex = Mutex()
override suspend fun update(old: State, new: State) = mapMutex.withLock { override suspend fun update(old: State, new: State) = mapMutex.withLock {
val stateByOldContext: State? = repo.getContextState(old.context)
when { when {
contextsToStates[old.context] != old -> return@withLock stateByOldContext != old -> return@withLock
old.context == new.context || !contextsToStates.containsKey(new.context) -> { stateByOldContext == null || old.context == new.context -> {
contextsToStates[old.context] = new repo.set(new)
_onChainStateUpdated.emit(old to new) _onChainStateUpdated.emit(old to new)
} }
else -> { else -> {
val stateOnNewOneContext = contextsToStates.getValue(new.context) val stateOnNewOneContext = repo.getContextState(new.context)
if (onContextsConflictResolver(old, new, stateOnNewOneContext)) { if (stateOnNewOneContext == null || onContextsConflictResolver(old, new, stateOnNewOneContext)) {
endChainWithoutLock(stateOnNewOneContext) stateOnNewOneContext ?.let { endChainWithoutLock(it) }
contextsToStates.remove(old.context) repo.removeState(old)
contextsToStates[new.context] = new repo.set(new)
_onChainStateUpdated.emit(old to new) _onChainStateUpdated.emit(old to new)
} }
} }
@ -66,15 +73,15 @@ class DefaultStatesManager(
} }
override suspend fun startChain(state: State) = mapMutex.withLock { override suspend fun startChain(state: State) = mapMutex.withLock {
if (!contextsToStates.containsKey(state.context)) { if (!repo.contains(state.context)) {
contextsToStates[state.context] = state repo.set(state)
_onStartChain.emit(state) _onStartChain.emit(state)
} }
} }
private suspend fun endChainWithoutLock(state: State) { private suspend fun endChainWithoutLock(state: State) {
if (contextsToStates[state.context] == state) { if (repo.getContextState(state.context) == state) {
contextsToStates.remove(state.context) repo.removeState(state)
_onEndChain.emit(state) _onEndChain.emit(state)
} }
} }
@ -85,6 +92,6 @@ class DefaultStatesManager(
} }
} }
override suspend fun getActiveStates(): List<State> = contextsToStates.values.toList() override suspend fun getActiveStates(): List<State> = repo.getStates()
} }

View File

@ -1,24 +1,17 @@
package dev.inmo.micro_utils.fsm.repos.common package dev.inmo.micro_utils.fsm.repos.common
import dev.inmo.micro_utils.fsm.common.State import dev.inmo.micro_utils.fsm.common.State
import dev.inmo.micro_utils.fsm.common.managers.DefaultStatesManagerStatesRepo import dev.inmo.micro_utils.fsm.common.managers.DefaultStatesManagerRepo
import dev.inmo.micro_utils.repos.* import dev.inmo.micro_utils.repos.*
import dev.inmo.micro_utils.repos.pagination.getAll import dev.inmo.micro_utils.repos.pagination.getAll
class KeyValueBasedDefaultStatesManagerStatesRepo( class KeyValueBasedDefaultStatesManagerRepo(
private val keyValueRepo: KeyValueRepo<Any, State> private val keyValueRepo: KeyValueRepo<Any, State>
) : DefaultStatesManagerStatesRepo { ) : DefaultStatesManagerRepo {
override suspend fun newState(state: State) { override suspend fun set(state: State) {
keyValueRepo.set(state.context, state) keyValueRepo.set(state.context, state)
} }
override suspend fun updateState(from: State, to: State) {
if (from.context != to.context && keyValueRepo.get(from.context) == from) {
keyValueRepo.unset(from.context)
}
keyValueRepo.set(to.context, to)
}
override suspend fun removeState(state: State) { override suspend fun removeState(state: State) {
if (keyValueRepo.get(state.context) == state) { if (keyValueRepo.get(state.context) == state) {
keyValueRepo.unset(state.context) keyValueRepo.unset(state.context)
@ -26,4 +19,7 @@ class KeyValueBasedDefaultStatesManagerStatesRepo(
} }
override suspend fun getStates(): List<State> = keyValueRepo.getAll { keys(it) }.map { it.second } override suspend fun getStates(): List<State> = keyValueRepo.getAll { keys(it) }.map { it.second }
override suspend fun getContextState(context: Any): State? = keyValueRepo.get(context)
override suspend fun contains(context: Any): Boolean = keyValueRepo.contains(context)
} }

View File

@ -9,7 +9,7 @@ import kotlinx.coroutines.flow.*
import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.sync.withLock
@Deprecated @Deprecated("Replace with DefaultStatesManager and KeyValueBasedDefaultStatesManagerRepo")
class KeyValueBasedStatesManager( class KeyValueBasedStatesManager(
private val keyValueRepo: KeyValueRepo<Any, State>, private val keyValueRepo: KeyValueRepo<Any, State>,
private val onContextsConflictResolver: suspend (old: State, new: State, currentNew: State) -> Boolean = { _, _, _ -> true } private val onContextsConflictResolver: suspend (old: State, new: State, currentNew: State) -> Boolean = { _, _, _ -> true }