92171a01ec
Review 07 H13: _devices and _tagsByRawPath were plain dictionaries cleared and refilled in place while the retained poll loop, the actor's GetHostStatuses and server reads enumerated them. Both are now built locally and published by reference under a lifecycle lock; teardown swaps in empty tables. Lane A contract: ShutdownAsync and Dispose each retire a disjoint set of device states, so Dispose running concurrently with a still-joining ShutdownAsync is safe. A lifecycle epoch makes an InitializeAsync that the runtime abandoned (overlapping the next ShutdownAsync) refuse to publish its tables, start probe loops or write health. A runtime cached on an already-retired device state by an in-flight read is disposed instead of leaked, and late reads/writes no longer overwrite shutdown's health. Per-key semaphores are no longer disposed under callers that may still hold them. Refs #738 Claude-Session: https://claude.ai/code/session_01Ub9hGmYLQvn9XETzw7CMsT
24 lines
1.0 KiB
C#
24 lines
1.0 KiB
C#
using Microsoft.Extensions.Logging;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests;
|
|
|
|
/// <summary>Thread-safe capturing logger shared by the remediation tests (poll loops log from pool threads).</summary>
|
|
internal sealed class AbLegacyTestLogger : ILogger<AbLegacyDriver>
|
|
{
|
|
/// <summary>Every entry logged, in order.</summary>
|
|
public System.Collections.Concurrent.ConcurrentQueue<(LogLevel Level, string Message)> Entries { get; } = new();
|
|
|
|
/// <summary>The Warning-level messages logged.</summary>
|
|
public IReadOnlyList<string> Warnings => [.. Entries.Where(e => e.Level == LogLevel.Warning).Select(e => e.Message)];
|
|
|
|
/// <inheritdoc />
|
|
public IDisposable? BeginScope<TState>(TState state) where TState : notnull => null;
|
|
|
|
/// <inheritdoc />
|
|
public bool IsEnabled(LogLevel logLevel) => true;
|
|
|
|
/// <inheritdoc />
|
|
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception,
|
|
Func<TState, Exception?, string> formatter) => Entries.Enqueue((logLevel, formatter(state, exception)));
|
|
}
|