public async Task Kill_ExitedNonChildProcess_DoesNotThrow(bool killTree) { // In this test, we kill a process in a way the Process instance // is not aware the process has terminated when we invoke Process.Kill. DateTime start = DateTime.UtcNow; using (Process nonChildProcess = CreateNonChildProcess()) { // Kill the process. int rv = kill(nonChildProcess.Id, SIGKILL); Assert.Equal(0, rv); // Wait until the process is reaped. while (rv == 0) { rv = kill(nonChildProcess.Id, 0); if (rv == 0) { // process still exists, wait some time. await Task.Delay(100); } DateTime now = DateTime.UtcNow; if (start.Ticks + (Helpers.PassingTestTimeoutMilliseconds * 10_000) <= now.Ticks) { Console.WriteLine("{0} Failed to kill process {1} started at {2}", now, nonChildProcess.Id, start); Helpers.DumpAllProcesses(); Assert.True(false, "test timed out"); } } // Call Process.Kill. nonChildProcess.Kill(killTree); } Process CreateNonChildProcess() { // Create a process that isn't a direct child. int nonChildPid = -1; RemoteInvokeHandle createNonChildProcess = RemoteExecutor.Invoke(arg => { RemoteInvokeHandle nonChildProcess = RemoteExecutor.Invoke( // Process that lives as long as the test process. testProcessPid => Process.GetProcessById(int.Parse(testProcessPid)).WaitForExit(), arg, // Don't pass our standard out to the sleepProcess or the ReadToEnd below won't return. new RemoteInvokeOptions { StartInfo = new ProcessStartInfo() { RedirectStandardOutput = true } }); using (nonChildProcess) { Console.WriteLine(nonChildProcess.Process.Id); // Don't wait for the process to exit. nonChildProcess.Process = null; } }, Process.GetCurrentProcess().Id.ToString(), new RemoteInvokeOptions { StartInfo = new ProcessStartInfo() { RedirectStandardOutput = true } }); using (createNonChildProcess) { nonChildPid = int.Parse(createNonChildProcess.Process.StandardOutput.ReadToEnd()); } return Process.GetProcessById(nonChildPid); } }