Acquire lock for several seconds to create LightClasses to workaround dead-lock over resolve
Relates to #KT-34279
This commit is contained in:
+15
-4
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user