Repository navigation
Expand file tree
/
Copy pathRequestWorkOwner.cs
More file actions
143 lines (121 loc) · 4.89 KB
/
Copy pathRequestWorkOwner.cs
File metadata and controls
143 lines (121 loc) · 4.89 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
using System.Collections.Immutable;
using Microsoft.Extensions.Options;
namespace KeyLoad.Clustering;
/// <summary>Owns bounded active execution request producers and verified capability frames for one silo.</summary>
public sealed class RequestWorkOwner : IAsyncDisposable
{
private const int GetIsJoinedEmptyRead = 0;
private const int Joined = 1;
private readonly Lock gate = new();
private readonly CancellationTokenSource shutdown = new();
private readonly CancellationToken shutdownToken;
private readonly RequestWorkAdmissions admissions;
private Task? drainTask;
private Task? disposeTask;
private bool admissionClosed;
private int isJoined;
/// <summary>Creates an empty silo-local work registry.</summary>
/// <param name="options">The centrally validated request and capability admission limits.</param>
public RequestWorkOwner(IOptions<GrainRoutingOptions> options)
{
ArgumentNullException.ThrowIfNull(options);
admissions = new(options);
shutdownToken = shutdown.Token;
}
/// <summary>Gets the owner cancellation token linked by admitted work.</summary>
internal CancellationToken ShutdownToken => shutdownToken;
/// <summary>Gets whether cancellation and every originally admitted frame have settled.</summary>
internal bool IsJoined => Volatile.Read(ref isJoined) != GetIsJoinedEmptyRead;
/// <summary>Admits one unique request frame while the owner is open.</summary>
/// <param name="requestId">The execution request identity.</param>
/// <param name="kind">The closed frame class.</param>
/// <returns>The lease which releases this exact admission.</returns>
internal RequestWorkLease Acquire(Guid requestId, RequestWorkKind kind)
{
if (requestId == Guid.Empty || !Enum.IsDefined(kind))
{
throw Errors.Fail(ErrorCode.Validation, RequestWorkLimits.InvalidIdentityMessage);
}
lock (gate)
{
if (admissionClosed)
{
throw Errors.Fail(ErrorCode.OwnershipLost, RequestWorkLimits.AdmissionClosedMessage);
}
return admissions.Acquire(this, requestId, kind);
}
}
/// <summary>Closes admission, cancels work, and returns the shared original drain task.</summary>
/// <returns>The same task to every caller.</returns>
internal Task DrainAsync()
=> RequestWorkDrain.Start(gate, ref drainTask, ref admissionClosed, shutdown, admissions, this);
internal void MarkJoined() => Volatile.Write(ref isJoined, Joined);
/// <summary>Returns the one disposal task through the asynchronous-disposal contract.</summary>
public ValueTask DisposeAsync() => new(GetDisposeTask());
private Task GetDisposeTask()
{
TaskCompletionSource start;
Task task;
lock (gate)
{
if (disposeTask is not null)
{
return disposeTask;
}
start = new(TaskCreationOptions.RunContinuationsAsynchronously);
task = DisposeCoreAsync(start.Task);
disposeTask = task;
}
start.TrySetResult();
return task;
}
private async Task DisposeCoreAsync(Task start)
{
await start;
var drainFailure = await RequestWorkSettlement.JoinAsync(DrainAsync());
Exception? disposeFailure = null;
try
{
shutdown.Dispose();
}
catch (Exception error) when (CqrsBoundaryErrors.IsNonFatal(error))
{
disposeFailure = error;
}
catch (Exception error) when (!CqrsBoundaryErrors.IsNonFatal(error))
{
disposeFailure = error;
}
RequestWorkSettlement.Rethrow(RequestWorkSettlement.Preserve(drainFailure, disposeFailure));
}
internal void Release(RequestWorkLease lease)
{
lock (gate)
{
admissions.Release(lease);
}
}
internal Task PrepareCompletionAsync(RequestWorkLease lease)
=> RequestWorkCompletion.PrepareAsync(gate, lease);
internal Task<RequestWorkOriginalSettlement> CompleteAsync(RequestWorkLease lease,
Exception? original, Exception? cleanup)
=> RequestWorkCompletion.CompleteAsync(gate, lease, original, cleanup);
internal bool TryBeginUnbound(RequestWorkLease lease)
=> RequestWorkCompletion.TryBeginUnbound(gate, lease);
internal void PrepareDisposal(RequestWorkLease lease, Exception? original, Exception? cleanup)
=> RequestWorkCompletion.PrepareDisposal(gate, lease, original, cleanup);
internal void Bind(RequestWorkLease lease, ImmutableArray<ResourceLifetime> lifetimes)
{
lock (gate)
{
admissions.Bind(lease, lifetimes);
}
}
internal void AdmitPublication(RequestWorkLease lease)
{
lock (gate)
{
admissions.AdmitPublication(lease);
}
}
}