[LL FIR] allow parallel resolve for non-jumping phases
^KT-56550 Fixed
diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/file/builder/LLFirLockProvider.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/file/builder/LLFirLockProvider.kt
index 36a0053..260b941 100644
--- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/file/builder/LLFirLockProvider.kt
+++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/file/builder/LLFirLockProvider.kt
@@ -6,7 +6,9 @@
package org.jetbrains.kotlin.analysis.low.level.api.fir.file.builder
import com.intellij.openapi.diagnostic.Logger
+import com.intellij.openapi.util.registry.Registry
import org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve.LLFirLazyResolveContractChecker
+import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.llFirSession
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.checkCanceled
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.lockWithPCECheck
import org.jetbrains.kotlin.fir.FirElementWithResolveState
@@ -18,8 +20,6 @@
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
-import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.llFirSession
-import org.jetbrains.kotlin.fir.declarations.FirFile
/**
* Keyed locks provider.
@@ -34,22 +34,26 @@
inline fun <R> withGlobalLock(
key: FirFile,
lockingIntervalMs: Long = DEFAULT_LOCKING_INTERVAL,
- action: () -> R
- ): R = globalLock.lockWithPCECheck(lockingIntervalMs) {
- val session = key.llFirSession
- if (!session.isValid && shouldRetryFlag.get()) {
- val description = session.ktModule.moduleDescription
- throw InvalidSessionException("Session '$description' is invalid", description)
- }
+ action: () -> R,
+ ): R {
+ if (!globalLockEnabled) return action()
- // Normally, analysis should not be allowed on an invalid session.
- // However, there isn't an easy way to cancel or redo it in general case, as it must then be supported on use-site.
- withRetryFlag(false, action)
+ return globalLock.lockWithPCECheck(lockingIntervalMs) {
+ val session = key.llFirSession
+ if (!session.isValid && shouldRetryFlag.get()) {
+ val description = session.ktModule.moduleDescription
+ throw InvalidSessionException("Session '$description' is invalid", description)
+ }
+
+ // Normally, analysis should not be allowed on an invalid session.
+ // However, there isn't an easy way to cancel or redo it in general case, as it must then be supported on use-site.
+ withRetryFlag(false, action)
+ }
}
fun withGlobalPhaseLock(
phase: FirResolvePhase,
- action: () -> Unit
+ action: () -> Unit,
) {
val lock = when (phase) {
FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE -> implicitTypesLock
@@ -77,7 +81,7 @@
inline fun withWriteLock(
target: FirElementWithResolveState,
phase: FirResolvePhase,
- action: () -> Unit
+ action: () -> Unit,
) {
withLock(target, phase, updatePhase = true, action)
}
@@ -93,7 +97,7 @@
inline fun withReadLock(
target: FirElementWithResolveState,
phase: FirResolvePhase,
- action: () -> Unit
+ action: () -> Unit,
) {
withLock(target, phase, updatePhase = false, action)
}
@@ -102,7 +106,7 @@
target: FirElementWithResolveState,
phase: FirResolvePhase,
updatePhase: Boolean,
- action: () -> Unit
+ action: () -> Unit,
) {
checker.lazyResolveToPhaseInside(phase) {
target.withCriticalSection(toPhase = phase, updatePhase = updatePhase, action = action)
@@ -112,7 +116,7 @@
inline fun withJumpingLock(
target: FirElementWithResolveState,
phase: FirResolvePhase,
- action: () -> Unit
+ action: () -> Unit,
) {
checker.lazyResolveToPhaseInside(phase, isJumpingPhase = true) {
target.withCriticalSection(toPhase = phase, updatePhase = true, action = action)
@@ -142,7 +146,7 @@
private inline fun FirElementWithResolveState.withCriticalSection(
toPhase: FirResolvePhase,
updatePhase: Boolean,
- action: () -> Unit
+ action: () -> Unit,
) {
while (true) {
checkCanceled()
@@ -183,14 +187,14 @@
}
private fun waitOnBarrier(
- stateSnapshot: FirInProcessOfResolvingToPhaseStateWithBarrier
+ stateSnapshot: FirInProcessOfResolvingToPhaseStateWithBarrier,
): Boolean {
return stateSnapshot.barrier.await(DEFAULT_LOCKING_INTERVAL, TimeUnit.MILLISECONDS)
}
private fun FirElementWithResolveState.trySettingBarrier(
toPhase: FirResolvePhase,
- stateSnapshot: FirResolveState
+ stateSnapshot: FirResolveState,
) {
val latch = CountDownLatch(1)
val newState = FirInProcessOfResolvingToPhaseStateWithBarrier(toPhase, latch)
@@ -199,7 +203,7 @@
private fun FirElementWithResolveState.tryLock(
toPhase: FirResolvePhase,
- stateSnapshot: FirResolveState
+ stateSnapshot: FirResolveState,
): Boolean {
val newState = FirInProcessOfResolvingToPhaseStateWithoutBarrier(toPhase)
return resolveStateFieldUpdater.compareAndSet(this, stateSnapshot, newState)
@@ -224,6 +228,10 @@
"resolveState"
)
+private val globalLockEnabled: Boolean by lazy(LazyThreadSafetyMode.PUBLICATION) {
+ Registry.`is`("kotlin.parallel.resolve.under.global.lock", false)
+}
+
private const val DEFAULT_LOCKING_INTERVAL = 50L
internal class InvalidSessionException(message: String, val moduleDescription: String) : RuntimeException(message)
diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/transformers/LLFirLazyResolverRunner.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/transformers/LLFirLazyResolverRunner.kt
index 6278599..0ec7dda 100644
--- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/transformers/LLFirLazyResolverRunner.kt
+++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/transformers/LLFirLazyResolverRunner.kt
@@ -22,8 +22,6 @@
val lazyResolver = LLFirLazyPhaseResolverByPhase.getByPhase(phase)
val firFile = target.firFile
val session = firFile.moduleData.session
-
- // TODO: global lock should be dropped in the context of KT-56550
lockProvider.withGlobalLock(firFile) {
lockProvider.withGlobalPhaseLock(phase) {
lazyResolver.resolve(target, lockProvider, session, scopeSession, towerDataContextCollector)