Closed preetb123 closed 7 months ago
Hi, could you provide a code sample facing this issue?
Is the falling StateMachine called from multiple threads? Pease specify with which scope do you call createStateMachine function.
Is the falling StateMachine called from multiple threads? Pease specify with which scope do you call createStateMachine function.
@nsk90 , thank you very much for replying.
I called createStateMachine
from GlobalScope.launch{}
.
@nsk90 for now I have made following modification and so far it did not crash. But not sure if it is the right way to do
private val SINGLE_THREAD = newSingleThreadContext("mvi")
fun sendEvent(event: AppEvent): Unit = intent {
sendEffect(ModelEffect.ControlEventSent(event))
val machine = NysnoApplication.getApp().machine
GlobalScope.launch {
withContext(SINGLE_THREAD){
machine?.processEvent(event)
}
}
}
I think the crash is related to GlobalScope
usage. As it uses Default coroutine dispatcher internally (correct?). This dispatcher uses thread pool, not a single thread. So processEvent
may be executed concurrenly, which is wrong. You should pass single threaded coroutine context into createStateMachine
function.
Try it. If this does not help, I will try to reproduce your issue locally to debug.
@nsk90 is this fine?
GlobalScope.launch {
withContext(singleThread){
machine = createAndStartStateMachine()
machine?.start(null)
printStateMachine()
}
}
yeah this looks better, please inline createAndStartStateMachine()
body, so is can see pure library methods, to give exact answer.
I think you don't need GlobalScope
at all.
Having separate single thread you can create your own CoroutineScope
from it, and use the latter as createStateMachine
argument.
This is not critical and does not change the way how it should work, both cases are effectively equivalent.
Something like this:
CoroutineScope(newSingleThreadContext("your context"))
// or
CoroutineScope(Dispatchers.Default.limitedParallelism(1))
this is how createAndStartStateMachine
looks. Actually it took lot of time to create if I pass start = true
while creating and I got machine
variable not initialized error in sendEvent
function while accessing machine
, so had to change it this way.
suspend fun createAndStartStateMachine(): StateMachine {
return createStateMachine(GlobalScope, "MyAppState", ChildMode.PARALLEL, start = false) {
logger = StateMachine.Logger {
Timber.tag("StateMachine").d(it())
}
state("AppState") {
addInitialState(AppState.LoginState) {
....
}
...
}
}
}
CoroutineScope(Dispatchers.Default.limitedParallelism(1))
you mean like this?
val applicationScope = CoroutineScope(Dispatchers.Default.limitedParallelism(1))
applicationScope.launch {
machine = createAndStartStateMachine()
machine?.start(null)
printStateMachine()
}
not, actually
GlobalScope.launch { // any scope/context, it does not matter
val machineScope = CoroutineScope(newSingleThreadContext("your context")) // must be single threaded, you should cancel it when you complete working with the machine.
val machine = createStateMachine(machineScope, ....) {...} // this scope/context matters
//...
// the library will execute all suspendable functions from machineScope context
machine.processEvent(...) // will switch to machineScope internally, so you can call it from any thread and context.
// please not that it is only about suspendable library functions. all other non-suspendable api cannot be used in
// multithreaded environment without proper synchronization.
}
not, actually
GlobalScope.launch { // any scope/context, it does not matter val machineScope = CoroutineScope(newSingleThreadContext("your context")) // must be single threaded, you should cancel it when you complete working with the machine. val machine = createStateMachine(machineScope, ....) {...} // this scope/context matters //... // the library will execute all suspendable functions from machineScope context machine.processEvent(...) // will switch to machineScope internally, so you can call it from any thread and context. // please not that it is only about suspendable library functions. all other non-suspendable api cannot be used in // multithreaded environment without proper synchronization. }
thank you very much @nsk90
App crash. How to avoid this while still processing every single event? @nsk90