Mutex is non-reentrant
If the same co-routine that acquired the lock attempts to acquire it again, it will deadlock. This is because the lock is not reentrant.
package com.glassthought.sandbox
import gt.sandbox.util.output.Out
import kotlinx.coroutines.*
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
val out = Out.standard()
class AttemptToGrabLockFromSameCoroutine {
private var value = 0
private val mutex = Mutex() // Mutex for coroutine-safe access to 'value'
// Suspend function to use coroutine-friendly synchronization with timeout
suspend fun outerCall() {
out.println("outerCall outside of mutex")
// Attempt to acquire the lock with a timeout
val success = withTimeoutOrNull(1000L) { // 1000ms timeout
mutex.withLock {
out.println("outerCall INSIDE of mutex")
innerCall()
}
}
if (success == null) {
out.println("ERROR: outerCall could not acquire the lock within timeout")
}
}
private suspend fun innerCall() {
out.println("innerCall outside of mutex")
// Attempt to acquire the lock with a timeout
val success = withTimeoutOrNull(1000L) { // 1000ms timeout
mutex.withLock {
out.println("innerCall INSIDE of mutex")
value++
}
}
if (success == null) {
out.println("ERROR: innerCall could not acquire the lock within timeout")
}
}
fun getValue() = value
}
fun main() = runBlocking {
val attemptToGrabLockFromSameCoroutine = AttemptToGrabLockFromSameCoroutine()
// Launch coroutines instead of creating threads
coroutineScope {
val jobs = (1..2).map {
launch { attemptToGrabLockFromSameCoroutine.outerCall() }
}
jobs.forEach { it.join() } // Wait for all coroutines to complete
}
println("Value: ${attemptToGrabLockFromSameCoroutine.getValue()}")
}
Command to reproduce:
gt.sandbox.checkout.commit fec6776d79e44c8ebe56 \
&& cd "${GT_SANDBOX_REPO}" \
&& cmd.run.announce "./gradlew run"
Recorded output of command:
> Task :app:checkKotlinGradlePluginConfigurationErrors SKIPPED
> Task :app:compileKotlin UP-TO-DATE
> Task :app:compileJava NO-SOURCE
> Task :app:processResources NO-SOURCE
> Task :app:classes UP-TO-DATE
> Task :app:run
[2024-11-17T01:11:38.506307Z][elapsed-since-start: 54ms][tname:main/tid:1] outerCall outside of mutex
[2024-11-17T01:11:38.530549Z][elapsed-since-start: 66ms][tname:main/tid:1] outerCall INSIDE of mutex
[2024-11-17T01:11:38.530880Z][elapsed-since-start: 66ms][tname:main/tid:1] innerCall outside of mutex
[2024-11-17T01:11:38.532629Z][elapsed-since-start: 68ms][tname:main/tid:1] outerCall outside of mutex
[2024-11-17T01:11:39.536510Z][elapsed-since-start: 1072ms][tname:main/tid:1] ERROR: innerCall could not acquire the lock within timeout
[2024-11-17T01:11:39.538559Z][elapsed-since-start: 1074ms][tname:main/tid:1] ERROR: outerCall could not acquire the lock within timeout
[2024-11-17T01:11:39.539809Z][elapsed-since-start: 1075ms][tname:main/tid:1] ERROR: outerCall could not acquire the lock within timeout
Value: 0
BUILD SUCCESSFUL in 1s
2 actionable tasks: 1 executed, 1 up-to-date
Backlinks