ntherning
10/20/2020, 6:48 AMMutex.withLock
and unexpected freezing in my iOS app when upgrading my project to kotlin 1.4.10 and coroutines 1.3.9-native-mt-2. I think I have been able to make a simple reproduction case:
runBlocking(newSingleThreadContext("thread")) {
val mutex = Mutex().apply { ensureNeverFrozen() }
launch {
runCatching { mutex.withLock { delay(1000) } }.exceptionOrNull()?.printStackTrace()
}
launch {
runCatching { mutex.withLock { delay(1000) } }.exceptionOrNull()?.printStackTrace()
}
}
This launches two coroutines in the same worker. Both tries to take the lock and then delays for 1 sec while holding it. As everything happens within the same worker thread I would not expect any mutability issues. But... This is what happens:
kotlin.native.concurrent.InvalidMutabilityException: mutation attempt of frozen kotlinx.coroutines.sync.MutexImpl.LockedQueue@788375b8
at kfun:kotlin.Throwable#<init>(kotlin.String?){} + 93 (kotlin/kotlin/Throwable.kt:23:37)
at kfun:kotlin.Exception#<init>(kotlin.String?){} + 91 (kotlin/kotlin/Exceptions.kt:23:44)
at kfun:kotlin.RuntimeException#<init>(kotlin.String?){} + 91 (kotlin/kotlin/Exceptions.kt:34:44)
at kfun:kotlin.native.concurrent.InvalidMutabilityException#<init>(kotlin.String){} + 91 (kotlin/kotlin/native/concurrent/Freezing.kt:22:60)
at ThrowInvalidMutabilityException + 690 (kotlin/kotlin/native/concurrent/Internal.kt:92:11)
at MutationCheck + 108
at kfun:kotlinx.coroutines.sync.MutexImpl.LockedQueue.<set-owner>#internal + 102 (sync/Mutex.kt:350:19)
at kfun:kotlinx.coroutines.sync.MutexImpl#unlock(kotlin.Any?){} + 3740 (sync/Mutex.kt:327:29)
at kfun:MutexInvalidMutabilityTest.$doTest$lambda-2$lambda-0COROUTINE$0.invokeSuspend#internal + 2206 (MutexInvalidMutabilityTest.kt:16:61)
...
You can find the full code and full stack trace it in this gist:
https://gist.github.com/ntherning/a1bf85d44a5989d66677762c15587552
Is this expected behavior? Perhaps I am doing something stupid? Or a bug in MutexImpl
?Tijl
10/20/2020, 6:55 AMSemaphore(1)
, I’d advice using try/finally
to release the semaphore (or make that into an extension function like withLock)ntherning
10/20/2020, 7:01 AMMarc Knaup
10/20/2020, 9:59 AMsemaphore.withPermit { … }
.Tijl
10/20/2020, 9:59 AM