forked from aspnet/AspNetWebStack
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathAsyncResultWrapper.cs
More file actions
364 lines (302 loc) · 15.2 KB
/
Copy pathAsyncResultWrapper.cs
File metadata and controls
364 lines (302 loc) · 15.2 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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Threading;
namespace System.Web.Mvc.Async
{
// This class is used for the following pattern:
// public IAsyncResult BeginInner(..., callback, state);
// public TInnerResult EndInner(asyncResult);
// public IAsyncResult BeginOuter(..., callback, state);
// public TOuterResult EndOuter(asyncResult);
// That is, Begin/EndOuter() wrap Begin/EndInner(), potentially with pre- and post-processing.
[DebuggerNonUserCode]
internal static class AsyncResultWrapper
{
// helper methods
private static readonly EndInvokeDelegate<Action, AsyncVoid> _voidEndInvoke = (IAsyncResult asyncResult, Action action) =>
{
action();
return default(AsyncVoid);
};
// kicks off an asynchronous operation
public static IAsyncResult Begin<TResult>(AsyncCallback callback, object state, BeginInvokeDelegate beginDelegate, EndInvokeDelegate<TResult> endDelegate, object tag = null, int timeout = Timeout.Infinite)
{
WrappedAsyncResult<TResult> asyncResult = new WrappedAsyncResult<TResult>(beginDelegate, endDelegate, tag, callbackSyncContext: null);
asyncResult.Begin(callback, state, timeout);
return asyncResult;
}
public static IAsyncResult Begin<TResult, TState>(AsyncCallback callback, object callbackState, BeginInvokeDelegate<TState> beginDelegate, EndInvokeDelegate<TState, TResult> endDelegate, TState invokeState, object tag = null, int timeout = Timeout.Infinite, SynchronizationContext callbackSyncContext = null)
{
WrappedAsyncResult<TResult, TState> asyncResult = new WrappedAsyncResult<TResult, TState>(beginDelegate, endDelegate, invokeState, tag, callbackSyncContext);
asyncResult.Begin(callback, callbackState, timeout);
return asyncResult;
}
public static IAsyncResult Begin<TState>(AsyncCallback callback, object callbackState, BeginInvokeDelegate<TState> beginDelegate, EndInvokeVoidDelegate<TState> endDelegate, TState invokeState, object tag = null, int timeout = Timeout.Infinite, SynchronizationContext callbackSyncContext = null)
{
WrappedAsyncVoid<TState> asyncResult = new WrappedAsyncVoid<TState>(beginDelegate, endDelegate, invokeState, tag, callbackSyncContext);
asyncResult.Begin(callback, callbackState, timeout);
return asyncResult;
}
// wraps a synchronous operation in an asynchronous wrapper, but still completes synchronously
public static IAsyncResult BeginSynchronous<TResult, TState>(AsyncCallback callback, object callbackState, EndInvokeDelegate<TState, TResult> func, TState funcState, object tag)
{
// Frequently called, so use static delegates
// Inline delegates that take a generic argument from a generic method don't get cached by the compiler so use a field from a static generic class
BeginInvokeDelegate<TState> beginDelegate = CachedDelegates<TState>.CompletedBeginInvoke;
// Pass in the blocking function as the End() method
WrappedAsyncResult<TResult, TState> asyncResult = new WrappedAsyncResult<TResult, TState>(beginDelegate, func, funcState, tag, callbackSyncContext: null);
asyncResult.Begin(callback, callbackState, Timeout.Infinite);
return asyncResult;
}
public static IAsyncResult BeginSynchronous(AsyncCallback callback, object state, Action action, object tag)
{
return BeginSynchronous<AsyncVoid, Action>(callback, state, _voidEndInvoke, action, tag);
}
// completes an asynchronous operation
public static TResult End<TResult>(IAsyncResult asyncResult)
{
return End<TResult>(asyncResult, tag: null);
}
public static TResult End<TResult>(IAsyncResult asyncResult, object tag)
{
return WrappedAsyncResultBase<TResult>.Cast(asyncResult, tag).End();
}
public static void End(IAsyncResult asyncResult)
{
End(asyncResult, tag: null);
}
public static void End(IAsyncResult asyncResult, object tag)
{
End<AsyncVoid>(asyncResult, tag);
}
private static class CachedDelegates<TState>
{
internal static BeginInvokeDelegate<TState> CompletedBeginInvoke = (AsyncCallback asyncCallback, object asyncState, TState invokeState) =>
{
// Begin() doesn't perform any work on its own and returns immediately.
SimpleAsyncResult innerAsyncResult = new SimpleAsyncResult(asyncState);
innerAsyncResult.MarkCompleted(completedSynchronously: true, callback: asyncCallback);
return innerAsyncResult;
};
}
[DebuggerNonUserCode]
[SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable", Justification = "The Timer will be disposed of either when it fires or when the operation completes successfully.")]
private abstract class WrappedAsyncResultBase<TResult> : IAsyncResult
{
private const int AsyncStateNone = 0;
private const int AsyncStateBeginUnwound = 1;
private const int AsyncStateCallbackFired = 2;
private int _asyncState;
private readonly object _beginDelegateLockObj = new object();
private readonly SingleEntryGate _endExecutedGate = new SingleEntryGate(); // prevent End() from being called twice
private readonly SingleEntryGate _handleCallbackGate = new SingleEntryGate(); // prevent callback from being handled multiple times
private readonly object _tag; // prevent an instance of this type from being passed to the wrong End() method
private IAsyncResult _innerAsyncResult;
private AsyncCallback _originalCallback;
private volatile bool _timedOut;
private Timer _timer;
private readonly SynchronizationContext _callbackSyncContext;
protected WrappedAsyncResultBase(object tag, SynchronizationContext callbackSyncContext)
{
_tag = tag;
_callbackSyncContext = callbackSyncContext;
}
public object AsyncState
{
get { return _innerAsyncResult.AsyncState; }
}
public WaitHandle AsyncWaitHandle
{
get { return null; }
}
public bool CompletedSynchronously { get; private set; }
public bool IsCompleted
{
get { return _timedOut || _innerAsyncResult.IsCompleted; }
}
// kicks off the process, instantiates a timer if requested
public void Begin(AsyncCallback callback, object state, int timeout)
{
_originalCallback = callback;
// Force the target Begin() operation to complete before the callback can continue,
// since the target operation might perform post-processing of the data.
lock (_beginDelegateLockObj)
{
_innerAsyncResult = CallBeginDelegate(HandleAsynchronousCompletion, state);
// If the callback has already fired, then the completion routine has no-oped and we
// can just treat this as if it were a normal synchronous completion.
int originalState = Interlocked.Exchange(ref _asyncState, AsyncStateBeginUnwound);
bool callbackAlreadyFired = (originalState == AsyncStateCallbackFired);
CompletedSynchronously = callbackAlreadyFired || _innerAsyncResult.CompletedSynchronously;
if (!CompletedSynchronously)
{
if (timeout > Timeout.Infinite)
{
CreateTimer(timeout);
}
}
}
if (CompletedSynchronously)
{
if (callback != null)
{
callback(this);
}
}
}
protected abstract IAsyncResult CallBeginDelegate(AsyncCallback callback, object callbackState);
protected abstract TResult CallEndDelegate(IAsyncResult asyncResult);
public static WrappedAsyncResultBase<TResult> Cast(IAsyncResult asyncResult, object tag)
{
if (asyncResult == null)
{
throw new ArgumentNullException("asyncResult");
}
WrappedAsyncResultBase<TResult> castResult = asyncResult as WrappedAsyncResultBase<TResult>;
if (castResult != null && Equals(castResult._tag, tag))
{
return castResult;
}
else
{
throw Error.AsyncCommon_InvalidAsyncResult("asyncResult");
}
}
private void CallbackUsingSyncContext()
{
_callbackSyncContext.Sync(() => _originalCallback(this));
}
private void CreateTimer(int timeout)
{
// this method should be called within a lock(_beginDelegateLockObj)
_timer = new Timer(HandleTimeout, null, timeout, Timeout.Infinite /* disable periodic signaling */);
}
public TResult End()
{
if (!_endExecutedGate.TryEnter())
{
throw Error.AsyncCommon_AsyncResultAlreadyConsumed();
}
if (_timedOut)
{
throw new TimeoutException();
}
WaitForBeginToCompleteAndDestroyTimer();
return CallEndDelegate(_innerAsyncResult);
}
private void ExecuteAsynchronousCallback(bool timedOut)
{
WaitForBeginToCompleteAndDestroyTimer();
if (_handleCallbackGate.TryEnter())
{
_timedOut = timedOut;
if (_originalCallback != null)
{
if (_callbackSyncContext != null)
{
CallbackUsingSyncContext();
}
else
{
_originalCallback(this);
}
}
}
}
private void HandleAsynchronousCompletion(IAsyncResult asyncResult)
{
// Transition the async state to CALLBACK_FIRED. If the Begin* method hasn't yet unwound,
// then we can no-op here since the Begin method will query the _asyncState field and
// treat this as a regular synchronous completion.
int originalState = Interlocked.Exchange(ref _asyncState, AsyncStateCallbackFired);
if (originalState != AsyncStateBeginUnwound)
{
return;
}
ExecuteAsynchronousCallback(timedOut: false);
}
private void HandleTimeout(object state)
{
ExecuteAsynchronousCallback(timedOut: true);
}
private void WaitForBeginToCompleteAndDestroyTimer()
{
lock (_beginDelegateLockObj)
{
// Wait for the target Begin() method to complete, as it might be performing
// post-processing. This also forces a memory barrier, so _innerAsyncResult
// is guaranteed to be non-null at this point.
if (_timer != null)
{
_timer.Dispose();
}
_timer = null;
}
}
}
private sealed class WrappedAsyncResult<TResult> : WrappedAsyncResultBase<TResult>
{
private readonly BeginInvokeDelegate _beginDelegate;
private readonly EndInvokeDelegate<TResult> _endDelegate;
public WrappedAsyncResult(BeginInvokeDelegate beginDelegate, EndInvokeDelegate<TResult> endDelegate, object tag, SynchronizationContext callbackSyncContext)
: base(tag, callbackSyncContext)
{
_beginDelegate = beginDelegate;
_endDelegate = endDelegate;
}
protected override IAsyncResult CallBeginDelegate(AsyncCallback callback, object callbackState)
{
return _beginDelegate(callback, callbackState);
}
protected override TResult CallEndDelegate(IAsyncResult asyncResult)
{
return _endDelegate(asyncResult);
}
}
private sealed class WrappedAsyncResult<TResult, TState> : WrappedAsyncResultBase<TResult>
{
private readonly BeginInvokeDelegate<TState> _beginDelegate;
private readonly EndInvokeDelegate<TState, TResult> _endDelegate;
private readonly TState _state;
public WrappedAsyncResult(BeginInvokeDelegate<TState> beginDelegate, EndInvokeDelegate<TState, TResult> endDelegate, TState state, object tag, SynchronizationContext callbackSyncContext)
: base(tag, callbackSyncContext)
{
_beginDelegate = beginDelegate;
_endDelegate = endDelegate;
_state = state;
}
protected override TResult CallEndDelegate(IAsyncResult asyncResult)
{
return _endDelegate(asyncResult, _state);
}
protected override IAsyncResult CallBeginDelegate(AsyncCallback callback, object callbackState)
{
return _beginDelegate(callback, callbackState, _state);
}
}
private sealed class WrappedAsyncVoid<TState> : WrappedAsyncResultBase<AsyncVoid>
{
private readonly BeginInvokeDelegate<TState> _beginDelegate;
private readonly EndInvokeVoidDelegate<TState> _endDelegate;
private readonly TState _state;
public WrappedAsyncVoid(BeginInvokeDelegate<TState> beginDelegate, EndInvokeVoidDelegate<TState> endDelegate, TState state, object tag, SynchronizationContext callbackSyncContext)
: base(tag, callbackSyncContext)
{
_beginDelegate = beginDelegate;
_endDelegate = endDelegate;
_state = state;
}
protected override AsyncVoid CallEndDelegate(IAsyncResult asyncResult)
{
_endDelegate(asyncResult, _state);
return default(AsyncVoid);
}
protected override IAsyncResult CallBeginDelegate(AsyncCallback callback, object callbackState)
{
return _beginDelegate(callback, callbackState, _state);
}
}
}
}