FIR IDE: add deadlock checking for read/write resolve locks
This commit is contained in:
+35
-7
@@ -6,8 +6,7 @@
|
|||||||
package org.jetbrains.kotlin.idea.fir.low.level.api.file.builder
|
package org.jetbrains.kotlin.idea.fir.low.level.api.file.builder
|
||||||
|
|
||||||
import com.google.common.collect.MapMaker
|
import com.google.common.collect.MapMaker
|
||||||
import com.intellij.openapi.diagnostic.logger
|
import org.jetbrains.kotlin.idea.fir.low.level.api.annotations.PrivateForInline
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirFile
|
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.util.lockWithPCECheck
|
import org.jetbrains.kotlin.idea.fir.low.level.api.util.lockWithPCECheck
|
||||||
import java.util.concurrent.ConcurrentMap
|
import java.util.concurrent.ConcurrentMap
|
||||||
import java.util.concurrent.locks.ReadWriteLock
|
import java.util.concurrent.locks.ReadWriteLock
|
||||||
@@ -17,22 +16,51 @@ import kotlin.concurrent.withLock
|
|||||||
internal class LockProvider<KEY> {
|
internal class LockProvider<KEY> {
|
||||||
private val locks: ConcurrentMap<KEY, ReadWriteLock> = MapMaker().weakKeys().makeMap()
|
private val locks: ConcurrentMap<KEY, ReadWriteLock> = MapMaker().weakKeys().makeMap()
|
||||||
|
|
||||||
|
@OptIn(PrivateForInline::class)
|
||||||
|
private val deadLockGuard = DeadLockGuard()
|
||||||
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
private inline fun getLockFor(key: KEY) = locks.getOrPut(key) { ReentrantReadWriteLock() }
|
private inline fun getLockFor(key: KEY) = locks.getOrPut(key) { ReentrantReadWriteLock() }
|
||||||
|
|
||||||
|
@OptIn(PrivateForInline::class)
|
||||||
inline fun <R> withReadLock(key: KEY, action: () -> R): R {
|
inline fun <R> withReadLock(key: KEY, action: () -> R): R {
|
||||||
val readLock = getLockFor(key).readLock()
|
val readLock = getLockFor(key).readLock()
|
||||||
return readLock.withLock { action() }
|
return deadLockGuard.guardReadLock { readLock.withLock { action() } }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(PrivateForInline::class)
|
||||||
inline fun <R> withWriteLock(key: KEY, action: () -> R): R {
|
inline fun <R> withWriteLock(key: KEY, action: () -> R): R {
|
||||||
val writeLock = getLockFor(key).writeLock()
|
val writeLock = getLockFor(key).writeLock()
|
||||||
return writeLock.withLock { action() }
|
return deadLockGuard.guardWriteLock { writeLock.withLock { action() } }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(PrivateForInline::class)
|
||||||
inline fun <R> withWriteLockPCECheck(key: KEY, lockingIntervalMs: Long, action: () -> R): R {
|
inline fun <R> withWriteLockPCECheck(key: KEY, lockingIntervalMs: Long, action: () -> R): R {
|
||||||
val writeLock = getLockFor(key).writeLock()
|
val writeLock = getLockFor(key).writeLock()
|
||||||
return writeLock.lockWithPCECheck(lockingIntervalMs, action)
|
return deadLockGuard.guardWriteLock { writeLock.lockWithPCECheck(lockingIntervalMs, action) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PrivateForInline
|
||||||
|
internal class DeadLockGuard {
|
||||||
|
private val readLocksCount = ThreadLocal.withInitial { 0 }
|
||||||
|
|
||||||
|
inline fun <R> guardReadLock(action: () -> R): R {
|
||||||
|
readLocksCount.set(readLocksCount.get() + 1)
|
||||||
|
return try {
|
||||||
|
action()
|
||||||
|
} finally {
|
||||||
|
readLocksCount.set(readLocksCount.get() - 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <R> guardWriteLock(action: () -> R): R {
|
||||||
|
|
||||||
|
if (readLocksCount.get() > 0) {
|
||||||
|
throw ReadWriteDeadLockException()
|
||||||
|
}
|
||||||
|
return action()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ReadWriteDeadLockException : IllegalStateException("Acquiring write lock when read lock hold")
|
||||||
|
|||||||
Reference in New Issue
Block a user