|
private ProcessExitStatus WaitForExitCore() |
|
{ |
|
switch (Interop.Sys.WaitForExitAndReap(this, ProcessId, out int exitCode, out int rawSignal)) |
|
{ |
|
case -1: |
|
throw new Win32Exception(); |
|
default: |
|
ProcessExitStatus status = new(exitCode, false, rawSignal != 0 ? (PosixSignal)rawSignal : null); |
|
OnProcessExited(); |
|
return status; |
|
} |
|
} |
|
|
|
private bool TryWaitForExitCore(int milliseconds, [NotNullWhen(true)] out ProcessExitStatus? exitStatus) |
|
{ |
|
switch (Interop.Sys.TryWaitForExit(this, ProcessId, milliseconds, out int exitCode, out int rawSignal)) |
|
{ |
|
case -1: |
|
throw new Win32Exception(); |
|
case 1: // timeout |
|
exitStatus = null; |
|
return false; |
|
default: |
|
exitStatus = new(exitCode, false, rawSignal != 0 ? (PosixSignal)rawSignal : null); |
|
OnProcessExited(); |
|
return true; |
|
} |
|
} |
|
|
|
private static int GetProcessIdCore() => throw new PlatformNotSupportedException(); |
|
|
|
private ProcessExitStatus WaitForExitOrKillOnTimeoutCore(int milliseconds) |
|
{ |
|
switch (Interop.Sys.WaitForExitOrKillOnTimeout(this, ProcessId, _isGroupLeader, milliseconds, out int exitCode, out int rawSignal, out int hasTimedout)) |
|
{ |
|
case -1: |
|
throw new Win32Exception(); |
|
default: |
|
ProcessExitStatus status = new(exitCode, hasTimedout == 1, rawSignal != 0 ? (PosixSignal)rawSignal : null); |
|
OnProcessExited(); |
|
return status; |
|
} |
|
} |
|
|
|
private async Task<ProcessExitStatus> WaitForExitAsyncCore(CancellationToken cancellationToken) |
|
{ |
|
if (!cancellationToken.CanBeCanceled) |
|
{ |
|
return await Task.Run(WaitForExitCore, cancellationToken).ConfigureAwait(false); |
|
} |
|
|
|
CreatePipe(out SafeFileHandle readHandle, out SafeFileHandle writeHandle); |
|
|
|
using (readHandle) |
|
using (writeHandle) |
|
{ |
|
using CancellationTokenRegistration registration = cancellationToken.Register(static state => |
|
{ |
|
((SafeFileHandle)state!).Close(); // Close the write end of the pipe to signal cancellation |
|
}, writeHandle); |
|
|
|
return await Task.Run(() => |
|
{ |
|
switch (Interop.Sys.TryWaitForExitCancellable(this, ProcessId, (int)readHandle.DangerousGetHandle(), out int exitCode, out int rawSignal)) |
|
{ |
|
case -1: |
|
throw new Win32Exception(); |
|
case 1: // canceled |
|
throw new OperationCanceledException(cancellationToken); |
|
default: |
|
ProcessExitStatus status = new(exitCode, false, rawSignal != 0 ? (PosixSignal)rawSignal : null); |
|
OnProcessExited(); |
|
return status; |
|
} |
|
}, cancellationToken).ConfigureAwait(false); |
|
} |
|
} |
|
|
|
private async Task<ProcessExitStatus> WaitForExitOrKillOnCancellationAsyncCore(CancellationToken cancellationToken) |
|
{ |
|
if (!cancellationToken.CanBeCanceled) |
|
{ |
|
return await Task.Run(WaitForExitCore, cancellationToken).ConfigureAwait(false); |
|
} |
|
|
|
CreatePipe(out SafeFileHandle readHandle, out SafeFileHandle writeHandle); |
|
|
|
using (readHandle) |
|
using (writeHandle) |
|
{ |
|
using CancellationTokenRegistration registration = cancellationToken.Register(static state => |
|
{ |
|
((SafeFileHandle)state!).Close(); // Close the write end of the pipe to signal cancellation |
|
}, writeHandle); |
|
|
|
return await Task.Run(() => |
|
{ |
|
switch (Interop.Sys.TryWaitForExitCancellable(this, ProcessId, (int)readHandle.DangerousGetHandle(), out int exitCode, out int rawSignal)) |
|
{ |
|
case -1: |
|
throw new Win32Exception(); |
|
case 1: // canceled |
|
bool wasKilled = KillCore(throwOnError: false, entireProcessGroup: _isGroupLeader); |
|
ProcessExitStatus status = WaitForExitCore(); |
|
return new ProcessExitStatus(status.ExitCode, wasKilled, status.Signal); |
|
default: |
|
ProcessExitStatus exitStatus = new(exitCode, false, rawSignal != 0 ? (PosixSignal)rawSignal : null); |
|
OnProcessExited(); |
|
return exitStatus; |
|
} |
|
}, cancellationToken).ConfigureAwait(false); |
|
} |
|
} |
WaitForExitOrKillOnTimeoutandWaitForExitOrKillOnCancellationAsyncmethods must not throw on timeout/cancellation, just kill the process (if it has not already exited) and returnProcessExitStatuswithCanceled=true(if the process was actually killed)runtime/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessExitStatus.cs
Lines 50 to 53 in fc42ca6
runtime/src/libraries/System.Diagnostics.Process/src/Microsoft/Win32/SafeHandles/SafeProcessHandle.Windows.cs
Lines 400 to 511 in 3f5919d
runtime/src/libraries/System.Diagnostics.Process/src/Microsoft/Win32/SafeHandles/SafeProcessHandle.Unix.cs
Lines 188 to 300 in a247583
pidfdandpollto wait for process exit was provided in Implement SafeProcessHandle APIs for Linux and other Unixes #124979SafeFileHandle.CreateAnonymousPipeAPI for creating pipe that is used for monitoring for cancellationWaitmethods need to be able to reap the process, but once they do they need to updateProcessWaitStateProcessWaitStatereaps the process first, theWait*methods should obtain the exit status from theresrc/libraries/System.Diagnostics.Process/tests/SafeProcessHandleTests.csintroduced by Introduce SafeProcessHandle.Start and ProcessId #126192 and reuse tests provided in Add new process management APIs to SafeProcessHandle #124375 and Implement SafeProcessHandle APIs for macOS #124923