mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Eel (and partially PY-82160): Introduce API to provide a scope for process.
No process lives on an island. Process might be bound to a certain scope. It must not outlive this scope. GitOrigin-RevId: 6ad86e67890c0684bdee54d0d314bf2b6760774d
This commit is contained in:
committed by
intellij-monorepo-bot
parent
2f61fa2a7b
commit
9cf4ff7e6e
@@ -13,6 +13,7 @@ import com.intellij.openapi.util.SystemInfo
|
||||
import com.intellij.platform.eel.*
|
||||
import com.intellij.platform.eel.path.EelPath
|
||||
import com.intellij.platform.eel.provider.LocalEelDescriptor
|
||||
import com.intellij.platform.eel.provider.utils.bindToScope
|
||||
import com.intellij.util.EnvironmentUtil
|
||||
import com.pty4j.PtyProcess
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -30,10 +31,12 @@ class EelLocalExecPosixApi : EelExecPosixApi {
|
||||
generatedBuilder: EelExecApi.ExecuteProcessOptions,
|
||||
): EelPosixProcess {
|
||||
val process = executeImpl(generatedBuilder)
|
||||
return if (process is PtyProcess)
|
||||
val r = if (process is PtyProcess)
|
||||
LocalEelPosixProcess.create(process, process::setWinSize)
|
||||
else
|
||||
LocalEelPosixProcess.create(process, null)
|
||||
generatedBuilder.scope?.let { r.bindToScope(it) }
|
||||
return r
|
||||
}
|
||||
|
||||
override val descriptor: EelDescriptor = LocalEelDescriptor
|
||||
@@ -59,10 +62,12 @@ class EelLocalExecWindowsApi : EelExecWindowsApi {
|
||||
): EelWindowsProcess {
|
||||
val process = executeImpl(generatedBuilder)
|
||||
val commandLineForDebug = (listOf(generatedBuilder.exe) + generatedBuilder.args).joinToString(" ")
|
||||
return if (process is PtyProcess)
|
||||
val r = if (process is PtyProcess)
|
||||
LocalEelWindowsProcess.create(process, process::setWinSize, commandLineForDebug)
|
||||
else
|
||||
LocalEelWindowsProcess.create(process, null, commandLineForDebug)
|
||||
generatedBuilder.scope?.let { r.bindToScope(it) }
|
||||
return r
|
||||
}
|
||||
|
||||
override val descriptor: EelDescriptor = LocalEelDescriptor
|
||||
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.platform.eel.provider.utils
|
||||
|
||||
import com.intellij.openapi.diagnostic.fileLogger
|
||||
import com.intellij.platform.eel.EelProcess
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.CoroutineName
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.CoroutineStart
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.NonCancellable
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
|
||||
private val logger = fileLogger()
|
||||
|
||||
@ApiStatus.Internal
|
||||
fun EelProcess.bindToScope(scope: CoroutineScope) {
|
||||
val context = CoroutineName("Waiting for process $this") + Dispatchers.IO
|
||||
if (!scope.isActive) {
|
||||
logger.warn("Scope $scope is dead, killing process $this")
|
||||
scope.launch(context, start = CoroutineStart.UNDISPATCHED) {
|
||||
killAndJoin()
|
||||
}
|
||||
}
|
||||
|
||||
scope.launch(context, start = CoroutineStart.UNDISPATCHED) {
|
||||
try {
|
||||
exitCode.await()
|
||||
}
|
||||
catch (e: CancellationException) {
|
||||
killAndJoin()
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun EelProcess.killAndJoin() {
|
||||
withContext(NonCancellable) {
|
||||
logger.warn("Sending kill to $this")
|
||||
kill()
|
||||
logger.warn("Waiting $this to exit")
|
||||
exitCode.await()
|
||||
logger.warn("Process $this died")
|
||||
}
|
||||
}
|
||||
@@ -53,6 +53,7 @@
|
||||
- *:getEnv():java.util.Map
|
||||
- *a:getExe():java.lang.String
|
||||
- *:getInteractionOptions():com.intellij.platform.eel.EelExecApi$InteractionOptions
|
||||
- getScope():kotlinx.coroutines.CoroutineScope
|
||||
- *:getWorkingDirectory():com.intellij.platform.eel.path.EelPath
|
||||
*:com.intellij.platform.eel.EelExecApi$InteractionOptions
|
||||
*f:com.intellij.platform.eel.EelExecApi$Pty
|
||||
@@ -75,6 +76,7 @@
|
||||
- *f:env(java.util.Map):com.intellij.platform.eel.EelExecApiHelpers$SpawnProcess
|
||||
- *f:exe(java.lang.String):com.intellij.platform.eel.EelExecApiHelpers$SpawnProcess
|
||||
- *f:interactionOptions(com.intellij.platform.eel.EelExecApi$InteractionOptions):com.intellij.platform.eel.EelExecApiHelpers$SpawnProcess
|
||||
- f:scope(kotlinx.coroutines.CoroutineScope):com.intellij.platform.eel.EelExecApiHelpers$SpawnProcess
|
||||
- *f:workingDirectory(com.intellij.platform.eel.path.EelPath):com.intellij.platform.eel.EelExecApiHelpers$SpawnProcess
|
||||
f:com.intellij.platform.eel.EelExecApiHelpersKt
|
||||
- *sf:spawnProcess(com.intellij.platform.eel.EelExecApi,java.lang.String):com.intellij.platform.eel.EelExecApiHelpers$SpawnProcess
|
||||
@@ -96,6 +98,7 @@ f:com.intellij.platform.eel.EelExecApiKt
|
||||
- *f:env(java.util.Map):com.intellij.platform.eel.EelExecPosixApiHelpers$SpawnProcess
|
||||
- *f:exe(java.lang.String):com.intellij.platform.eel.EelExecPosixApiHelpers$SpawnProcess
|
||||
- *f:interactionOptions(com.intellij.platform.eel.EelExecApi$InteractionOptions):com.intellij.platform.eel.EelExecPosixApiHelpers$SpawnProcess
|
||||
- f:scope(kotlinx.coroutines.CoroutineScope):com.intellij.platform.eel.EelExecPosixApiHelpers$SpawnProcess
|
||||
- *f:workingDirectory(com.intellij.platform.eel.path.EelPath):com.intellij.platform.eel.EelExecPosixApiHelpers$SpawnProcess
|
||||
f:com.intellij.platform.eel.EelExecPosixApiHelpersKt
|
||||
- *sf:spawnProcess(com.intellij.platform.eel.EelExecPosixApi,java.lang.String):com.intellij.platform.eel.EelExecPosixApiHelpers$SpawnProcess
|
||||
@@ -112,6 +115,7 @@ f:com.intellij.platform.eel.EelExecPosixApiHelpersKt
|
||||
- *f:env(java.util.Map):com.intellij.platform.eel.EelExecWindowsApiHelpers$SpawnProcess
|
||||
- *f:exe(java.lang.String):com.intellij.platform.eel.EelExecWindowsApiHelpers$SpawnProcess
|
||||
- *f:interactionOptions(com.intellij.platform.eel.EelExecApi$InteractionOptions):com.intellij.platform.eel.EelExecWindowsApiHelpers$SpawnProcess
|
||||
- f:scope(kotlinx.coroutines.CoroutineScope):com.intellij.platform.eel.EelExecWindowsApiHelpers$SpawnProcess
|
||||
- *f:workingDirectory(com.intellij.platform.eel.path.EelPath):com.intellij.platform.eel.EelExecWindowsApiHelpers$SpawnProcess
|
||||
f:com.intellij.platform.eel.EelExecWindowsApiHelpersKt
|
||||
- *sf:spawnProcess(com.intellij.platform.eel.EelExecWindowsApi,java.lang.String):com.intellij.platform.eel.EelExecWindowsApiHelpers$SpawnProcess
|
||||
@@ -338,6 +342,7 @@ f:com.intellij.platform.eel.EelTunnelsApiKt
|
||||
- *f:env(java.util.Map):com.intellij.platform.eel.ExecuteProcessOptionsBuilder
|
||||
- *f:exe(java.lang.String):com.intellij.platform.eel.ExecuteProcessOptionsBuilder
|
||||
- *f:interactionOptions(com.intellij.platform.eel.EelExecApi$InteractionOptions):com.intellij.platform.eel.ExecuteProcessOptionsBuilder
|
||||
- f:scope(kotlinx.coroutines.CoroutineScope):com.intellij.platform.eel.ExecuteProcessOptionsBuilder
|
||||
- *f:workingDirectory(com.intellij.platform.eel.path.EelPath):com.intellij.platform.eel.ExecuteProcessOptionsBuilder
|
||||
*f:com.intellij.platform.eel.GetConnectionToRemotePortArgsBuilder
|
||||
- <init>():V
|
||||
|
||||
@@ -9,6 +9,7 @@ import com.intellij.platform.eel.EelExecApi.ExecuteProcessOptions
|
||||
import com.intellij.platform.eel.EelExecApi.InteractionOptions
|
||||
import com.intellij.platform.eel.EelExecApi.PtyOrStdErrSettings
|
||||
import com.intellij.platform.eel.path.EelPath
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import org.jetbrains.annotations.CheckReturnValue
|
||||
|
||||
@@ -76,6 +77,8 @@ object EelExecApiHelpers {
|
||||
|
||||
private var ptyOrStdErrSettings: PtyOrStdErrSettings? = interactionOptions
|
||||
|
||||
private var scope: CoroutineScope? = null
|
||||
|
||||
private var workingDirectory: EelPath? = null
|
||||
|
||||
@ApiStatus.Experimental
|
||||
@@ -127,6 +130,13 @@ object EelExecApiHelpers {
|
||||
this.ptyOrStdErrSettings = arg
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope this process is bound to. Once scope dies -- this process dies as well.
|
||||
*/
|
||||
fun scope(arg: CoroutineScope?): Execute = apply {
|
||||
this.scope = arg
|
||||
}
|
||||
|
||||
/**
|
||||
* All argument, all paths, should be valid for the remote machine. F.i., if the IDE runs on Windows, but IJent runs on Linux,
|
||||
* [ExecuteProcessOptions.workingDirectory] is the path on the Linux host. There's no automatic path mapping in this interface.
|
||||
@@ -149,6 +159,7 @@ object EelExecApiHelpers {
|
||||
exe = exe,
|
||||
interactionOptions = interactionOptions,
|
||||
ptyOrStdErrSettings = ptyOrStdErrSettings,
|
||||
scope = scope,
|
||||
workingDirectory = workingDirectory,
|
||||
)
|
||||
)
|
||||
@@ -171,6 +182,8 @@ object EelExecApiHelpers {
|
||||
|
||||
private var ptyOrStdErrSettings: PtyOrStdErrSettings? = interactionOptions
|
||||
|
||||
private var scope: CoroutineScope? = null
|
||||
|
||||
private var workingDirectory: EelPath? = null
|
||||
|
||||
@ApiStatus.Experimental
|
||||
@@ -222,6 +235,13 @@ object EelExecApiHelpers {
|
||||
this.ptyOrStdErrSettings = arg
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope this process is bound to. Once scope dies -- this process dies as well.
|
||||
*/
|
||||
fun scope(arg: CoroutineScope?): SpawnProcess = apply {
|
||||
this.scope = arg
|
||||
}
|
||||
|
||||
/**
|
||||
* All argument, all paths, should be valid for the remote machine. F.i., if the IDE runs on Windows, but IJent runs on Linux,
|
||||
* [ExecuteProcessOptions.workingDirectory] is the path on the Linux host. There's no automatic path mapping in this interface.
|
||||
@@ -245,6 +265,7 @@ object EelExecApiHelpers {
|
||||
exe = exe,
|
||||
interactionOptions = interactionOptions,
|
||||
ptyOrStdErrSettings = ptyOrStdErrSettings,
|
||||
scope = scope,
|
||||
workingDirectory = workingDirectory,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -9,6 +9,7 @@ import com.intellij.platform.eel.EelExecApi.ExecuteProcessOptions
|
||||
import com.intellij.platform.eel.EelExecApi.InteractionOptions
|
||||
import com.intellij.platform.eel.EelExecApi.PtyOrStdErrSettings
|
||||
import com.intellij.platform.eel.path.EelPath
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
|
||||
|
||||
@@ -48,6 +49,8 @@ object EelExecPosixApiHelpers {
|
||||
|
||||
private var ptyOrStdErrSettings: PtyOrStdErrSettings? = interactionOptions
|
||||
|
||||
private var scope: CoroutineScope? = null
|
||||
|
||||
private var workingDirectory: EelPath? = null
|
||||
|
||||
@ApiStatus.Experimental
|
||||
@@ -99,6 +102,13 @@ object EelExecPosixApiHelpers {
|
||||
this.ptyOrStdErrSettings = arg
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope this process is bound to. Once scope dies -- this process dies as well.
|
||||
*/
|
||||
fun scope(arg: CoroutineScope?): SpawnProcess = apply {
|
||||
this.scope = arg
|
||||
}
|
||||
|
||||
/**
|
||||
* All argument, all paths, should be valid for the remote machine. F.i., if the IDE runs on Windows, but IJent runs on Linux,
|
||||
* [ExecuteProcessOptions.workingDirectory] is the path on the Linux host. There's no automatic path mapping in this interface.
|
||||
@@ -121,6 +131,7 @@ object EelExecPosixApiHelpers {
|
||||
exe = exe,
|
||||
interactionOptions = interactionOptions,
|
||||
ptyOrStdErrSettings = ptyOrStdErrSettings,
|
||||
scope = scope,
|
||||
workingDirectory = workingDirectory,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -9,6 +9,7 @@ import com.intellij.platform.eel.EelExecApi.ExecuteProcessOptions
|
||||
import com.intellij.platform.eel.EelExecApi.InteractionOptions
|
||||
import com.intellij.platform.eel.EelExecApi.PtyOrStdErrSettings
|
||||
import com.intellij.platform.eel.path.EelPath
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
|
||||
|
||||
@@ -48,6 +49,8 @@ object EelExecWindowsApiHelpers {
|
||||
|
||||
private var ptyOrStdErrSettings: PtyOrStdErrSettings? = interactionOptions
|
||||
|
||||
private var scope: CoroutineScope? = null
|
||||
|
||||
private var workingDirectory: EelPath? = null
|
||||
|
||||
@ApiStatus.Experimental
|
||||
@@ -99,6 +102,13 @@ object EelExecWindowsApiHelpers {
|
||||
this.ptyOrStdErrSettings = arg
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope this process is bound to. Once scope dies -- this process dies as well.
|
||||
*/
|
||||
fun scope(arg: CoroutineScope?): SpawnProcess = apply {
|
||||
this.scope = arg
|
||||
}
|
||||
|
||||
/**
|
||||
* All argument, all paths, should be valid for the remote machine. F.i., if the IDE runs on Windows, but IJent runs on Linux,
|
||||
* [ExecuteProcessOptions.workingDirectory] is the path on the Linux host. There's no automatic path mapping in this interface.
|
||||
@@ -121,6 +131,7 @@ object EelExecWindowsApiHelpers {
|
||||
exe = exe,
|
||||
interactionOptions = interactionOptions,
|
||||
ptyOrStdErrSettings = ptyOrStdErrSettings,
|
||||
scope = scope,
|
||||
workingDirectory = workingDirectory,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -8,6 +8,7 @@ import com.intellij.platform.eel.EelExecApi.ExecuteProcessOptions
|
||||
import com.intellij.platform.eel.EelExecApi.InteractionOptions
|
||||
import com.intellij.platform.eel.EelExecApi.PtyOrStdErrSettings
|
||||
import com.intellij.platform.eel.path.EelPath
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
|
||||
|
||||
@@ -31,6 +32,8 @@ class ExecuteProcessOptionsBuilder(
|
||||
|
||||
private var ptyOrStdErrSettings: PtyOrStdErrSettings? = interactionOptions
|
||||
|
||||
private var scope: CoroutineScope? = null
|
||||
|
||||
private var workingDirectory: EelPath? = null
|
||||
|
||||
@ApiStatus.Experimental
|
||||
@@ -82,6 +85,13 @@ class ExecuteProcessOptionsBuilder(
|
||||
this.ptyOrStdErrSettings = arg
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope this process is bound to. Once scope dies -- this process dies as well.
|
||||
*/
|
||||
fun scope(arg: CoroutineScope?): ExecuteProcessOptionsBuilder = apply {
|
||||
this.scope = arg
|
||||
}
|
||||
|
||||
/**
|
||||
* All argument, all paths, should be valid for the remote machine. F.i., if the IDE runs on Windows, but IJent runs on Linux,
|
||||
* [ExecuteProcessOptions.workingDirectory] is the path on the Linux host. There's no automatic path mapping in this interface.
|
||||
@@ -98,6 +108,7 @@ class ExecuteProcessOptionsBuilder(
|
||||
exe = exe,
|
||||
interactionOptions = interactionOptions,
|
||||
ptyOrStdErrSettings = ptyOrStdErrSettings,
|
||||
scope = scope,
|
||||
workingDirectory = workingDirectory,
|
||||
)
|
||||
}
|
||||
@@ -109,5 +120,6 @@ internal class ExecuteProcessOptionsImpl(
|
||||
override val exe: String,
|
||||
override val interactionOptions: InteractionOptions?,
|
||||
override val ptyOrStdErrSettings: PtyOrStdErrSettings?,
|
||||
override val scope: CoroutineScope?,
|
||||
override val workingDirectory: EelPath?,
|
||||
) : ExecuteProcessOptions
|
||||
@@ -5,6 +5,7 @@ import com.intellij.platform.eel.EelExecApi.ExecuteProcessOptions
|
||||
import com.intellij.platform.eel.channels.EelReceiveChannel
|
||||
import com.intellij.platform.eel.channels.EelSendChannel
|
||||
import com.intellij.platform.eel.path.EelPath
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import org.jetbrains.annotations.CheckReturnValue
|
||||
|
||||
@@ -50,6 +51,11 @@ sealed interface EelExecApi {
|
||||
@get:ApiStatus.Experimental
|
||||
val args: List<String> get() = listOf()
|
||||
|
||||
/**
|
||||
* Scope this process is bound to. Once scope dies -- this process dies as well.
|
||||
*/
|
||||
val scope: CoroutineScope? get() = null
|
||||
|
||||
/**
|
||||
* By default, environment is always inherited, which may be unwanted. [ExecuteProcessOptions.env] allows
|
||||
* to alter some environment variables, it doesn't clear the variables from the parent. When the process should be started in an
|
||||
|
||||
Reference in New Issue
Block a user