fix(adminui): /hosts timer dispose-race hardening + IAsyncDisposable parity with DriverStatusPanel (review)
This commit is contained in:
@@ -18,7 +18,7 @@
|
||||
@inject IDriverStatusSnapshotStore DriverStore
|
||||
@inject IDbContextFactory<OtOpcUaConfigDbContext> DbFactory
|
||||
@inject Microsoft.Extensions.Logging.ILogger<Hosts> Logger
|
||||
@implements IDisposable
|
||||
@implements IAsyncDisposable
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h4 class="mb-0">Cluster hosts</h4>
|
||||
@@ -226,12 +226,20 @@ else
|
||||
await LoadConfigAsync();
|
||||
RebuildDriverGroups();
|
||||
DriverStore.SnapshotChanged += OnSnapshotChanged;
|
||||
_timer = new Timer(_ => InvokeAsync(async () =>
|
||||
_timer = new Timer(_ => _ = InvokeAsync(async () =>
|
||||
{
|
||||
Refresh();
|
||||
await LoadConfigAsync();
|
||||
RebuildDriverGroups();
|
||||
StateHasChanged();
|
||||
try
|
||||
{
|
||||
Refresh();
|
||||
await LoadConfigAsync();
|
||||
RebuildDriverGroups();
|
||||
StateHasChanged();
|
||||
}
|
||||
catch (Exception ex) when (ex is ObjectDisposedException or OperationCanceledException)
|
||||
{
|
||||
// Circuit disposed while a tick was in flight — ignore (the discarded task would
|
||||
// otherwise swallow this silently). Mirrors DriverStatusPanel's drain-on-dispose.
|
||||
}
|
||||
}), null,
|
||||
TimeSpan.FromSeconds(RefreshIntervalSeconds),
|
||||
TimeSpan.FromSeconds(RefreshIntervalSeconds));
|
||||
@@ -337,10 +345,13 @@ else
|
||||
_ => "chip-idle",
|
||||
};
|
||||
|
||||
public void Dispose()
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
_timer?.Dispose();
|
||||
// Unsubscribe first so the singleton store can't invoke a handler on a disposed component.
|
||||
DriverStore.SnapshotChanged -= OnSnapshotChanged;
|
||||
// Drain the timer so an in-flight callback can't touch a component that's already gone
|
||||
// (System.Threading.Timer's async dispose awaits any in-flight callback — .NET 6+).
|
||||
if (_timer is not null) await _timer.DisposeAsync();
|
||||
}
|
||||
|
||||
private sealed record MemberRow(
|
||||
|
||||
Reference in New Issue
Block a user