From f9298d8e85bd9ba44f76d16009cdc868ed4a3082 Mon Sep 17 00:00:00 2001 From: Ilya Kirillov Date: Tue, 13 Dec 2022 14:02:59 +0100 Subject: [PATCH] [LL FIR] try to check for PCE in ValueWithPostCompute to avoid possible deadlocks --- .../level/api/fir/caches/ValueWithPostCompute.kt | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/caches/ValueWithPostCompute.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/caches/ValueWithPostCompute.kt index 848bf272302..833b1ae9951 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/caches/ValueWithPostCompute.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/caches/ValueWithPostCompute.kt @@ -5,7 +5,9 @@ package org.jetbrains.kotlin.analysis.low.level.api.fir.fir.caches +import org.jetbrains.kotlin.analysis.low.level.api.fir.util.lockWithPCECheck import org.jetbrains.kotlin.util.shouldIjPlatformExceptionBeRethrown +import java.util.concurrent.locks.ReentrantLock /** * Lazily calculated value which runs postCompute in the same thread, @@ -24,6 +26,7 @@ internal class ValueWithPostCompute( ) { private var _calculate: ((KEY) -> Pair)? = calculate private var _postCompute: ((KEY, VALUE, DATA) -> Unit)? = postCompute + private val lock = ReentrantLock() /** * can be in one of the following three states: @@ -58,7 +61,7 @@ internal class ValueWithPostCompute( if (stateSnapshot.threadId == Thread.currentThread().id) { return stateSnapshot.value as VALUE } else { - synchronized(this) { // wait until other thread which holds the lock now computes the value + lock.lockWithPCECheck(LOCKING_INTERVAL_MS) { // wait until other thread which holds the lock now computes the value when (val newStateSnapshot = value) { ValueIsNotComputed -> { // if we have a PCE during value computation, then we will enter the critical section with `value == ValueIsNotComputed` @@ -77,7 +80,7 @@ internal class ValueWithPostCompute( } } } - ValueIsNotComputed -> synchronized(this) { + ValueIsNotComputed -> lock.lockWithPCECheck(LOCKING_INTERVAL_MS) { return computeValueWithoutLock() } is ExceptionWasThrownDuringValueComputation -> { @@ -92,7 +95,7 @@ internal class ValueWithPostCompute( @Suppress("UNCHECKED_CAST") // should be called under a synchronized section private fun computeValueWithoutLock(): VALUE { - require(Thread.holdsLock(this)) + require(lock.isHeldByCurrentThread) // if we entered synchronized section that's mean that the value is not yet calculated and was not started to be calculated // or the some other thread calculated the value while we were waiting to acquire the lock @@ -146,4 +149,6 @@ internal class ValueWithPostCompute( private class ValueIsPostComputingNow(val value: Any?, val threadId: Long) private class ExceptionWasThrownDuringValueComputation(val error: Throwable) private object ValueIsNotComputed -} \ No newline at end of file +} + +private const val LOCKING_INTERVAL_MS = 50L \ No newline at end of file