From ce7790389804495bc0d9c5a75b3d319ee8c5ce04 Mon Sep 17 00:00:00 2001 From: Ilya Kirillov Date: Tue, 22 Dec 2020 13:58:05 +0100 Subject: [PATCH] FIR IDE: add deadlock checking for read/write resolve locks --- .../level/api/file/builder/LockProvider.kt | 42 +++++++++++++++---- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/file/builder/LockProvider.kt b/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/file/builder/LockProvider.kt index 7011942030e..3b4bd676b0a 100644 --- a/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/file/builder/LockProvider.kt +++ b/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/file/builder/LockProvider.kt @@ -6,8 +6,7 @@ package org.jetbrains.kotlin.idea.fir.low.level.api.file.builder import com.google.common.collect.MapMaker -import com.intellij.openapi.diagnostic.logger -import org.jetbrains.kotlin.fir.declarations.FirFile +import org.jetbrains.kotlin.idea.fir.low.level.api.annotations.PrivateForInline import org.jetbrains.kotlin.idea.fir.low.level.api.util.lockWithPCECheck import java.util.concurrent.ConcurrentMap import java.util.concurrent.locks.ReadWriteLock @@ -17,22 +16,51 @@ import kotlin.concurrent.withLock internal class LockProvider { private val locks: ConcurrentMap = MapMaker().weakKeys().makeMap() + @OptIn(PrivateForInline::class) + private val deadLockGuard = DeadLockGuard() + @Suppress("NOTHING_TO_INLINE") private inline fun getLockFor(key: KEY) = locks.getOrPut(key) { ReentrantReadWriteLock() } + @OptIn(PrivateForInline::class) inline fun withReadLock(key: KEY, action: () -> R): R { val readLock = getLockFor(key).readLock() - return readLock.withLock { action() } + return deadLockGuard.guardReadLock { readLock.withLock { action() } } } - + @OptIn(PrivateForInline::class) inline fun withWriteLock(key: KEY, action: () -> R): R { val writeLock = getLockFor(key).writeLock() - return writeLock.withLock { action() } + return deadLockGuard.guardWriteLock { writeLock.withLock { action() } } } + @OptIn(PrivateForInline::class) inline fun withWriteLockPCECheck(key: KEY, lockingIntervalMs: Long, action: () -> R): R { val writeLock = getLockFor(key).writeLock() - return writeLock.lockWithPCECheck(lockingIntervalMs, action) + return deadLockGuard.guardWriteLock { writeLock.lockWithPCECheck(lockingIntervalMs, action) } } -} \ No newline at end of file +} + +@PrivateForInline +internal class DeadLockGuard { + private val readLocksCount = ThreadLocal.withInitial { 0 } + + inline fun guardReadLock(action: () -> R): R { + readLocksCount.set(readLocksCount.get() + 1) + return try { + action() + } finally { + readLocksCount.set(readLocksCount.get() - 1) + } + } + + inline fun guardWriteLock(action: () -> R): R { + + if (readLocksCount.get() > 0) { + throw ReadWriteDeadLockException() + } + return action() + } +} + +class ReadWriteDeadLockException : IllegalStateException("Acquiring write lock when read lock hold")