mirror of
https://github.com/alexhopeoconnor/dnsmasq-webui.git
synced 2026-10-03 22:12:00 +10:00
Stop log background loops cleanly during shutdown.
Cancel the hosted-service watcher loops before the service provider is disposed so file-change and app-log pushes do not try to create scopes after shutdown begins.
This commit is contained in:
@@ -17,6 +17,8 @@ public sealed class AppLogsPushTriggeredHostedService : IApplicationHostedServic
|
||||
private readonly Channel<byte> _channel;
|
||||
private readonly ILogsService _logsService;
|
||||
private readonly ILogger<AppLogsPushTriggeredHostedService> _logger;
|
||||
private CancellationTokenSource? _stopCts;
|
||||
private Task? _runLoopTask;
|
||||
|
||||
public AppLogsPushTriggeredHostedService(
|
||||
Channel<byte> appLogsPushChannel,
|
||||
@@ -30,11 +32,35 @@ public sealed class AppLogsPushTriggeredHostedService : IApplicationHostedServic
|
||||
|
||||
public Task StartAsync(CancellationToken ct)
|
||||
{
|
||||
_ = RunLoopAsync(ct);
|
||||
_stopCts = CancellationTokenSource.CreateLinkedTokenSource(ct);
|
||||
_runLoopTask = RunLoopAsync(_stopCts.Token);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task StopAsync(CancellationToken ct) => Task.CompletedTask;
|
||||
public async Task StopAsync(CancellationToken ct)
|
||||
{
|
||||
var stopCts = _stopCts;
|
||||
_stopCts = null;
|
||||
if (stopCts != null)
|
||||
{
|
||||
try { stopCts.Cancel(); }
|
||||
finally { stopCts.Dispose(); }
|
||||
}
|
||||
|
||||
var runLoopTask = _runLoopTask;
|
||||
_runLoopTask = null;
|
||||
if (runLoopTask == null)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
await runLoopTask;
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// expected during shutdown
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RunLoopAsync(CancellationToken ct)
|
||||
{
|
||||
|
||||
@@ -21,6 +21,8 @@ public sealed class LogsFileWatcherHostedService : IApplicationHostedService
|
||||
private FileSystemWatcher? _watcher;
|
||||
private string? _currentPath;
|
||||
private readonly object _lock = new();
|
||||
private CancellationTokenSource? _stopCts;
|
||||
private Task? _runLoopTask;
|
||||
|
||||
public LogsFileWatcherHostedService(
|
||||
IDnsmasqConfigSetService configSetService,
|
||||
@@ -34,11 +36,41 @@ public sealed class LogsFileWatcherHostedService : IApplicationHostedService
|
||||
|
||||
public Task StartAsync(CancellationToken ct)
|
||||
{
|
||||
_ = RunLoopAsync(ct);
|
||||
_stopCts = CancellationTokenSource.CreateLinkedTokenSource(ct);
|
||||
_runLoopTask = RunLoopAsync(_stopCts.Token);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task StopAsync(CancellationToken ct) => Task.CompletedTask;
|
||||
public async Task StopAsync(CancellationToken ct)
|
||||
{
|
||||
var stopCts = _stopCts;
|
||||
_stopCts = null;
|
||||
if (stopCts != null)
|
||||
{
|
||||
try { stopCts.Cancel(); }
|
||||
finally { stopCts.Dispose(); }
|
||||
}
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
DisposeWatcherLocked();
|
||||
_currentPath = null;
|
||||
}
|
||||
|
||||
var runLoopTask = _runLoopTask;
|
||||
_runLoopTask = null;
|
||||
if (runLoopTask == null)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
await runLoopTask;
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// expected during shutdown
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RunLoopAsync(CancellationToken ct)
|
||||
{
|
||||
@@ -67,8 +99,7 @@ public sealed class LogsFileWatcherHostedService : IApplicationHostedService
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
_watcher?.Dispose();
|
||||
_watcher = null;
|
||||
DisposeWatcherLocked();
|
||||
_currentPath = null;
|
||||
}
|
||||
}
|
||||
@@ -82,8 +113,7 @@ public sealed class LogsFileWatcherHostedService : IApplicationHostedService
|
||||
if (string.Equals(_currentPath, normalized, StringComparison.Ordinal))
|
||||
return;
|
||||
|
||||
_watcher?.Dispose();
|
||||
_watcher = null;
|
||||
DisposeWatcherLocked();
|
||||
_currentPath = normalized;
|
||||
|
||||
if (string.IsNullOrEmpty(normalized))
|
||||
@@ -126,18 +156,35 @@ public sealed class LogsFileWatcherHostedService : IApplicationHostedService
|
||||
|
||||
private void OnFileChanged(object sender, FileSystemEventArgs e)
|
||||
{
|
||||
_ = InvokePushAsync();
|
||||
var ct = _stopCts?.Token ?? CancellationToken.None;
|
||||
if (ct.IsCancellationRequested)
|
||||
return;
|
||||
_ = InvokePushAsync(ct);
|
||||
}
|
||||
|
||||
private async Task InvokePushAsync()
|
||||
private async Task InvokePushAsync(CancellationToken ct)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _logsService.RunAndPushDnsmasqLogsAsync(CancellationToken.None);
|
||||
await _logsService.RunAndPushDnsmasqLogsAsync(ct);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// expected during shutdown
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogDebug(ex, "Error pushing logs on file change");
|
||||
}
|
||||
}
|
||||
|
||||
private void DisposeWatcherLocked()
|
||||
{
|
||||
if (_watcher == null)
|
||||
return;
|
||||
|
||||
_watcher.Changed -= OnFileChanged;
|
||||
_watcher.Dispose();
|
||||
_watcher = null;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user