Skip to content

Commit 6cf3f95

Browse files
committed
Add IChildProcess.Id
1 parent 02e90ba commit 6cf3f95

File tree

9 files changed

+71
-21
lines changed

9 files changed

+71
-21
lines changed

src/ChildProcess.Test/ProcessManagement/ChildProcessTest_Creation.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,5 +119,24 @@ static string ToResolvedCurrentDirectory(string path)
119119
return ExecuteForStandardOutput(si);
120120
}
121121
}
122+
123+
[Fact]
124+
public void CanObtainProcessId()
125+
{
126+
var si = new ChildProcessStartInfo(TestUtil.DotnetCommandName, TestUtil.TestChildPath, "EchoBack")
127+
{
128+
StdInputRedirection = InputRedirection.InputPipe,
129+
StdOutputRedirection = OutputRedirection.NullDevice,
130+
};
131+
132+
using var sut = ChildProcess.Start(si);
133+
134+
using var p = System.Diagnostics.Process.GetProcessById(sut.Id);
135+
Assert.StartsWith(Path.GetFileNameWithoutExtension(TestUtil.DotnetCommandName), p.ProcessName, StringComparison.OrdinalIgnoreCase);
136+
sut.StandardInput.Close();
137+
138+
sut.WaitForExit();
139+
Assert.Equal(0, sut.ExitCode);
140+
}
122141
}
123142
}

src/ChildProcess/PlatformAbstraction/Utilities/NamedPipeUtil.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ namespace Asmichi.PlatformAbstraction.Utilities
88
internal static class NamedPipeUtil
99
{
1010
// NOTE: There may be multiple instances of this assembly (AppDomain or AssemblyLoadContext).
11-
// Therefore embedding pid only does not provide uniqueness.
12-
public static string MakePipePathPrefix(string pathPrefix, uint pid) =>
11+
// Therefore embedding the process id only does not provide uniqueness.
12+
public static string MakePipePathPrefix(string pathPrefix, uint processId) =>
1313
Path.Combine(
1414
pathPrefix,
15-
string.Format(CultureInfo.InvariantCulture, "Asmichi.ChildProcess.{0:D5}.{1}.", pid, Path.GetRandomFileName()));
15+
string.Format(CultureInfo.InvariantCulture, "Asmichi.ChildProcess.{0:D5}.{1}.", processId, Path.GetRandomFileName()));
1616
}
1717
}

src/ChildProcess/ProcessManagement/ChildProcessImpl.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,16 @@ public Task<bool> WaitForExitAsync(TimeSpan timeout, CancellationToken cancellat
126126
return operation.Completion;
127127
}
128128

129+
public int Id
130+
{
131+
get
132+
{
133+
CheckNotDisposed();
134+
135+
return _stateHolder.State.ProcessId;
136+
}
137+
}
138+
129139
public int ExitCode
130140
{
131141
get

src/ChildProcess/ProcessManagement/IChildProcess.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@
77

88
namespace Asmichi.ProcessManagement
99
{
10+
// Not providing `Handle` since *nix does not provide a file descriptor of a process
11+
// (except for a pidfd in linux).
12+
//
13+
// "everything is a file descriptor or a process" --- Linus Torvalds
14+
// https://lore.kernel.org/lkml/[email protected]/
15+
1016
/// <summary>
1117
/// <para>
1218
/// Provides methods for accessing a child-process-like object.
@@ -19,6 +25,15 @@ namespace Asmichi.ProcessManagement
1925
/// </summary>
2026
public interface IChildProcess : IDisposable
2127
{
28+
/// <summary>
29+
/// Gets the system-generated process identifier for the process.
30+
/// </summary>
31+
/// <remarks>
32+
/// On *nix, the identifier will get invalid as soon as the process exits.
33+
/// Make sure to keep the process running when using the identifier.
34+
/// </remarks>
35+
int Id { get; }
36+
2237
/// <summary>
2338
/// Gets a value indicating whether the exit code of the process is 0.
2439
/// </summary>

src/ChildProcess/ProcessManagement/IChildProcessState.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ internal interface IChildProcessStateHolder : IDisposable
1919
// NOTE: A pipe to a process itself is not a part of the state of a child process (but of ours).
2020
internal interface IChildProcessState
2121
{
22+
int ProcessId { get; }
2223
int ExitCode { get; }
2324
WaitHandle ExitedWaitHandle { get; }
2425
bool HasExitCode { get; }

src/ChildProcess/ProcessManagement/UnixChildProcessState.cs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ internal sealed class UnixChildProcessState : IChildProcessState, IDisposable
5555
private readonly bool _allowSignal;
5656
private int _refCount = 1;
5757
private bool _hasExited;
58-
private int _pid = -1;
58+
private int _processId = -1;
5959
private int _exitCode = -1;
6060

6161
private UnixChildProcessState(UnixChildProcessStateHelper helper, long token, bool allowSignal)
@@ -65,11 +65,11 @@ private UnixChildProcessState(UnixChildProcessStateHelper helper, long token, bo
6565
_allowSignal = allowSignal;
6666
}
6767

68+
public int ProcessId => GetProcessId();
6869
public int ExitCode => GetExitCode();
6970
public bool HasExitCode => GetHasExited();
7071
public long Token => _token;
7172
public WaitHandle ExitedWaitHandle => _exitedEvent;
72-
public int Pid => GetPid();
7373

7474
public void Dispose()
7575
{
@@ -180,21 +180,21 @@ private bool GetHasExited()
180180
}
181181
}
182182

183-
private int GetPid()
183+
private int GetProcessId()
184184
{
185-
Debug.Assert(_pid != -1);
186-
return _pid;
185+
Debug.Assert(_processId != -1);
186+
return _processId;
187187
}
188188

189189
/// <summary>
190-
/// Sets the PID of the child process.
190+
/// Sets the process ID of the child process.
191191
/// The caller of <see cref="Create"/> must call this before returning <see cref="UnixChildProcessState"/> to <see cref="ChildProcessImpl"/>.
192192
/// </summary>
193-
/// <param name="pid">The PID of the created child process.</param>
194-
public void SetPid(int pid)
193+
/// <param name="processId">The process ID of the created child process.</param>
194+
public void SetProcessId(int processId)
195195
{
196-
Debug.Assert(_pid == -1);
197-
_pid = pid;
196+
Debug.Assert(_processId == -1);
197+
_processId = processId;
198198
}
199199

200200
public void SetExited(int exitCode)

src/ChildProcess/ProcessManagement/UnixChildProcessStateHelper.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ public IChildProcessStateHolder SpawnProcess(
194194
GC.KeepAlive(stdOut);
195195
GC.KeepAlive(stdErr);
196196

197-
var (error, pid) = subchannel.ReceiveCommonResponse();
197+
var (error, processId) = subchannel.ReceiveCommonResponse();
198198
if (error > 0)
199199
{
200200
throw new Win32Exception(error);
@@ -205,7 +205,7 @@ public IChildProcessStateHolder SpawnProcess(
205205
string.Format(CultureInfo.InvariantCulture, "Internal logic error: Bad request {0}.", error));
206206
}
207207

208-
stateHolder.State.SetPid(pid);
208+
stateHolder.State.SetProcessId(processId);
209209

210210
return stateHolder;
211211
}
@@ -237,7 +237,7 @@ public void SendSignal(long token, UnixHelperProcessSignalNumber signalNumber)
237237
{
238238
subchannel.SendExactBytes(request);
239239

240-
var (error, pid) = subchannel.ReceiveCommonResponse();
240+
var (error, _) = subchannel.ReceiveCommonResponse();
241241
if (error > 0)
242242
{
243243
throw new Win32Exception(error);

src/ChildProcess/ProcessManagement/WindowsChildProcessState.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,21 @@ internal class WindowsChildProcessState : IChildProcessStateHolder, IChildProces
2020
private readonly InputWriterOnlyPseudoConsole? _pseudoConsole;
2121
private readonly bool _allowSignal;
2222
private readonly WaitHandle _exitedWaitHandle;
23+
private readonly int _processId;
2324
private int _exitCode = -1;
2425
private bool _hasExitCode;
2526
private bool _isPseudoConsoleDisposed;
2627

2728
public WindowsChildProcessState(
29+
int processId,
2830
SafeProcessHandle processHandle,
2931
SafeJobObjectHandle jobObjectHandle,
3032
InputWriterOnlyPseudoConsole? pseudoConsole,
3133
bool allowSignal)
3234
{
3335
Debug.Assert(!(allowSignal && pseudoConsole is null));
3436

37+
_processId = processId;
3538
_processHandle = processHandle;
3639
_jobObjectHandle = jobObjectHandle;
3740
_pseudoConsole = pseudoConsole;
@@ -52,6 +55,7 @@ public void Dispose()
5255
}
5356

5457
public IChildProcessState State => this;
58+
public int ProcessId => _processId;
5559
public int ExitCode => GetExitCode();
5660
public WaitHandle ExitedWaitHandle => _exitedWaitHandle;
5761
public bool HasExitCode => _hasExitCode;

src/ChildProcess/ProcessManagement/WindowsChildProcessStateHelper.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,8 @@ public unsafe IChildProcessStateHolder SpawnProcess(
8080
| Kernel32.EXTENDED_STARTUPINFO_PRESENT
8181
| Kernel32.CREATE_SUSPENDED;
8282

83-
(processHandle, threadHandle) = InvokeCreateProcess(
83+
int processId;
84+
(processId, processHandle, threadHandle) = InvokeCreateProcess(
8485
commandLine,
8586
CreationFlags,
8687
environmentBlock,
@@ -102,7 +103,7 @@ public unsafe IChildProcessStateHolder SpawnProcess(
102103
throw new Win32Exception();
103104
}
104105

105-
return new WindowsChildProcessState(processHandle, jobObjectHandle, pseudoConsole, startInfo.AllowSignal);
106+
return new WindowsChildProcessState(processId, processHandle, jobObjectHandle, pseudoConsole, startInfo.AllowSignal);
106107
}
107108
}
108109
catch
@@ -155,7 +156,7 @@ private static unsafe void ChangeCodePage(
155156
| Kernel32.EXTENDED_STARTUPINFO_PRESENT;
156157

157158
SafeThreadHandle threadHandle;
158-
(processHandle, threadHandle) = InvokeCreateProcess(
159+
(_, processHandle, threadHandle) = InvokeCreateProcess(
159160
commandLine,
160161
CreationFlags,
161162
null,
@@ -222,7 +223,7 @@ private static unsafe SafeJobObjectHandle CreateJobObject(bool killOnClose)
222223
}
223224
}
224225

225-
private static unsafe (SafeProcessHandle processHandle, SafeThreadHandle threadHandle) InvokeCreateProcess(
226+
private static unsafe (int processId, SafeProcessHandle processHandle, SafeThreadHandle threadHandle) InvokeCreateProcess(
226227
StringBuilder commandLine,
227228
int creationFlags,
228229
char[]? environmentBlock,
@@ -259,7 +260,7 @@ private static unsafe (SafeProcessHandle processHandle, SafeThreadHandle threadH
259260
throw new Win32Exception();
260261
}
261262

262-
return (new SafeProcessHandle(pi.hProcess, true), new SafeThreadHandle(pi.hThread, true));
263+
return (pi.dwProcessId, new SafeProcessHandle(pi.hProcess, true), new SafeThreadHandle(pi.hThread, true));
263264
}
264265
}
265266
}

0 commit comments

Comments
 (0)