< Summary - AsyncResponse (Release / net8.0+net10.0 / unit+integration)

Information
Class: AsyncResponse.WorkerJobSkewScope
Assembly: AsyncResponse.Core
File(s): /_/src/AsyncResponse.Core/WorkerJobSkewScope.cs
Line coverage
93%
Covered lines: 14
Uncovered lines: 1
Coverable lines: 15
Total lines: 74
Line coverage: 93.3%
Branch coverage
87%
Covered branches: 7
Total branches: 8
Branch coverage: 87.5%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
get_IsForcedEarlyExecution()100%22100%
TryConsumeForcedEarlyExecution()100%44100%
Enter()100%11100%
.ctor(...)100%11100%
Dispose()50%2280%

File(s)

/_/src/AsyncResponse.Core/WorkerJobSkewScope.cs

#LineLine coverage
 1namespace AsyncResponse;
 2
 3/// <summary>
 4/// Marks the currently-executing worker job as one the shared executor released EARLY because
 5/// consecutive re-delay hops proved the publishing and delivery-gating clocks disagree
 6/// (see <c>WorkerJobExecutor</c>'s redelay stall guard).
 7/// <para>
 8/// Durable timers need to know. A timer step that suspends mints a NEW wake-up envelope, and a
 9/// fresh envelope carries no stall evidence — so under persistent skew the run would suspend, be
 10/// handed back early, rebuild the proof from scratch, execute, suspend again, and never finish:
 11/// the guard's own state is erased on every lap. While this marker is set, the timer waits in
 12/// process for the remainder instead, which honors the due time without minting an envelope that
 13/// forgets what the previous hops established.
 14/// </para>
 15/// <para>
 16/// The proof is ONE-SHOT: the wake-up that carried it targets a single parked timer step, and
 17/// only that step may spend it (<see cref="TryConsumeForcedEarlyExecution"/>). An unrelated later
 18/// timer reached in the same replay suspends normally — its own wake-up rebuilds the stall
 19/// evidence if the skew persists — instead of inheriting an exemption that would pin it in
 20/// process for its full remainder or fail it on the timer ceiling.
 21/// </para>
 22/// </summary>
 23internal static class WorkerJobSkewScope
 24{
 225    private static readonly AsyncLocal<Marker?> _forcedEarly = new();
 26
 27    /// <summary>Whether the job executing on this async flow was released early by the stall guard.</summary>
 828    public static bool IsForcedEarlyExecution => _forcedEarly.Value is { Consumed: false };
 29
 30    /// <summary>
 31    /// Claims the marker for the calling step: returns <c>true</c> exactly once per
 32    /// <see cref="Enter"/>, then <c>false</c> for every later caller on the same job.
 33    /// </summary>
 34    public static bool TryConsumeForcedEarlyExecution()
 35    {
 8036        if (_forcedEarly.Value is not { Consumed: false } marker)
 7437            return false;
 38
 639        marker.Consumed = true;
 640        return true;
 41    }
 42
 43    /// <summary>Marks the current job as force-executed until the returned scope is disposed.</summary>
 44    public static IDisposable Enter()
 45    {
 846        var previous = _forcedEarly.Value;
 847        _forcedEarly.Value = new Marker();
 848        return new Scope(previous);
 49    }
 50
 51    /// <summary>
 52    /// Heap cell shared by every continuation of the job: an <see cref="AsyncLocal{T}"/> VALUE
 53    /// written inside an async callee never flows back to its caller, so consumption mutates the
 54    /// referenced marker instead of the ambient slot.
 55    /// </summary>
 56    private sealed class Marker
 57    {
 58        public bool Consumed;
 59    }
 60
 861    private sealed class Scope(Marker? _previous) : IDisposable
 62    {
 63        private bool _disposed;
 64
 65        public void Dispose()
 66        {
 867            if (_disposed)
 068                return;
 69
 870            _disposed = true;
 871            _forcedEarly.Value = _previous;
 872        }
 73    }
 74}