Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion dotnet/src/Client.cs
Original file line number Diff line number Diff line change
Expand Up @@ -762,7 +762,13 @@ private static async Task CleanupCliProcessAsync(Process childProcess, ProcessSt
s_stderrPumpShutdownTimeout);
}

AddCleanupError(errors, ex, logger);
// Once the owned process has exited, stderr is diagnostic-only. A descendant
// can briefly retain the inherited pipe on Windows, but that must not turn a
// successful process shutdown into a client cleanup failure.
if (!processExited)
{
AddCleanupError(errors, ex, logger);
}
}
catch (Exception ex)
{
Expand Down
250 changes: 250 additions & 0 deletions dotnet/src/Generated/Rpc.cs

Large diffs are not rendered by default.

116 changes: 108 additions & 8 deletions dotnet/src/Generated/SessionEvents.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 8 additions & 3 deletions dotnet/test/E2E/RpcShellEdgeCaseE2ETests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,21 @@ public async Task Shell_Exec_With_Timeout_Kills_Long_Running_Command()
var markerPath = Path.Join(Ctx.WorkDir, $"shell-timeout-{Guid.NewGuid():N}.txt");
var startedPath = Path.Join(Ctx.WorkDir, $"shell-timeout-started-{Guid.NewGuid():N}.txt");

// Sleep 30s but timeout at 200ms — runtime should SIGTERM the child before the
// Sleep 30s but use a much shorter timeout — runtime should SIGTERM the child before the
// sleep completes, which means the marker file must NEVER appear within a wait
// window comfortably greater than the timeout but well under the sleep duration.
// Process startup on Windows can exceed 200ms on loaded runners, so match the
// platform-specific allowance used by the Rust coverage for this RPC.
var timeout = OperatingSystem.IsWindows()
? TimeSpan.FromSeconds(2)
: TimeSpan.FromMilliseconds(200);
var command = OperatingSystem.IsWindows()
? $"echo started>\"{startedPath}\" & for /L %i in (1,1,2147483647) do @rem & echo should-not-exist>\"{markerPath}\""
? $"powershell -NoLogo -NoProfile -Command \"Set-Content -LiteralPath '{startedPath}' -Value started; Start-Sleep -Seconds 30; Set-Content -LiteralPath '{markerPath}' -Value should-not-exist\""
: $"printf 'started' > '{startedPath}'; sleep 30; printf 'should-not-exist' > '{markerPath}'";

// On Windows, terminating the shell wrapper can briefly leave children alive.
// Keep this long-running command outside the fixture workspace so cleanup is not blocked by cwd handles.
var result = await session.Rpc.Shell.ExecAsync(command, cwd: Path.GetTempPath(), timeout: TimeSpan.FromMilliseconds(200));
var result = await session.Rpc.Shell.ExecAsync(command, cwd: Path.GetTempPath(), timeout: timeout);
Assert.False(string.IsNullOrWhiteSpace(result.ProcessId));

await TestHelper.WaitForConditionAsync(
Expand Down
Loading
Loading