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.ArrayUtil
|
||||||
import com.intellij.util.containers.ContainerUtil
|
import com.intellij.util.containers.ContainerUtil
|
||||||
import gnu.trove.THashMap
|
import gnu.trove.THashMap
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
import java.util.concurrent.locks.ReentrantLock
|
||||||
|
|
||||||
class KotlinClassInnerStuffCache(val myClass: PsiExtensibleClass, externalDependencies: List<Any>) {
|
class KotlinClassInnerStuffCache(val myClass: PsiExtensibleClass, externalDependencies: List<Any>) {
|
||||||
private val myTracker = SimpleModificationTracker()
|
private val myTracker = SimpleModificationTracker()
|
||||||
private val dependencies: List<Any> = externalDependencies + myTracker
|
private val dependencies: List<Any> = externalDependencies + myTracker
|
||||||
|
|
||||||
fun <T : Any> get(initializer: () -> T) = object : Lazy<T> {
|
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 {
|
private val holder = lazyPub {
|
||||||
PsiCachedValueImpl(PsiManager.getInstance(myClass.project),
|
PsiCachedValueImpl(PsiManager.getInstance(myClass.project),
|
||||||
CachedValueProvider<T> {
|
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 1: Lets say A calculation requires another value e.g. B to be calculated
|
||||||
// Assumption 2: Thread T2 wants to calculate value for B
|
// 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
|
// 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
|
// due to CAS (within putUserDataIfAbsent etc) the same instance of calculated value will be used
|
||||||
|
|
||||||
if (!initIsRunning.get()) {
|
// TODO: NOTE: acquire lock for a several seconds to avoid dead-lock via resolve is a WORKAROUND
|
||||||
synchronized(holder) {
|
|
||||||
|
val notRunning = !initIsRunning.get()
|
||||||
|
if (notRunning && lock.tryLock(500, TimeUnit.MILLISECONDS)) {
|
||||||
|
try {
|
||||||
initIsRunning.set(true)
|
initIsRunning.set(true)
|
||||||
try {
|
try {
|
||||||
computeValue()
|
computeValue()
|
||||||
} finally {
|
} finally {
|
||||||
initIsRunning.set(false)
|
initIsRunning.set(false)
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
lock.unlock()
|
||||||
}
|
}
|
||||||
} else {
|
} 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()
|
computeValue()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user