Sandbox: Start aus einem langlebigen Thread - das Elternsterbe-Signal haengt am Thread (#654)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
6204f1247c
commit
f97dfa06d6
@@ -219,7 +219,7 @@ public sealed class BwrapSandbox : ISandboxRunner
|
||||
|
||||
private async Task<SandboxResult> RunProcessAsync(ProcessStartInfo start, SandboxRequest request, CancellationToken ct)
|
||||
{
|
||||
using var process = Process.Start(start) ?? throw new InvalidOperationException("The sandbox did not start.");
|
||||
using var process = await Spawner.StartAsync(start);
|
||||
using var timeout = CancellationTokenSource.CreateLinkedTokenSource(ct);
|
||||
timeout.CancelAfter(request.Limits.Timeout);
|
||||
|
||||
@@ -411,6 +411,52 @@ public sealed class BwrapSandbox : ISandboxRunner
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// #654: Starts every sandbox from one thread that lives as long as the process.
|
||||
///
|
||||
/// <para><b>Why.</b> <c>--die-with-parent</c> uses the kernel's parent-death signal, and that
|
||||
/// is bound to the <i>thread</i> that forked, not to the process. .NET forks on the calling
|
||||
/// thread — a thread-pool thread, and the pool retires idle threads. Seen on the test machine
|
||||
/// under load: the pool dropped a thread, bwrap got SIGKILL (exit 137), and when it happened
|
||||
/// while bwrap was still building the sandbox, the inside lived on as an orphan holding the
|
||||
/// output pipes. Started from a thread that never ends, the signal fires only when the
|
||||
/// instance itself dies — which is what it is for.</para>
|
||||
/// </summary>
|
||||
internal static class Spawner
|
||||
{
|
||||
private static readonly System.Collections.Concurrent.BlockingCollection<(ProcessStartInfo Start, TaskCompletionSource<Process> Done)> Queue = new();
|
||||
|
||||
static Spawner()
|
||||
{
|
||||
var thread = new Thread(() =>
|
||||
{
|
||||
foreach (var (start, done) in Queue.GetConsumingEnumerable())
|
||||
{
|
||||
try
|
||||
{
|
||||
done.SetResult(Process.Start(start) ?? throw new InvalidOperationException("The sandbox did not start."));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
done.SetException(ex);
|
||||
}
|
||||
}
|
||||
})
|
||||
{
|
||||
IsBackground = true,
|
||||
Name = "ClawdDotNet sandbox spawner"
|
||||
};
|
||||
thread.Start();
|
||||
}
|
||||
|
||||
public static Task<Process> StartAsync(ProcessStartInfo start)
|
||||
{
|
||||
var done = new TaskCompletionSource<Process>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
Queue.Add((start, done));
|
||||
return done.Task;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>#654: What the installed bwrap can do beyond the basics.</summary>
|
||||
public sealed record BwrapFeatures(bool DisableUserns, bool ClearEnv)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user