mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
PY-88517 Make pythonSdkConfigurationMutex per-project to prevent cross-project notifications
The mutex was a global singleton whose isLocked StateFlow was observed by every project's InterpreterFixExecutor. A lock state change from any project fanned out updateAllNotifications() to all projects, triggering stale cache refreshes with visible "Checking existing environments" progress bars in idle projects. Replace the global val with a per-project service so each project observes only its own mutex state. (cherry picked from commit cc14dce7d07231f6d7d4c3f654cdd8d3a2099ffc) IJ-MR-197413 GitOrigin-RevId: 65753107b7fe9e654c99f7097f1f41c0499231dc
This commit is contained in:
committed by
intellij-monorepo-bot
parent
ed92591d08
commit
e9f911fb1d
+1
-1
@@ -35,7 +35,7 @@ internal class PySdkFromEnvironmentVariableConfigurator(private val project: Pro
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun checkAndSetSdk(project: Project, pycharmPythonPathEnvVariable: String) = pythonSdkConfigurationMutex.withLock {
|
||||
private suspend fun checkAndSetSdk(project: Project, pycharmPythonPathEnvVariable: String) = project.pythonSdkConfigurationMutex.withLock {
|
||||
withContext(Dispatchers.EDT) {
|
||||
val sdk = PySdkFromEnvironmentVariable.findOrCreateSdkByPath(pycharmPythonPathEnvVariable) ?: return@withContext
|
||||
|
||||
|
||||
@@ -175,7 +175,7 @@ private suspend fun createOrOpenProjectAndSdk(
|
||||
title = PyCharmCommunityCustomizationBundle.message("misc.project.generating.env"),
|
||||
cancellation = TaskCancellation.cancellable()
|
||||
) {
|
||||
pythonSdkConfigurationMutex.withLock {
|
||||
project.pythonSdkConfigurationMutex.withLock {
|
||||
createVenvAndSdk(ModuleOrProject.ProjectOnly(project), confirmInstallation, systemPythonService, vfsProjectPath)
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -50,7 +50,7 @@ internal class PyCharmWelcomeScreenProjectProvider : WelcomeScreenProjectProvide
|
||||
if (PlatformProjectOpenProcessor.isNewProject(project)) {
|
||||
// Don't prompt to install Python on the welcome screen (PY-88204).
|
||||
// If Python is already available, the venv/SDK will be configured silently.
|
||||
pythonSdkConfigurationMutex.withLock {
|
||||
project.pythonSdkConfigurationMutex.withLock {
|
||||
createVenvAndSdk(ModuleOrProject.ProjectOnly(project), confirmInstallation = { false }).orLogException(thisLogger())
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -91,7 +91,7 @@ internal class PythonLangSupport(private val errorSink: ErrorSink = ShowingMessa
|
||||
@Throws(NoSdkException::class)
|
||||
@RequiresEdt
|
||||
override fun getSdkForProject(project: Project, selectedSdk: Sdk?): Sdk = runWithModalProgressBlocking(project, "...") {
|
||||
pythonSdkConfigurationMutex.withLock {
|
||||
project.pythonSdkConfigurationMutex.withLock {
|
||||
when (val r = createVenvAndSdk(ModuleOrProject.ProjectOnly(project))) {
|
||||
is Result.Failure -> {
|
||||
errorSink.emit(r.error, project)
|
||||
|
||||
+1
-1
@@ -105,7 +105,7 @@ private fun CreateSdkInfoWithTool.asDTO(moduleDir: Directory?): ModuleCreateInfo
|
||||
* Returns the configured SDK, or `null` if no SDK could be configured.
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
suspend fun Module.autoConfigureSdkIfNeeded(): PyResult<Sdk>? = pythonSdkConfigurationMutex.withLock {
|
||||
suspend fun Module.autoConfigureSdkIfNeeded(): PyResult<Sdk>? = project.pythonSdkConfigurationMutex.withLock {
|
||||
val moduleInfo = getModuleInfo() ?: return@withLock null
|
||||
|
||||
when (moduleInfo) {
|
||||
|
||||
@@ -138,7 +138,7 @@ internal class ModulesSdkConfigurator private constructor(
|
||||
* Errors are logged.
|
||||
*
|
||||
*/
|
||||
suspend fun configureSdks(modulesOnly: Set<ModuleName>) = pythonSdkConfigurationMutex.withLock {
|
||||
suspend fun configureSdks(modulesOnly: Set<ModuleName>) = project.pythonSdkConfigurationMutex.withLock {
|
||||
withContext(Dispatchers.Default) {
|
||||
val modulesMap = project.modules.associateBy { it.name }
|
||||
val modulesWithSameSdk = mutableMapOf<Module, Module>()
|
||||
@@ -197,7 +197,7 @@ suspend fun configureSdkAutomatically(project: Project): Unit = withContext(Disp
|
||||
when (pythonModules.size) {
|
||||
0 -> return@withContext
|
||||
1 -> pythonModules.first().autoConfigureSdkIfNeeded()?.orLogException(logger)
|
||||
else -> pythonSdkConfigurationMutex.withLock {
|
||||
else -> project.pythonSdkConfigurationMutex.withLock {
|
||||
for (module in pythonModules) {
|
||||
if (module.findPythonSdk() != null) continue
|
||||
val sdkSuggestion = module.suggestSdk()
|
||||
|
||||
@@ -1,16 +1,29 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.jetbrains.python.sdk
|
||||
|
||||
import com.intellij.openapi.components.Service
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
|
||||
/**
|
||||
* Global mutex that serializes all Python SDK configuration operations — both
|
||||
* Per-project mutex that serializes Python SDK configuration operations — both
|
||||
* auto-configuration on startup and manual SDK creation from the UI.
|
||||
*
|
||||
* This is intentionally global (not per-module) because setting an SDK on one module
|
||||
* This is per-project (not global) so that SDK configuration in one project
|
||||
* does not trigger spurious "Checking existing environments" notifications in others.
|
||||
*
|
||||
* Within a project the mutex is still global (not per-module) because setting an SDK on one module
|
||||
* can affect others via inherited project SDK in multi-module workspaces.
|
||||
*
|
||||
* Observe [isLocked][ObservableMutex.isLocked] to track whether an SDK configuration is running.
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
val pythonSdkConfigurationMutex: ObservableMutex = ObservableMutex()
|
||||
val Project.pythonSdkConfigurationMutex: ObservableMutex
|
||||
@ApiStatus.Internal
|
||||
get() = service<PythonSdkConfigurationMutexService>().mutex
|
||||
|
||||
|
||||
@Service(Service.Level.PROJECT)
|
||||
internal class PythonSdkConfigurationMutexService {
|
||||
val mutex: ObservableMutex = ObservableMutex()
|
||||
}
|
||||
|
||||
@@ -160,7 +160,7 @@ private class CacheEvictingFix(
|
||||
@ApiStatus.Internal
|
||||
@Service(Service.Level.PROJECT)
|
||||
class InterpreterFixExecutor(private val project: Project, internal val scope: CoroutineScope) : BusyGuardExecutor {
|
||||
override val isBusy: StateFlow<Boolean> = pythonSdkConfigurationMutex.isLocked
|
||||
override val isBusy: StateFlow<Boolean> = project.pythonSdkConfigurationMutex.isLocked
|
||||
|
||||
init {
|
||||
scope.launch {
|
||||
@@ -170,7 +170,7 @@ class InterpreterFixExecutor(private val project: Project, internal val scope: C
|
||||
|
||||
override fun execute(action: suspend () -> Unit) {
|
||||
scope.launch {
|
||||
pythonSdkConfigurationMutex.tryWithLock { action() }.orLogException(LOG)
|
||||
project.pythonSdkConfigurationMutex.tryWithLock { action() }.orLogException(LOG)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -129,7 +129,7 @@ class NewPythonProjectStep(parent: NewProjectWizardStep, val createPythonModuleS
|
||||
}
|
||||
|
||||
runWithModalProgressBlocking(project, PyBundle.message("python.sdk.creating.python.sdk")) {
|
||||
pythonSdkConfigurationMutex.withLock {
|
||||
project.pythonSdkConfigurationMutex.withLock {
|
||||
val (sdk, _) = pySdkCreator.getSdk(moduleOrProject).getOr {
|
||||
errorSink.emit(it.error, project)
|
||||
return@withLock
|
||||
|
||||
@@ -47,7 +47,7 @@ class PyV3BaseProjectSettings(var createGitRepository: Boolean = false) {
|
||||
}.getOr { return@coroutineScope it }
|
||||
}
|
||||
|
||||
val sdkResult = pythonSdkConfigurationMutex.withLock {
|
||||
val sdkResult = module.project.pythonSdkConfigurationMutex.withLock {
|
||||
val (sdk: Sdk, interpreterStatistics: InterpreterStatisticsInfo) = withBackgroundProgress(project, PyBundle.message("python.sdk.creating.python.sdk")) {
|
||||
getSdkAndInterpreter(module)
|
||||
}.getOr { return@withLock it }
|
||||
|
||||
@@ -44,7 +44,7 @@ class PythonAddLocalInterpreterPresenter(
|
||||
private val _sdkShared = MutableSharedFlow<Sdk>(1)
|
||||
val sdkCreatedFlow: Flow<Sdk> = _sdkShared.asSharedFlow()
|
||||
|
||||
suspend fun okClicked(addEnvironment: PythonAddEnvironment<PathHolder.Eel>): Unit = pythonSdkConfigurationMutex.withLock {
|
||||
suspend fun okClicked(addEnvironment: PythonAddEnvironment<PathHolder.Eel>): Unit = moduleOrProject.project.pythonSdkConfigurationMutex.withLock {
|
||||
when (val r = addEnvironment.getOrCreateSdkWithModal(moduleOrProject)) {
|
||||
is Result.Failure -> {
|
||||
errorSink.emit(r.error, moduleOrProject.project)
|
||||
@@ -57,4 +57,4 @@ class PythonAddLocalInterpreterPresenter(
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +63,6 @@ import com.jetbrains.python.sdk.installSdkIfNeeded
|
||||
import com.jetbrains.python.sdk.moduleIfExists
|
||||
import com.jetbrains.python.sdk.persist
|
||||
import com.jetbrains.python.sdk.pythonSdk
|
||||
import com.jetbrains.python.sdk.pythonSdkConfigurationMutex
|
||||
import com.jetbrains.python.sdk.service.PySdkService.Companion.pySdkService
|
||||
import com.jetbrains.python.sdk.setAssociationToModule
|
||||
import com.jetbrains.python.sdk.suggestAssociatedSdkName
|
||||
|
||||
@@ -117,7 +117,7 @@ class PythonLanguageRuntimeUI(
|
||||
|
||||
val sdk = runWithModalProgressBlocking(project, message("python.sdk.progress.setting.up.environment")) {
|
||||
withContext(TraceContext(message("trace.context.add.remote.python.sdk.dialog", targetSupplier.get().getTargetType().displayName))) {
|
||||
pythonSdkConfigurationMutex.withLock {
|
||||
project.pythonSdkConfigurationMutex.withLock {
|
||||
sdkManager.getOrCreateSdkWithModal(ModuleOrProject.ModuleAndProject(module)).onFailure {
|
||||
errorSink.emit(it)
|
||||
}.successOrNull?.also {
|
||||
@@ -134,4 +134,4 @@ class PythonLanguageRuntimeUI(
|
||||
}
|
||||
|
||||
override fun validate(): Collection<ValidationInfo> = validationErrors
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user