mirror of
https://github.com/InsanusMokrassar/MicroUtils.git
synced 2026-10-17 18:30:28 +00:00
rewrite of SmartMutex
This commit is contained in:
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user