Anton Krutko
12/04/2025, 6:22 AMcounter values themselves is not important in that example. Coroutines version 1.10.2 and 1.8.0.
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
var counter = 0L
val mutableSharedFlow = MutableSharedFlow<Long>()
var cs: CoroutineScope? = null
fun start() {
cs?.cancel()
cs = CoroutineScope(Dispatchers.IO + SupervisorJob())
mutableSharedFlow.onEach {
delay(50)
println(it)
}.launchIn(cs!!)
cs!!.launch {
while (currentCoroutineContext().isActive) {
mutableSharedFlow.emit(counter)
counter++
}
}
}
suspend fun main() {
while (true) {
start()
println("restart, counter = $counter")
delay(3000)
}
}
The output we see:
....
47
48
49
50
51
52
53
54
restart, counter = 56
restart, counter = 56
restart, counter = 56
restart, counter = 56
restart, counter = 56
... // proper behavior (emissions happening) is never restored after this point
Any help will be greatly appreciated.louiscad
12/04/2025, 9:13 AMAnton Krutko
12/04/2025, 10:01 AMcollectLatest. It doesn't seem to help. The understanding, however, of the core reason for this issue to happen is very crucial to us. Workaround is not that important.Mohammad Fallah
12/06/2025, 7:03 PMextraBuffer it works as expected, but I don't know why?
It seems this occurs only when no buffer is used and both the collector and emitter are cancelled simultaneously while the collector is suspended.louiscad
12/06/2025, 8:02 PMextraBufferCapacity are for.
Knowing that, having a special behavior for the case of a single collector would be surprising as that'd be a kind of unintended side effect.
For example, imagine adding a second collector that saves data to a usage statistics/analytics local database (to be flushed later). If the storage saving is done in a suspend or blocking way (i.e. not memory buffered until another coroutine consumes it), it can become slow for external reasons (e.g. system or app update, or file transfer reducing I/O capacity).
Would you want that to affect the behavior in an unpredictable and unchosen way?
I think you wouldn't.
So, to make it explicit, and avoid any weirdness, you need an extra buffer if there's no replay, to be fed for slow collectors, and for fast collectors too, since being fast can never be guaranteed for the library code.Anton Krutko
12/08/2025, 1:48 AMMutableSharedFlow has an extra buffer or not. If there is no extra buffer configured cancellation mechanism should still work in an expected way.Anton Krutko
12/14/2025, 1:32 AM