https://kotlinlang.org logo
Title
e

Erik

03/01/2021, 2:11 PM
I wonder if the following is a UniFlow bug: https://gist.github.com/erikhuizinga/05a2712bdb0f32c45fa192cd137770e0 UniFlow's internals use
CoroutineScope.launch(<http://Dispatchers.IO|Dispatchers.IO>) { action() }
(not literally, but that's what it comes down to). If you create a lot of
action {}
actions quickly, or if your system is slow, then these launched coroutines aren't necessarily run in order. Remember: launching is fire-and-forget. You don't know when the coroutine is actually run! So, is my code just an example of bad practice? Or is my code an example of a UniFlow running actions in an unordered fashion, and is that a bug?
An alternative approach: 1. Start a slow action, e.g. add some delay before setting state 2. Start a fast action immediately after the slow action You will likely see that the effect of the fast action happens before the slow action completes
Scratch that last comment, the
actor
in the uniflow internals should ensure processing of actions in the order they are sent to it, no matter how fast or slow the actions run. However, my original bug still stands: UniFlow uses
launch
to ultimately send actions to the actor, which may cause actions going out of order
And that seems like a 🐛 bug to me in a state machine with action inputs and state/event outputs!
a

arnaud.giuliani

03/01/2021, 4:58 PM
Hey @Erik what version do you use?
UniFlow uses 
launch
 to ultimately send actions to the actor, which may cause actions going out of order
Ok, interesting 🤔
e

Erik

03/01/2021, 5:07 PM
For the version, see the linked gist: 0.11.6
Coroutines 1.3.5 (but that shouldn't matter)
Kotlin 1.4.31
In other words: is it a bug if UniFlow doesn't preserve action order? Because then I can file a bug report
a

arnaud.giuliani

03/02/2021, 9:00 AM
Did you tried woth last 0.13.0 alpha?
I agree it should preserve order as much as possible
apart if you launch new job without joining them from actions
@Erik here below the new code of the reducer:
there is no
launch
but just a
withContext
to execute directly the function
e

Erik

03/08/2021, 12:46 PM
a

arnaud.giuliani

03/08/2021, 1:45 PM
withContext can only be called from a coroutines
here we are just enqueing action into the Actor
I can write this:
with a
launchOnMain
e

Erik

03/09/2021, 10:07 AM
I understand, it's all because you use
actor.send
which is a
suspend fun
So then I would argue that that shouldn't be used, but
offer
should be used instead. The buffer is there for a reason: we should limit concurrency in some way, because it's not free. When the action buffer is full, we can either suspend, ignore the action, or worse: throw.
Suspending requires launching a coroutine. Launching many coroutines doesn't provide order guarantees, unless you can maybe launch them in some scope/context that preserves launch order?
So either do not launch and
offer
, or use a different scope/context.
runBlocking
would work, but that could block the user, so not an option
a

arnaud.giuliani

03/09/2021, 10:45 AM
interesting catch 👍
offer
could block then ... but we avoid the coroutines lunch just for offering
e

Erik

03/09/2021, 10:49 AM
I would maybe just log an exception when the buffer is full
But not block. It would lose actions though. Not sure what's better. Maybe this behaviour can be configurable? Just like the shared flow on buffer overflow behaviour
a

arnaud.giuliani

03/09/2021, 10:53 AM
Or just log an error? 🤔
as if we throw an exception we can’t do anything either
Can go with something simple like that:
e

Erik

03/09/2021, 11:43 AM
Sure
So, in conclusion: is this (being) picked up, or do I need to do anything, e.g. create an issue?
a

arnaud.giuliani

03/09/2021, 3:22 PM
already on master 💪
I’m polishing some last stuff to make the 1.0.0