mirror of
https://github.com/alexhopeoconnor/dnsmasq-webui.git
synced 2026-10-04 04:18:13 +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 Channel<byte> _channel;
|
||||||
private readonly ILogsService _logsService;
|
private readonly ILogsService _logsService;
|
||||||
private readonly ILogger<AppLogsPushTriggeredHostedService> _logger;
|
private readonly ILogger<AppLogsPushTriggeredHostedService> _logger;
|
||||||
|
private CancellationTokenSource? _stopCts;
|
||||||
|
private Task? _runLoopTask;
|
||||||
|
|
||||||
public AppLogsPushTriggeredHostedService(
|
public AppLogsPushTriggeredHostedService(
|
||||||
Channel<byte> appLogsPushChannel,
|
Channel<byte> appLogsPushChannel,
|
||||||
@@ -30,11 +32,35 @@ public sealed class AppLogsPushTriggeredHostedService : IApplicationHostedServic
|
|||||||
|
|
||||||
public Task StartAsync(CancellationToken ct)
|
public Task StartAsync(CancellationToken ct)
|
||||||
{
|
{
|
||||||
_ = RunLoopAsync(ct);
|
_stopCts = CancellationTokenSource.CreateLinkedTokenSource(ct);
|
||||||
|
_runLoopTask = RunLoopAsync(_stopCts.Token);
|
||||||
return Task.CompletedTask;
|
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)
|
private async Task RunLoopAsync(CancellationToken ct)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ public sealed class LogsFileWatcherHostedService : IApplicationHostedService
|
|||||||
private FileSystemWatcher? _watcher;
|
private FileSystemWatcher? _watcher;
|
||||||
private string? _currentPath;
|
private string? _currentPath;
|
||||||
private readonly object _lock = new();
|
private readonly object _lock = new();
|
||||||
|
private CancellationTokenSource? _stopCts;
|
||||||
|
private Task? _runLoopTask;
|
||||||
|
|
||||||
public LogsFileWatcherHostedService(
|
public LogsFileWatcherHostedService(
|
||||||
IDnsmasqConfigSetService configSetService,
|
IDnsmasqConfigSetService configSetService,
|
||||||
@@ -34,11 +36,41 @@ public sealed class LogsFileWatcherHostedService : IApplicationHostedService
|
|||||||
|
|
||||||
public Task StartAsync(CancellationToken ct)
|
public Task StartAsync(CancellationToken ct)
|
||||||
{
|
{
|
||||||
_ = RunLoopAsync(ct);
|
_stopCts = CancellationTokenSource.CreateLinkedTokenSource(ct);
|
||||||
|
_runLoopTask = RunLoopAsync(_stopCts.Token);
|
||||||
return Task.CompletedTask;
|
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)
|
private async Task RunLoopAsync(CancellationToken ct)
|
||||||
{
|
{
|
||||||
@@ -67,8 +99,7 @@ public sealed class LogsFileWatcherHostedService : IApplicationHostedService
|
|||||||
|
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
_watcher?.Dispose();
|
DisposeWatcherLocked();
|
||||||
_watcher = null;
|
|
||||||
_currentPath = null;
|
_currentPath = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -82,8 +113,7 @@ public sealed class LogsFileWatcherHostedService : IApplicationHostedService
|
|||||||
if (string.Equals(_currentPath, normalized, StringComparison.Ordinal))
|
if (string.Equals(_currentPath, normalized, StringComparison.Ordinal))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_watcher?.Dispose();
|
DisposeWatcherLocked();
|
||||||
_watcher = null;
|
|
||||||
_currentPath = normalized;
|
_currentPath = normalized;
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(normalized))
|
if (string.IsNullOrEmpty(normalized))
|
||||||
@@ -126,18 +156,35 @@ public sealed class LogsFileWatcherHostedService : IApplicationHostedService
|
|||||||
|
|
||||||
private void OnFileChanged(object sender, FileSystemEventArgs e)
|
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
|
try
|
||||||
{
|
{
|
||||||
await _logsService.RunAndPushDnsmasqLogsAsync(CancellationToken.None);
|
await _logsService.RunAndPushDnsmasqLogsAsync(ct);
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
// expected during shutdown
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.LogDebug(ex, "Error pushing logs on file change");
|
_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