rewrite of SmartMutex

This commit is contained in:
2026-10-16 22:12:16 +06:00
parent 721056d550
commit 41d92561a8
2 changed files with 15 additions and 60 deletions

View File

@@ -50,77 +50,31 @@ sealed interface SmartMutex {
override val lockStateFlow: StateFlow<Boolean> = _lockStateFlow.asStateFlow()
private val internalChangesMutex = Mutex()
private val backingMutex = Mutex()
fun immutable() = Immutable(lockStateFlow)
/**
* Holds call until this [SmartMutex] will be re-locked. That means that while [isLocked] == true, [holds] will
* wait for [isLocked] == false and then try to lock
*/
suspend fun lock() {
do {
waitUnlock()
val shouldContinue = internalChangesMutex.withLock {
if (_lockStateFlow.value) {
true
} else {
_lockStateFlow.value = true
false
}
}
} while (shouldContinue && currentCoroutineContext().isActive)
backingMutex.lock()
internalChangesMutex.withLock {
_lockStateFlow.value = true
}
}
/**
* Will try to lock this [SmartMutex] immediataly
*
* @return True if lock was successful. False otherwise
*/
suspend fun tryLock(): Boolean {
return if (!_lockStateFlow.value) {
internalChangesMutex.withLock {
if (!_lockStateFlow.value) {
_lockStateFlow.value = true
true
} else {
false
}
}
} else {
false
val result = backingMutex.tryLock()
if (result) internalChangesMutex.withLock {
_lockStateFlow.value = true
}
return result
}
/**
* If [isLocked] == true - will change it to false and return true. If current call will not unlock this
* [SmartMutex] - false
*/
suspend fun unlock(): Boolean {
if (!_lockStateFlow.value) {
return false
val tryUnlock = internalChangesMutex.withLock {
_lockStateFlow.value.also { if (it) _lockStateFlow.value = false }
}
// The uncontended path cannot suspend, including during cancelled cleanup.
if (internalChangesMutex.tryLock()) {
try {
return unlockState()
} finally {
internalChangesMutex.unlock()
}
}
return withContext(NonCancellable) {
internalChangesMutex.withLock {
unlockState()
}
}
}
// The caller must hold internalChangesMutex while updating the state.
private fun unlockState(): Boolean {
if (!_lockStateFlow.value) {
return false
}
_lockStateFlow.value = false
return true
if (tryUnlock) backingMutex.unlock()
return tryUnlock
}
}
}

View File

@@ -32,7 +32,8 @@ class SmartMutexTests {
releaser.join()
assertTrue(releaser.isCancelled)
assertFalse(mutex.isLocked, "Cancellation must not prevent the delegated unlock")
assertTrue(mutex.isLocked, "Cancellation must not prevent the delegated unlock")
assertTrue(mutex.unlock())
assertTrue(mutex.tryLock())
assertTrue(mutex.unlock())
}