Nullify ProcessExecutor

This commit is contained in:
Jordan Dominion
2023-11-24 10:02:13 -05:00
parent 4144ed6c99
commit 07ad701f57
5 changed files with 24 additions and 26 deletions
+1 -1
View File
@@ -29,7 +29,7 @@ namespace Tgstation.Server.Host.System
/// the result of this function must be <see langword="await"/>ed before <see cref="IAsyncDisposable.DisposeAsync"/> is called.
/// May call <see cref="IAsyncDisposable.DisposeAsync"/> internally if the process has exited.
/// </remarks>
ValueTask<string> GetCombinedOutput(CancellationToken cancellationToken);
ValueTask<string?> GetCombinedOutput(CancellationToken cancellationToken);
/// <summary>
/// Asycnhronously terminates the process.
@@ -18,7 +18,7 @@
IProcess LaunchProcess(
string fileName,
string workingDirectory,
string? arguments = null,
string arguments,
string? fileRedirect = null,
bool readStandardHandles = false,
bool noShellExecute = false);
@@ -34,13 +34,13 @@
/// </summary>
/// <param name="id">The <see cref="IProcess.Id"/>.</param>
/// <returns>The <see cref="IProcess"/> represented by <paramref name="id"/> on success, <see langword="null"/> on failure.</returns>
IProcess GetProcess(int id);
IProcess? GetProcess(int id);
/// <summary>
/// Get a <see cref="IProcess"/> with a given <paramref name="name"/>.
/// </summary>
/// <param name="name">The name of the process executable without the extension.</param>
/// <returns>The <see cref="IProcess"/> represented by <paramref name="name"/> on success, <see langword="null"/> on failure.</returns>
IProcess GetProcessByName(string name);
IProcess? GetProcessByName(string name);
}
}
@@ -86,7 +86,7 @@ namespace Tgstation.Server.Host.System
throw new JobException(ErrorCode.GameServerOffline, ex);
}
string output;
string? output;
int exitCode;
await using (var gcoreProc = lazyLoadedProcessExecutor.Value.LaunchProcess(
GCorePath,
+4 -4
View File
@@ -57,7 +57,7 @@ namespace Tgstation.Server.Host.System
/// <summary>
/// The <see cref="Task{TResult}"/> resulting in the process' standard output/error text.
/// </summary>
readonly Task<string> readTask;
readonly Task<string?>? readTask;
/// <summary>
/// If the <see cref="Process"/> was disposed.
@@ -78,8 +78,8 @@ namespace Tgstation.Server.Host.System
IProcessFeatures processFeatures,
IAsyncDelayer asyncDelayer,
global::System.Diagnostics.Process handle,
CancellationTokenSource readerCts,
Task<string> readTask,
CancellationTokenSource? readerCts,
Task<string?>? readTask,
ILogger<Process> logger,
bool preExisting)
{
@@ -144,7 +144,7 @@ namespace Tgstation.Server.Host.System
}
/// <inheritdoc />
public async ValueTask<string> GetCombinedOutput(CancellationToken cancellationToken)
public async ValueTask<string?> GetCombinedOutput(CancellationToken cancellationToken)
{
if (readTask == null)
throw new InvalidOperationException("Output/Error stream reading was not enabled!");
@@ -9,8 +9,6 @@ using Microsoft.Extensions.Logging;
using Tgstation.Server.Host.IO;
using Tgstation.Server.Host.Utils;
#nullable disable
namespace Tgstation.Server.Host.System
{
/// <inheritdoc />
@@ -86,7 +84,7 @@ namespace Tgstation.Server.Host.System
}
/// <inheritdoc />
public IProcess GetProcess(int id)
public IProcess? GetProcess(int id)
{
logger.LogDebug("Attaching to process {pid}...", id);
global::System.Diagnostics.Process handle;
@@ -116,7 +114,7 @@ namespace Tgstation.Server.Host.System
string fileName,
string workingDirectory,
string arguments,
string fileRedirect,
string? fileRedirect,
bool readStandardHandles,
bool noShellExecute)
{
@@ -143,11 +141,11 @@ namespace Tgstation.Server.Host.System
handle.StartInfo.UseShellExecute = !noShellExecute;
Task<string> readTask = null;
CancellationTokenSource disposeCts = null;
Task<string?>? readTask = null;
CancellationTokenSource? disposeCts = null;
try
{
TaskCompletionSource processStartTcs = null;
TaskCompletionSource? processStartTcs = null;
if (readStandardHandles)
{
processStartTcs = new TaskCompletionSource();
@@ -203,11 +201,11 @@ namespace Tgstation.Server.Host.System
}
/// <inheritdoc />
public IProcess GetProcessByName(string name)
public IProcess? GetProcessByName(string name)
{
logger.LogTrace("GetProcessByName: {processName}...", name ?? throw new ArgumentNullException(nameof(name)));
var procs = global::System.Diagnostics.Process.GetProcessesByName(name);
global::System.Diagnostics.Process handle = null;
global::System.Diagnostics.Process? handle = null;
foreach (var proc in procs)
if (handle == null)
handle = proc;
@@ -231,7 +229,7 @@ namespace Tgstation.Server.Host.System
/// <param name="fileRedirect">The optional path to redirect the streams to.</param>
/// <param name="disposeToken">The <see cref="CancellationToken"/> that triggers when the <see cref="Process"/> is disposed.</param>
/// <returns>A <see cref="Task{TResult}"/> resulting in the program's output/error text if <paramref name="fileRedirect"/> is <see langword="null"/>, <see langword="null"/> otherwise.</returns>
async Task<string> ConsumeReaders(global::System.Diagnostics.Process handle, Task startTask, string fileRedirect, CancellationToken disposeToken)
async Task<string?> ConsumeReaders(global::System.Diagnostics.Process handle, Task startTask, string? fileRedirect, CancellationToken disposeToken)
{
await startTask;
@@ -241,9 +239,9 @@ namespace Tgstation.Server.Host.System
// once we obtain these handles we're responsible for them
using var stdOutHandle = handle.StandardOutput;
using var stdErrHandle = handle.StandardError;
Task<string> outputReadTask = null, errorReadTask = null;
Task<string?>? outputReadTask = null, errorReadTask = null;
bool outputOpen = true, errorOpen = true;
async Task<string> GetNextLine()
async Task<string?> GetNextLine()
{
if (outputOpen && outputReadTask == null)
outputReadTask = stdOutHandle.ReadLineAsync(disposeToken).AsTask();
@@ -251,7 +249,7 @@ namespace Tgstation.Server.Host.System
if (errorOpen && errorReadTask == null)
errorReadTask = stdErrHandle.ReadLineAsync(disposeToken).AsTask();
var completedTask = await Task.WhenAny(outputReadTask ?? errorReadTask, errorReadTask ?? outputReadTask);
var completedTask = await Task.WhenAny(outputReadTask ?? errorReadTask!, errorReadTask ?? outputReadTask!);
var line = await completedTask;
if (completedTask == outputReadTask)
{
@@ -275,7 +273,7 @@ namespace Tgstation.Server.Host.System
await using var fileStream = fileRedirect != null ? ioManager.CreateAsyncSequentialWriteStream(fileRedirect) : null;
await using var writer = fileStream != null ? new StreamWriter(fileStream) : null;
string text;
string? text;
var stringBuilder = fileStream == null ? new StringBuilder() : null;
try
{
@@ -283,11 +281,11 @@ namespace Tgstation.Server.Host.System
{
if (fileStream != null)
{
await writer.WriteLineAsync(text.AsMemory(), disposeToken);
await writer!.WriteLineAsync(text.AsMemory(), disposeToken);
await writer.FlushAsync(disposeToken);
}
else
stringBuilder.AppendLine(text);
stringBuilder!.AppendLine(text);
}
logger.LogTrace("Finished read for PID {pid}", pid);
@@ -296,7 +294,7 @@ namespace Tgstation.Server.Host.System
{
logger.LogWarning(ex, "PID {pid} stream reading interrupted!", pid);
if (fileStream != null)
await writer.WriteLineAsync("-- Process detached, log truncated. This is likely due a to TGS restart --");
await writer!.WriteLineAsync("-- Process detached, log truncated. This is likely due a to TGS restart --");
}
return stringBuilder?.ToString();