Acquire lock for several seconds to create LightClasses to workaround dead-lock over resolve

Relates to #KT-34279
This commit is contained in:
Vladimir Dolzhenko
2019-10-24 13:14:04 +02:00
parent c676a30919
commit d16a1b14d8
@@ -21,13 +21,15 @@ import com.intellij.psi.util.CachedValueProvider
import com.intellij.util.ArrayUtil
import com.intellij.util.containers.ContainerUtil
import gnu.trove.THashMap
import java.util.concurrent.TimeUnit
import java.util.concurrent.locks.ReentrantLock
class KotlinClassInnerStuffCache(val myClass: PsiExtensibleClass, externalDependencies: List<Any>) {
private val myTracker = SimpleModificationTracker()
private val dependencies: List<Any> = externalDependencies + myTracker
fun <T : Any> get(initializer: () -> T) = object : Lazy<T> {
// Note: holder is used as initialization monitor as well
private val lock = ReentrantLock()
private val holder = lazyPub {
PsiCachedValueImpl(PsiManager.getInstance(myClass.project),
CachedValueProvider<T> {
@@ -49,20 +51,29 @@ class KotlinClassInnerStuffCache(val myClass: PsiExtensibleClass, externalDepend
// Assumption 1: Lets say A calculation requires another value e.g. B to be calculated
// Assumption 2: Thread T2 wants to calculate value for B
// to avoid dead-lock case we mark thread as doing calculation and acquire lock only once per thread
// to avoid dead-lock
// - we mark thread as doing calculation and acquire lock only once per thread
// as a trade-off to prevent dependent value could be calculated several time
// due to CAS (within putUserDataIfAbsent etc) the same instance of calculated value will be used
if (!initIsRunning.get()) {
synchronized(holder) {
// TODO: NOTE: acquire lock for a several seconds to avoid dead-lock via resolve is a WORKAROUND
val notRunning = !initIsRunning.get()
if (notRunning && lock.tryLock(500, TimeUnit.MILLISECONDS)) {
try {
initIsRunning.set(true)
try {
computeValue()
} finally {
initIsRunning.set(false)
}
} finally {
lock.unlock()
}
} else {
if (notRunning) { // means unable to acquire lock within specified time limit
Logger.getInstance(KotlinClassInnerStuffCache::class.java).error("failed to acquire lock for $initializer")
}
computeValue()
}
}