Is it a normal behaviour that if I do ```// This i...
# coroutines
p
Is it a normal behaviour that if I do
Copy code
// This is in a loop
val e = async {
  writeFileToDisk()
}
filesBeingWritten.add(e)

//somewhere else 
filesBeingWritten.awaitAll()
Is much slower than this
Copy code
// This is in a loop
writeFileToDisk()
where
writeFileToDisk()
is a suspending function calling
Copy code
withContext(<http://Dispatchers.IO|Dispatchers.IO>) {
  val get = Paths.get(imageFilename)
  Files.write(get, bytes)
}
And by "slower" I mean it never seems to even complete..? Is there a limitation / maximum amount of Deferred objects we can await at a same time?
o
I don't think there's a maximum, but depending how you're calling
async
and where the
awaitAll()
is, you might be causing some unexpected issues
if you can show a full (but minimal) example, it should be explainable
p
Copy code
private suspend fun closeMotion(motionDirectory: Path) = coroutineScope {
    // Some code omitted

    val begin = Instant.now()
    val f1 = listOf(*filesBeingWritten.toTypedArray())
    println("Awaiting ${f1.count()} frames to be written.")
    filesBeingWritten.clear()
    try {

      f1.awaitAll()
    } catch (e: Exception) {
      println("AAAA $e")
    }
    println("It took : ${Duration.between(begin, Instant.now()).seconds}")
  }
sure, so here's the part of the code that's calling
awaitAll()
including some time benchmarks
the
filesBeingWritten
object is just a mutableList of
Deferred<Unit>
o
FYI that list copy can be written as
filesBeingWritten.toList()
-- but probably not the issue here
p
maybe the stdout is bugged too... because I do print some stuff in
writeFileToDisk
and it only gets printed to stdout once I cancel the root job
does
awaitAll
have a timeout mechanism by default?
d
No
c
What’s the Disptacher being used when you call
closeMotion
? If it’s a single-threaded dispatcher, then it could be having a deadlock where it spawns an async task, but needs the current task to finish before it can start processing it, but it can’t finish until the new async task has finished
p
I assume it's using the Default, but I am doing something different for this one
so this might be the thing
here's what I do
Copy code
// declared top level
val supervisor = SupervisorJob()
private val motionScope = CoroutineScope(supervisor + EmptyCoroutineContext)
private var motionTimer: Job? = null

  private suspend fun updateMotionTimer() {
    motionTimer?.cancel()
    motionTimer = motionScope.launch {
      val motionDirectory = // Getting the path
      delay(RECORDING_DURATION_AFTER_MOTION_IN_SECONDS.seconds)
      if (isActive) {
        closeMotion(motionDirectory)
      }
    }
  }
o
if you're adding to
filesBeingWritten
and reading it from a different thread, it might have concurrency issues -- I'm not clear if your code is written in a way that avoids that
p
in the code above I copied the list specifically to avoid this, but maybe I'm not doing it right
o
copying doesn't avoid that? you have to ensure that the thing that writes to the list is entirely finished and not running by the time you're accessing it
otherwise, since it's not synchronized, data race can occur and all sorts of things can happen, it's unspecified behavior
p
well it's an ever-running process, so it's never really finished
right, I should probably use a synchronized collection
this whole thing is a motion detection algorithm for a security camera, so it's in an infinite loop
s
@Pacane Your use-case is the exact problem solved by a Channel: https://kotlinlang.org/docs/reference/coroutines/channels.html#fan-out
If you don’t want to concurrently read from the channel and write to the FS, just use a standard channel--producer/consumer.
a
this is like mutating the array while iterating over it