David W
12/29/2021, 8:38 PMList<Lock>
and an anonymous function. It needs to lock all of the locks, execute the function, and then unlock all locks.
The anonymous function should also allow non-local returns. I've been banging my head against this for hours, anyone have a solution?
inline fun <T> write(locks: List<ReentrantReadWriteLock>, action: () -> T): T
Joffrey
12/29/2021, 8:41 PMfinally
block around the the call to action()
, so that it's done in both normal and exceptional completionDavid W
12/29/2021, 8:43 PMinline fun <T> write(lockContext: LockContext = IOLocks.defaultLock, action: () -> T): T {
val finalLock = lockContext.locks.last()
lockContext.locks.forEach { lock ->
if (lock === finalLock) {
val result = try {
lock.write {
Timber.tag(tag)
.d { "Write locked from ${timber.Timber.findClassName()} on ${getCurrentThreadName()}." }
flow.tryEmit(true)
try {
action()
} finally {
Timber.tag(tag)
.d { "Write unlocked from ${timber.Timber.findClassName()} on ${getCurrentThreadName()}." }
flow.tryEmit(false)
}
}
} finally {
(lockContext.locks - finalLock).forEach { lockToUnlock ->
Timber.d { "Unlocking $lockToUnlock" }
val readCount = if (lockToUnlock.writeHoldCount == 0) lockToUnlock.readHoldCount else 0
repeat(readCount) { lockToUnlock.readLock().lock() }
lockToUnlock.writeLock().unlock()
}
}
return result
} else {
Timber.d { "Locking $lock" }
val rl = lock.readLock()
val readCount = if (lock.writeHoldCount == 0) lock.readHoldCount else 0
repeat(readCount) { rl.unlock() }
lock.writeLock().lock()
}
}
// If no locks, run func and return result.
return action.invoke()
}
java.lang.IllegalMonitorStateException
at java.base/java.util.concurrent.locks.ReentrantReadWriteLock$Sync.tryRelease(ReentrantReadWriteLock.java:372)
at java.base/java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1006)
at java.base/java.util.concurrent.locks.ReentrantReadWriteLock$WriteLock.unlock(ReentrantReadWriteLock.java:1147)
action
func is changing threads (default dispatcher) between when it locks and unlocks a lockDispatchers.Default
, could it change threads during execution?
suspend fun Path.awaitWrite(timeoutMillis: Long = 1000): Path {
var delayAcc = 0
while (!this@awaitWrite.isWritable() && delayAcc < timeoutMillis) {
delayAcc += 10
delay(timeMillis = 10)
}
if (this@awaitWrite.isWritable()) {
return this@awaitWrite
} else {
throw RuntimeException("Timed out waiting for write access to $this.")
}
}
runBlocking
and no more IllegalMonitorStateException
Joffrey
12/29/2021, 9:48 PMIf this were run onYes, every suspension point (every call to a suspend function) acts as a dispatch point, and the suspend function can resume on any thread of the dispatcher., could it change threads during execution?Dispatchers.Default
ephemient
12/30/2021, 12:43 AMDavid W
12/30/2021, 2:29 AMMutex
isn't reentrant, though. Each piece of my code that touches files is responsible for using a lock, so there are bits that call each other, resulting in double calls to the same lock from the same thread, by design.ReentrantReadWriteLock
for Mutex that i seeephemient
12/30/2021, 2:52 AMDavid W
12/30/2021, 3:31 AMNick Allen
01/01/2022, 3:33 AMephemient
01/02/2022, 4:28 AM