| | | 1 | | namespace AsyncResponse; |
| | | 2 | | |
| | | 3 | | /// <summary> |
| | | 4 | | /// The worker job executing on the current async flow, published by <c>WorkerJobExecutor</c> for |
| | | 5 | | /// the duration of the invocation. |
| | | 6 | | /// <para> |
| | | 7 | | /// The durable-flow executor needs the job's identity (<see cref="WorkerJobEnvelope.JobId"/>), and |
| | | 8 | | /// the envelope itself, to handle one case the invoked method cannot see from its arguments: the |
| | | 9 | | /// broker redelivering the very job whose handler is still running, because an in-flight ceiling |
| | | 10 | | /// lapsed under it. That delivery finds the execution lease held by its own first delivery; it is |
| | | 11 | | /// the only copy of the run's wake-up the broker still has, so it must be recognised (the lease |
| | | 12 | | /// records the job) and, where the transport can delay, re-published as the SAME job. |
| | | 13 | | /// </para> |
| | | 14 | | /// <para> |
| | | 15 | | /// Entered by the frame that awaits the invocation, and entered for EVERY job — including one |
| | | 16 | | /// that carries no id. An <see cref="AsyncLocal{T}"/> value written inside an async callee never |
| | | 17 | | /// flows back to its caller, so the executor cannot delegate this to a helper; and the in-memory |
| | | 18 | | /// transport runs a job under its ENQUEUER's captured execution context, so a follow-up job would |
| | | 19 | | /// otherwise inherit the ambient job of whichever handler published it. |
| | | 20 | | /// </para> |
| | | 21 | | /// </summary> |
| | | 22 | | internal static class WorkerJobScope |
| | | 23 | | { |
| | 15 | 24 | | private static readonly AsyncLocal<WorkerJobEnvelope?> _current = new(); |
| | | 25 | | |
| | | 26 | | /// <summary>The job executing on this async flow, or <c>null</c> outside a worker job.</summary> |
| | 2033 | 27 | | public static WorkerJobEnvelope? Current => _current.Value; |
| | | 28 | | |
| | | 29 | | /// <summary>Publishes <paramref name="job"/> as the executing job until the returned scope is disposed.</summary> |
| | | 30 | | public static IDisposable Enter(WorkerJobEnvelope job) |
| | | 31 | | { |
| | 6288 | 32 | | var previous = _current.Value; |
| | 6288 | 33 | | _current.Value = job; |
| | 6288 | 34 | | return new Scope(previous); |
| | | 35 | | } |
| | | 36 | | |
| | 6288 | 37 | | private sealed class Scope(WorkerJobEnvelope? _previous) : IDisposable |
| | | 38 | | { |
| | | 39 | | private bool _disposed; |
| | | 40 | | |
| | | 41 | | public void Dispose() |
| | | 42 | | { |
| | 6274 | 43 | | if (_disposed) |
| | 0 | 44 | | return; |
| | | 45 | | |
| | 6274 | 46 | | _disposed = true; |
| | 6274 | 47 | | _current.Value = _previous; |
| | 6274 | 48 | | } |
| | | 49 | | } |
| | | 50 | | } |