so this is just a fun behaviour of coroutines I th...
# coroutines
g
so this is just a fun behaviour of coroutines I thought I would post here. I had a class called
UserProcessMutex
, it would create a file and acquire a file lock on it, and thus if somebody ran my program twice the second copy could detect the first copy by trying to acquire the same lock. It had functions
tryLock(): Boolean
and
release()
on it.
tryLock
looked roughly like this:
Copy code
fun tryLock(): Boolean {
  val path = Path.of("/my-prog-mutex.lock")
  
  val fileChannel = FileChannel.open(path, StandardOpenOption.WRITE)

  lock = fileChannel.tryLock()

  if(lock == null) fileChannel.close()

  return lock != null
}
I wrote my code like this
Copy code
suspend fun main(){
  
  val mutex = UserProcessMutex()
  mutex.tryLock()

  enterYieldingMainEventLoop()

  doFinallyTasks()
}
and I would run two different instances of
main
, and both would succeed in getting the lock. Why? This problem disappeared when i added some code in a finally block to call
release()
As best I can figure out, kotlins closures were dropping
mutex
once the function `enterYieldingMainEventLoop`actually suspended, meaning the GC would clean up the
FileChannel
references, releasing the locks. This is interesting because the same code without suspension points would keep
mutex
on stack across suspension boundaries, which would've prevented this bug. Or maybe it was something else.
c
Suspension points can end the scope of a variable. If a variable exists before a suspension point and doesn't exist after, the compiler can delete it at the suspension point.