diff --git a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/SmartMutex.kt b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/SmartMutex.kt index 7c5f61680d8..2c7b2de738e 100644 --- a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/SmartMutex.kt +++ b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/SmartMutex.kt @@ -50,77 +50,31 @@ sealed interface SmartMutex { override val lockStateFlow: StateFlow = _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 } } } diff --git a/coroutines/src/commonTest/kotlin/SmartMutexTests.kt b/coroutines/src/commonTest/kotlin/SmartMutexTests.kt index b75a26b9c2d..90019408539 100644 --- a/coroutines/src/commonTest/kotlin/SmartMutexTests.kt +++ b/coroutines/src/commonTest/kotlin/SmartMutexTests.kt @@ -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()) }