Add extra synchronization to IPC state management

Thanks @ezhevita
This commit is contained in:
Łukasz Domeradzki
2025-09-08 23:39:29 +02:00
parent 5d2665207a
commit 2bbf197108
2 changed files with 67 additions and 25 deletions

View File

@@ -355,8 +355,8 @@ public static class ASF {
} }
ArchiLogger.LogGenericInfo(Strings.IPCConfigChanged); ArchiLogger.LogGenericInfo(Strings.IPCConfigChanged);
await ArchiKestrel.Stop().ConfigureAwait(false);
await ArchiKestrel.Start().ConfigureAwait(false); await ArchiKestrel.Restart().ConfigureAwait(false);
} }
private static async Task OnChangedFile(string name, string fullPath) { private static async Task OnChangedFile(string name, string fullPath) {

View File

@@ -30,6 +30,7 @@ using System.Net;
using System.Reflection; using System.Reflection;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Nodes; using System.Text.Json.Nodes;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using ArchiSteamFarm.Core; using ArchiSteamFarm.Core;
using ArchiSteamFarm.Helpers.Json; using ArchiSteamFarm.Helpers.Json;
@@ -64,6 +65,8 @@ internal static class ArchiKestrel {
internal static HistoryTarget? HistoryTarget { get; private set; } internal static HistoryTarget? HistoryTarget { get; private set; }
private static readonly SemaphoreSlim StateSemaphore = new(1, 1);
private static WebApplication? WebApplication; private static WebApplication? WebApplication;
internal static void OnNewHistoryTarget(HistoryTarget? historyTarget = null) { internal static void OnNewHistoryTarget(HistoryTarget? historyTarget = null) {
@@ -78,43 +81,43 @@ internal static class ArchiKestrel {
} }
} }
internal static async Task Start() { internal static async Task Restart() {
if (WebApplication != null) { await StateSemaphore.WaitAsync().ConfigureAwait(false);
return;
}
ASF.ArchiLogger.LogGenericInfo(Strings.IPCStarting);
// Init history logger for /Api/Log usage
Logging.InitHistoryLogger();
WebApplication webApplication = await CreateWebApplication().ConfigureAwait(false);
try { try {
// Start the server await StopInternally().ConfigureAwait(false);
await webApplication.StartAsync().ConfigureAwait(false); await StartInternally().ConfigureAwait(false);
} catch (Exception e) { } finally {
ASF.ArchiLogger.LogGenericException(e); StateSemaphore.Release();
}
await webApplication.DisposeAsync().ConfigureAwait(false); }
internal static async Task Start() {
if (IsRunning) {
return; return;
} }
WebApplication = webApplication; await StateSemaphore.WaitAsync().ConfigureAwait(false);
ASF.ArchiLogger.LogGenericInfo(Strings.IPCReady); try {
await StartInternally().ConfigureAwait(false);
} finally {
StateSemaphore.Release();
}
} }
internal static async Task Stop() { internal static async Task Stop() {
if (WebApplication == null) { if (!IsRunning) {
return; return;
} }
await WebApplication.StopAsync().ConfigureAwait(false); await StateSemaphore.WaitAsync().ConfigureAwait(false);
await WebApplication.DisposeAsync().ConfigureAwait(false);
WebApplication = null; try {
await StopInternally().ConfigureAwait(false);
} finally {
StateSemaphore.Release();
}
} }
[UnconditionalSuppressMessage("AssemblyLoadTrimming", "IL2026:RequiresUnreferencedCode", Justification = "PathString is a primitive, it's unlikely to be trimmed to the best of our knowledge")] [UnconditionalSuppressMessage("AssemblyLoadTrimming", "IL2026:RequiresUnreferencedCode", Justification = "PathString is a primitive, it's unlikely to be trimmed to the best of our knowledge")]
@@ -519,4 +522,43 @@ internal static class ArchiKestrel {
headers.CacheControl = cacheControl; headers.CacheControl = cacheControl;
} }
private static async Task StartInternally() {
if (WebApplication != null) {
return;
}
ASF.ArchiLogger.LogGenericInfo(Strings.IPCStarting);
// Init history logger for /Api/Log usage
Logging.InitHistoryLogger();
WebApplication webApplication = await CreateWebApplication().ConfigureAwait(false);
try {
// Start the server
await webApplication.StartAsync().ConfigureAwait(false);
} catch (Exception e) {
ASF.ArchiLogger.LogGenericException(e);
await webApplication.DisposeAsync().ConfigureAwait(false);
return;
}
WebApplication = webApplication;
ASF.ArchiLogger.LogGenericInfo(Strings.IPCReady);
}
private static async Task StopInternally() {
if (WebApplication == null) {
return;
}
await WebApplication.StopAsync().ConfigureAwait(false);
await WebApplication.DisposeAsync().ConfigureAwait(false);
WebApplication = null;
}
} }