groostav
03/08/2025, 1:02 AMUserProcessMutex
, 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:
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
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.CLOVIS
03/09/2025, 12:59 PM