Skip to content

Commit

Permalink
Refactor IoWorker of debugger. Fixes #336. (#377)
Browse files Browse the repository at this point in the history
  • Loading branch information
Kuinox authored Jan 27, 2024
1 parent afde6cb commit 1265053
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 29 deletions.
8 changes: 3 additions & 5 deletions src/Draco.Debugger/Debugger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public sealed class Debugger
/// <summary>
/// The task that is completed, when the process has terminated.
/// </summary>
public Task Terminated => this.terminatedCompletionSource.Task;
public Task Terminated => this.ioWorker.WorkLoopTask;

/// <summary>
/// The main user-module.
Expand Down Expand Up @@ -94,7 +94,6 @@ public event EventHandler<string> OnStandardError
private readonly IoWorker<CorDebugProcess> ioWorker;

private readonly TaskCompletionSource terminatedCompletionSource = new();
private readonly CancellationTokenSource terminateTokenSource = new();

private readonly SessionCache sessionCache = new();

Expand All @@ -111,7 +110,7 @@ internal Debugger(
this.ioWorker = ioWorker;

this.InitializeEventHandler(cb);
ioWorker.Run(this.terminateTokenSource.Token);
ioWorker.Start();
}

private void ClearCache()
Expand Down Expand Up @@ -322,8 +321,7 @@ private void OnUnloadModuleHandler(object? sender, UnloadModuleCorDebugManagedCa

private void OnExitProcessHandler(object? sender, ExitProcessCorDebugManagedCallbackEventArgs args)
{
this.terminateTokenSource.Cancel();
this.terminatedCompletionSource.SetResult();
this.ioWorker.SignalStop();
// TODO: Get exit code properly
this.OnExited?.Invoke(sender, 0);
this.Continue();
Expand Down
55 changes: 31 additions & 24 deletions src/Draco.Debugger/IO/IoWorker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,13 @@ internal sealed class IoWorker<TProcess>
/// </summary>
public StreamWriter StandardInput { get; }

/// <summary>
/// The task of the worker loop. Is always completed when the loop is not running.
/// </summary>
public Task WorkLoopTask { get; private set; } = Task.CompletedTask;
private readonly TProcess process;
private readonly RemoteIoHandles handles;
private readonly CancellationTokenSource _readCTS = new();

public IoWorker(TProcess process, RemoteIoHandles handles)
{
Expand All @@ -43,36 +48,38 @@ public IoWorker(TProcess process, RemoteIoHandles handles)
/// </summary>
/// <param name="cancellationToken">Can be used to cancel the worker.</param>
/// <returns>The task of the worker loop.</returns>
public Task Run(CancellationToken cancellationToken) => Task.Run(async () =>
public void Start() =>
this.WorkLoopTask = Task.WhenAll(
this.ReadStdout(),
this.ReadStderr()
);

public void SignalStop() => this._readCTS.Cancel();

private async Task ReadStdout()
{
var stdoutReader = new StreamReader(this.handles.StandardOutputReader);
var stderrReader = new StreamReader(this.handles.StandardErrorReader);

var stdoutBuffer = new char[BufferSize];
var stderrBuffer = new char[BufferSize];

var stdoutTask = null as Task<int>;
var stderrTask = null as Task<int>;

var cancellationToken = this._readCTS.Token;
while (!cancellationToken.IsCancellationRequested)
{
stdoutTask ??= stdoutReader.ReadAsync(stdoutBuffer, cancellationToken).AsTask();
stderrTask ??= stderrReader.ReadAsync(stderrBuffer, cancellationToken).AsTask();
var result = await stdoutReader.ReadAsync(stdoutBuffer, cancellationToken);
var str = new string(stdoutBuffer, 0, result);
this.OnStandardOut?.Invoke(this.process, str);
}
}

await Task.WhenAny(stdoutTask, stderrTask);
private async Task ReadStderr()
{
var stderrReader = new StreamReader(this.handles.StandardErrorReader);
var stderrBuffer = new char[BufferSize];
var cancellationToken = this._readCTS.Token;

if (stdoutTask.IsCompleted)
{
var str = new string(stdoutBuffer, 0, stdoutTask.Result);
this.OnStandardOut?.Invoke(this.process, str);
stdoutTask = null;
}
if (stderrTask.IsCompleted)
{
var str = new string(stderrBuffer, 0, stderrTask.Result);
this.OnStandardError?.Invoke(this.process, str);
stderrTask = null;
}
while (!cancellationToken.IsCancellationRequested)
{
var result = await stderrReader.ReadAsync(stderrBuffer, cancellationToken);
var str = new string(stderrBuffer, 0, result);
this.OnStandardError?.Invoke(this.process, str);
}
}, cancellationToken);
}
}

0 comments on commit 1265053

Please sign in to comment.