[LL FIR] allow parallel resolve for non-jumping phases

^KT-56550 Fixed
This commit is contained in:
Dmitrii Gridin
2023-04-25 15:06:36 +02:00
committed by Space Team
parent 7f778eafa5
commit aa5261395e
2 changed files with 29 additions and 23 deletions
@@ -6,7 +6,9 @@
package org.jetbrains.kotlin.analysis.low.level.api.fir.file.builder package org.jetbrains.kotlin.analysis.low.level.api.fir.file.builder
import com.intellij.openapi.diagnostic.Logger 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.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.checkCanceled
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.lockWithPCECheck import org.jetbrains.kotlin.analysis.low.level.api.fir.util.lockWithPCECheck
import org.jetbrains.kotlin.fir.FirElementWithResolveState import org.jetbrains.kotlin.fir.FirElementWithResolveState
@@ -18,8 +20,6 @@ import java.util.concurrent.locks.ReentrantLock
import kotlin.contracts.ExperimentalContracts import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind import kotlin.contracts.InvocationKind
import kotlin.contracts.contract 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. * Keyed locks provider.
@@ -34,22 +34,26 @@ internal class LLFirLockProvider(private val checker: LLFirLazyResolveContractCh
inline fun <R> withGlobalLock( inline fun <R> withGlobalLock(
key: FirFile, key: FirFile,
lockingIntervalMs: Long = DEFAULT_LOCKING_INTERVAL, lockingIntervalMs: Long = DEFAULT_LOCKING_INTERVAL,
action: () -> R action: () -> R,
): R = globalLock.lockWithPCECheck(lockingIntervalMs) { ): R {
val session = key.llFirSession if (!globalLockEnabled) return action()
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. return globalLock.lockWithPCECheck(lockingIntervalMs) {
// However, there isn't an easy way to cancel or redo it in general case, as it must then be supported on use-site. val session = key.llFirSession
withRetryFlag(false, action) 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( fun withGlobalPhaseLock(
phase: FirResolvePhase, phase: FirResolvePhase,
action: () -> Unit action: () -> Unit,
) { ) {
val lock = when (phase) { val lock = when (phase) {
FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE -> implicitTypesLock FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE -> implicitTypesLock
@@ -77,7 +81,7 @@ internal class LLFirLockProvider(private val checker: LLFirLazyResolveContractCh
inline fun withWriteLock( inline fun withWriteLock(
target: FirElementWithResolveState, target: FirElementWithResolveState,
phase: FirResolvePhase, phase: FirResolvePhase,
action: () -> Unit action: () -> Unit,
) { ) {
withLock(target, phase, updatePhase = true, action) withLock(target, phase, updatePhase = true, action)
} }
@@ -93,7 +97,7 @@ internal class LLFirLockProvider(private val checker: LLFirLazyResolveContractCh
inline fun withReadLock( inline fun withReadLock(
target: FirElementWithResolveState, target: FirElementWithResolveState,
phase: FirResolvePhase, phase: FirResolvePhase,
action: () -> Unit action: () -> Unit,
) { ) {
withLock(target, phase, updatePhase = false, action) withLock(target, phase, updatePhase = false, action)
} }
@@ -102,7 +106,7 @@ internal class LLFirLockProvider(private val checker: LLFirLazyResolveContractCh
target: FirElementWithResolveState, target: FirElementWithResolveState,
phase: FirResolvePhase, phase: FirResolvePhase,
updatePhase: Boolean, updatePhase: Boolean,
action: () -> Unit action: () -> Unit,
) { ) {
checker.lazyResolveToPhaseInside(phase) { checker.lazyResolveToPhaseInside(phase) {
target.withCriticalSection(toPhase = phase, updatePhase = updatePhase, action = action) target.withCriticalSection(toPhase = phase, updatePhase = updatePhase, action = action)
@@ -112,7 +116,7 @@ internal class LLFirLockProvider(private val checker: LLFirLazyResolveContractCh
inline fun withJumpingLock( inline fun withJumpingLock(
target: FirElementWithResolveState, target: FirElementWithResolveState,
phase: FirResolvePhase, phase: FirResolvePhase,
action: () -> Unit action: () -> Unit,
) { ) {
checker.lazyResolveToPhaseInside(phase, isJumpingPhase = true) { checker.lazyResolveToPhaseInside(phase, isJumpingPhase = true) {
target.withCriticalSection(toPhase = phase, updatePhase = true, action = action) target.withCriticalSection(toPhase = phase, updatePhase = true, action = action)
@@ -142,7 +146,7 @@ internal class LLFirLockProvider(private val checker: LLFirLazyResolveContractCh
private inline fun FirElementWithResolveState.withCriticalSection( private inline fun FirElementWithResolveState.withCriticalSection(
toPhase: FirResolvePhase, toPhase: FirResolvePhase,
updatePhase: Boolean, updatePhase: Boolean,
action: () -> Unit action: () -> Unit,
) { ) {
while (true) { while (true) {
checkCanceled() checkCanceled()
@@ -183,14 +187,14 @@ internal class LLFirLockProvider(private val checker: LLFirLazyResolveContractCh
} }
private fun waitOnBarrier( private fun waitOnBarrier(
stateSnapshot: FirInProcessOfResolvingToPhaseStateWithBarrier stateSnapshot: FirInProcessOfResolvingToPhaseStateWithBarrier,
): Boolean { ): Boolean {
return stateSnapshot.barrier.await(DEFAULT_LOCKING_INTERVAL, TimeUnit.MILLISECONDS) return stateSnapshot.barrier.await(DEFAULT_LOCKING_INTERVAL, TimeUnit.MILLISECONDS)
} }
private fun FirElementWithResolveState.trySettingBarrier( private fun FirElementWithResolveState.trySettingBarrier(
toPhase: FirResolvePhase, toPhase: FirResolvePhase,
stateSnapshot: FirResolveState stateSnapshot: FirResolveState,
) { ) {
val latch = CountDownLatch(1) val latch = CountDownLatch(1)
val newState = FirInProcessOfResolvingToPhaseStateWithBarrier(toPhase, latch) val newState = FirInProcessOfResolvingToPhaseStateWithBarrier(toPhase, latch)
@@ -199,7 +203,7 @@ internal class LLFirLockProvider(private val checker: LLFirLazyResolveContractCh
private fun FirElementWithResolveState.tryLock( private fun FirElementWithResolveState.tryLock(
toPhase: FirResolvePhase, toPhase: FirResolvePhase,
stateSnapshot: FirResolveState stateSnapshot: FirResolveState,
): Boolean { ): Boolean {
val newState = FirInProcessOfResolvingToPhaseStateWithoutBarrier(toPhase) val newState = FirInProcessOfResolvingToPhaseStateWithoutBarrier(toPhase)
return resolveStateFieldUpdater.compareAndSet(this, stateSnapshot, newState) return resolveStateFieldUpdater.compareAndSet(this, stateSnapshot, newState)
@@ -224,6 +228,10 @@ private val resolveStateFieldUpdater = AtomicReferenceFieldUpdater.newUpdater(
"resolveState" "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 private const val DEFAULT_LOCKING_INTERVAL = 50L
internal class InvalidSessionException(message: String, val moduleDescription: String) : RuntimeException(message) internal class InvalidSessionException(message: String, val moduleDescription: String) : RuntimeException(message)
@@ -22,8 +22,6 @@ internal object LLFirLazyResolverRunner {
val lazyResolver = LLFirLazyPhaseResolverByPhase.getByPhase(phase) val lazyResolver = LLFirLazyPhaseResolverByPhase.getByPhase(phase)
val firFile = target.firFile val firFile = target.firFile
val session = firFile.moduleData.session val session = firFile.moduleData.session
// TODO: global lock should be dropped in the context of KT-56550
lockProvider.withGlobalLock(firFile) { lockProvider.withGlobalLock(firFile) {
lockProvider.withGlobalPhaseLock(phase) { lockProvider.withGlobalPhaseLock(phase) {
lazyResolver.resolve(target, lockProvider, session, scopeSession, towerDataContextCollector) lazyResolver.resolve(target, lockProvider, session, scopeSession, towerDataContextCollector)