Guard against multiple run invocations
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
using System.Diagnostics;
|
using System.Collections.Concurrent;
|
||||||
|
using System.Diagnostics;
|
||||||
using Ardalis.GuardClauses;
|
using Ardalis.GuardClauses;
|
||||||
using AsyncReadProcess.Common;
|
using AsyncReadProcess.Common;
|
||||||
using AsyncReadProcess;
|
using AsyncReadProcess;
|
||||||
@@ -9,44 +10,53 @@ namespace SharpIDE.Application.Features.Run;
|
|||||||
|
|
||||||
public class RunService
|
public class RunService
|
||||||
{
|
{
|
||||||
public HashSet<SharpIdeProjectModel> RunningProjects { get; } = [];
|
private readonly ConcurrentDictionary<SharpIdeProjectModel, SemaphoreSlim> _projectLocks = [];
|
||||||
// TODO: optimise this Copilot junk
|
|
||||||
public async Task RunProject(SharpIdeProjectModel project)
|
public async Task RunProject(SharpIdeProjectModel project)
|
||||||
{
|
{
|
||||||
Guard.Against.Null(project, nameof(project));
|
Guard.Against.Null(project, nameof(project));
|
||||||
Guard.Against.NullOrWhiteSpace(project.FilePath, nameof(project.FilePath), "Project file path cannot be null or empty.");
|
Guard.Against.NullOrWhiteSpace(project.FilePath, nameof(project.FilePath), "Project file path cannot be null or empty.");
|
||||||
await Task.CompletedTask.ConfigureAwait(ConfigureAwaitOptions.ForceYielding);
|
await Task.CompletedTask.ConfigureAwait(ConfigureAwaitOptions.ForceYielding);
|
||||||
|
|
||||||
var processStartInfo = new ProcessStartInfo2
|
var semaphoreSlim = _projectLocks.GetOrAdd(project, new SemaphoreSlim(1, 1));
|
||||||
|
var waitResult = await semaphoreSlim.WaitAsync(0);
|
||||||
|
if (waitResult is false) throw new InvalidOperationException($"Project {project.Name} is already running.");
|
||||||
|
try
|
||||||
{
|
{
|
||||||
FileName = "dotnet",
|
var processStartInfo = new ProcessStartInfo2
|
||||||
Arguments = $"run --project \"{project.FilePath}\" --no-build",
|
|
||||||
RedirectStandardOutput = true,
|
|
||||||
RedirectStandardError = true
|
|
||||||
};
|
|
||||||
|
|
||||||
await using var process = new AsyncReadProcess.Process2()
|
|
||||||
{
|
|
||||||
StartInfo = processStartInfo
|
|
||||||
};
|
|
||||||
|
|
||||||
process.Start();
|
|
||||||
|
|
||||||
_ = Task.Run(async () =>
|
|
||||||
{
|
|
||||||
await foreach(var log in process.CombinedOutputChannel.Reader.ReadAllAsync())
|
|
||||||
{
|
{
|
||||||
var logString = System.Text.Encoding.UTF8.GetString(log, 0, log.Length);
|
FileName = "dotnet",
|
||||||
Console.Write(logString);
|
Arguments = $"run --project \"{project.FilePath}\" --no-build",
|
||||||
}
|
RedirectStandardOutput = true,
|
||||||
});
|
RedirectStandardError = true
|
||||||
|
};
|
||||||
|
|
||||||
project.Running = true;
|
await using var process = new AsyncReadProcess.Process2()
|
||||||
GlobalEvents.InvokeProjectsRunningChanged();
|
{
|
||||||
await process.WaitForExitAsync();
|
StartInfo = processStartInfo
|
||||||
project.Running = false;
|
};
|
||||||
GlobalEvents.InvokeProjectsRunningChanged();
|
|
||||||
|
|
||||||
Console.WriteLine("Project ran successfully.");
|
process.Start();
|
||||||
|
|
||||||
|
_ = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await foreach(var log in process.CombinedOutputChannel.Reader.ReadAllAsync())
|
||||||
|
{
|
||||||
|
var logString = System.Text.Encoding.UTF8.GetString(log, 0, log.Length);
|
||||||
|
Console.Write(logString);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
project.Running = true;
|
||||||
|
GlobalEvents.InvokeProjectsRunningChanged();
|
||||||
|
await process.WaitForExitAsync();
|
||||||
|
project.Running = false;
|
||||||
|
GlobalEvents.InvokeProjectsRunningChanged();
|
||||||
|
|
||||||
|
Console.WriteLine("Project ran successfully.");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
semaphoreSlim.Release();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user