PY-76036 PY-75988 PY-75990 PY-76065: Fix various threading issues after the new platform threading policy

All "implicit" locks are removed from the platform, so we need to call read/write action explicitly (which is a right thing to do in any case).

GitOrigin-RevId: 290788bc78e39ca42f7d0f14ae4ccd16dd315ce7
This commit is contained in:
Ilya.Kazakevich
2024-09-20 23:02:57 +02:00
committed by intellij-monorepo-bot
parent 888e3be3de
commit 0438f8093b
7 changed files with 53 additions and 19 deletions

View File

@@ -46,8 +46,10 @@ import java.util.Map;
* Needs not to be instantiated and only holds static methods.
*
* @see PythonSdkUtil for Pyhton SDK utilities with no run-time dependencies
*
* @deprecated please use Kotlin coroutines to run processes in background
*/
//TODO: rename to PySdkExecuteUtil or PySdkRuntimeUtil
@Deprecated
public final class PySdkUtil {
private static final Logger LOG = Logger.getInstance(PySdkUtil.class);

View File

@@ -334,6 +334,7 @@ public abstract class PythonSdkFlavor<D extends PyFlavorData> {
}
@Nullable
@RequiresBackgroundThread(generateAssertion = false) //because of process output
public String getVersionString(@Nullable String sdkHome) {
if (sdkHome == null) {
return null;
@@ -394,6 +395,7 @@ public abstract class PythonSdkFlavor<D extends PyFlavorData> {
}
@NotNull
@RequiresBackgroundThread(generateAssertion = false) //because of process output
public LanguageLevel getLanguageLevel(@NotNull String sdkHome) {
return getLanguageLevelFromVersionString(getVersionString(sdkHome));
}

View File

@@ -5,9 +5,9 @@ import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.EDT
import com.intellij.openapi.application.ModalityState
import com.intellij.openapi.application.asContextElement
import com.intellij.openapi.application.writeIntentReadAction
import com.intellij.openapi.components.Service
import com.intellij.openapi.components.service
import com.intellij.util.concurrency.ThreadingAssertions
import com.intellij.util.concurrency.annotations.RequiresBlockingContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -25,6 +25,11 @@ private class MyService(val coroutineScope: CoroutineScope)
@RequiresBlockingContext
internal fun <T : Any> Flow<T>.oneShotConsumer(consumer: Consumer<T>) {
ApplicationManager.getApplication().service<MyService>().coroutineScope.launch(Dispatchers.EDT + ModalityState.defaultModalityState().asContextElement()) {
consumer.accept(this@oneShotConsumer.first())
// Platform doesn't guarantee write intent lock on EDT
//todo fix all clients and remove global lock from here
val t = this@oneShotConsumer.first()
writeIntentReadAction {
consumer.accept(t)
}
}
}

View File

@@ -17,11 +17,7 @@ package com.jetbrains.python.sdk
import com.intellij.execution.ExecutionException
import com.intellij.execution.target.*
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.PathManager
import com.intellij.openapi.application.WriteAction
import com.intellij.openapi.application.invokeAndWaitIfNeeded
import com.intellij.openapi.application.runInEdt
import com.intellij.openapi.application.*
import com.intellij.openapi.diagnostic.getOrLogException
import com.intellij.openapi.diagnostic.thisLogger
import com.intellij.openapi.module.Module
@@ -43,7 +39,10 @@ import com.intellij.openapi.vfs.LocalFileSystem
import com.intellij.openapi.vfs.StandardFileSystems
import com.intellij.openapi.vfs.VfsUtil
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.platform.ide.progress.ModalTaskOwner
import com.intellij.platform.ide.progress.runWithModalProgressBlocking
import com.intellij.util.PathUtil
import com.intellij.util.concurrency.annotations.RequiresBackgroundThread
import com.intellij.webcore.packaging.PackagesNotificationPanel
import com.jetbrains.extensions.failure
import com.jetbrains.python.PyBundle
@@ -59,12 +58,16 @@ import com.jetbrains.python.sdk.flavors.VirtualEnvSdkFlavor
import com.jetbrains.python.sdk.flavors.conda.CondaEnvSdkFlavor
import com.jetbrains.python.target.PyTargetAwareAdditionalData
import com.jetbrains.python.ui.PyUiUtil
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.jetbrains.annotations.ApiStatus
import java.io.File
import java.io.IOException
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
import javax.swing.SwingUtilities
import kotlin.Result
import kotlin.io.path.div
import kotlin.io.path.pathString
@@ -187,13 +190,22 @@ fun createSdkByGenerateTask(
throw e
}
val suggestedName = suggestedSdkName ?: suggestAssociatedSdkName(homeFile.path, associatedProjectPath)
val sdkName = suggestedSdkName ?: if (SwingUtilities.isEventDispatchThread()) {
runWithModalProgressBlocking(ModalTaskOwner.guess(), "...") {
withContext(Dispatchers.IO) {
suggestAssociatedSdkName(homeFile.path, associatedProjectPath)
}
}
}
else {
suggestAssociatedSdkName(homeFile.path, associatedProjectPath)
}
return SdkConfigurationUtil.setupSdk(
existingSdks.toTypedArray(),
homeFile,
PythonSdkType.getInstance(),
null,
suggestedName)
sdkName)
}
fun showSdkExecutionException(sdk: Sdk?, e: ExecutionException, @NlsContexts.DialogTitle title: String) {
@@ -378,6 +390,7 @@ fun getInnerVirtualEnvRoot(sdk: Sdk): VirtualFile? {
}
}
@RequiresBackgroundThread
internal fun suggestAssociatedSdkName(sdkHome: String, associatedPath: String?): String? {
// please don't forget to update com.jetbrains.python.inspections.PyInterpreterInspection.Visitor#getSuitableSdkFix
// after changing this method

View File

@@ -33,6 +33,7 @@ import com.intellij.remote.ext.LanguageCaseCollector;
import com.intellij.util.Consumer;
import com.intellij.util.ExceptionUtil;
import com.intellij.util.PlatformUtils;
import com.intellij.util.concurrency.annotations.RequiresBackgroundThread;
import com.intellij.util.containers.ContainerUtil;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.PyNames;
@@ -244,7 +245,7 @@ public final class PythonSdkType extends SdkType {
return name;
}
}
@RequiresBackgroundThread(generateAssertion = false) //because of process output
public static @Nullable String suggestBaseSdkName(@NotNull String sdkHome) {
final PythonSdkFlavor flavor = PythonSdkFlavor.getFlavor(sdkHome);
if (flavor == null) return null;

View File

@@ -3,12 +3,13 @@ package com.jetbrains.python.sdk.add.v2
import com.intellij.execution.ExecutionException
import com.intellij.openapi.application.EDT
import com.intellij.openapi.application.writeAction
import com.intellij.openapi.module.ModuleUtil
import com.intellij.openapi.project.ProjectManager
import com.intellij.openapi.projectRoots.ProjectJdkTable
import com.intellij.openapi.projectRoots.Sdk
import com.intellij.openapi.projectRoots.impl.SdkConfigurationUtil
import com.intellij.openapi.vfs.StandardFileSystems
import com.intellij.openapi.vfs.VfsUtil
import com.intellij.platform.ide.progress.ModalTaskOwner
import com.intellij.platform.ide.progress.TaskCancellation
import com.intellij.platform.ide.progress.withModalProgress
@@ -53,13 +54,16 @@ suspend fun PythonMutableTargetAddInterpreterModel.setupVirtualenv(venvPath: Pat
catch (e: InvalidPathException) {
return Result.failure(e)
}
val venvPython = VirtualEnvReader.Instance.findPythonInPythonRoot(dir)?.toString()
val venvPython = VirtualEnvReader.Instance.findPythonInPythonRoot(dir)
if (venvPython == null) {
return failure(message("commandLine.directoryCantBeAccessed", venvPathOnTarget))
}
val homeFile = try {
StandardFileSystems.local().refreshAndFindFileByPath(venvPython)
// refresh needs write action
writeAction {
VfsUtil.findFile(venvPython, true)
}
}
catch (e: ExecutionException) {
return Result.failure(e)
@@ -68,10 +72,13 @@ suspend fun PythonMutableTargetAddInterpreterModel.setupVirtualenv(venvPath: Pat
return failure(message("commandLine.directoryCantBeAccessed", venvPathOnTarget))
}
val suggestedName = /*suggestedSdkName ?:*/ suggestAssociatedSdkName(homeFile.path, projectPath.toString())
val newSdk = SdkConfigurationUtil.setupSdk(existingSdks.toTypedArray(), homeFile,
PythonSdkType.getInstance(),
false, null, suggestedName)!!
// "suggest name" calls external process and can't be called from EDT
val newSdk = withContext(Dispatchers.IO) {
val suggestedName = /*suggestedSdkName ?:*/ suggestAssociatedSdkName(homeFile.path, projectPath.toString())
SdkConfigurationUtil.setupSdk(existingSdks.toTypedArray(), homeFile,
PythonSdkType.getInstance(),
false, null, suggestedName)!!
}
addSdk(newSdk)

View File

@@ -4,6 +4,7 @@ package com.jetbrains.python.util
import com.intellij.openapi.application.EDT
import com.intellij.openapi.application.ModalityState
import com.intellij.openapi.application.asContextElement
import com.intellij.openapi.application.writeIntentReadAction
import com.intellij.openapi.diagnostic.thisLogger
import com.intellij.openapi.ui.Messages
import com.intellij.openapi.util.NlsSafe
@@ -56,7 +57,10 @@ object ShowingMessageErrorSync : ErrorSink {
override suspend fun emit(value: @NlsSafe String) {
withContext(Dispatchers.EDT + ModalityState.any().asContextElement()) {
thisLogger().warn(value)
Messages.showErrorDialog(value, PyBundle.message("python.error"))
// Platform doesn't allow dialogs without lock for now, fix later
writeIntentReadAction {
Messages.showErrorDialog(value, PyBundle.message("python.error"))
}
}
}
}