using System.Diagnostics;
namespace ZymonicServices;
/// Call from a background timer. At most one callback may be waiting on the dispatcher.
public sealed class ZymonicDispatcherProbe
{
private readonly Action _post;
private int _pending;
private int _generation;
public ZymonicDispatcherProbe(Action post) => _post = post;
// Keep an outstanding callback outstanding, but discard its timing after suspension or disabling.
public void Invalidate() => Interlocked.Increment(ref _generation);
public void Poll(ZymonicPerformanceRecorder recorder)
{
if (Interlocked.CompareExchange(ref _pending, 1, 0) == 0)
{
int generation = Volatile.Read(ref _generation);
long started = Stopwatch.GetTimestamp();
try
{
_post(() =>
{
try
{
if (generation == Volatile.Read(ref _generation))
{
recorder.RecordElapsed("ui.dispatch", started);
}
}
finally
{
Volatile.Write(ref _pending, 0);
}
});
}
catch
{
// A dispatcher can reject a post during shutdown. Diagnostics must not crash the app.
Volatile.Write(ref _pending, 0);
}
}
}
}