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
|
namespace System.Web.Mvc.Async {
using System;
using System.Diagnostics.CodeAnalysis;
using System.Threading;
// 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.
internal static class AsyncResultWrapper {
// helper methods
private static Func<AsyncVoid> MakeVoidDelegate(Action action) {
return () =>
{
action();
return default(AsyncVoid);
};
}
private static EndInvokeDelegate<AsyncVoid> MakeVoidDelegate(EndInvokeDelegate endDelegate) {
return ar =>
{
endDelegate(ar);
return default(AsyncVoid);
};
}
// kicks off an asynchronous operation
public static IAsyncResult Begin<TResult>(AsyncCallback callback, object state, BeginInvokeDelegate beginDelegate, EndInvokeDelegate<TResult> endDelegate) {
return Begin<TResult>(callback, state, beginDelegate, endDelegate, null /* tag */);
}
public static IAsyncResult Begin<TResult>(AsyncCallback callback, object state, BeginInvokeDelegate beginDelegate, EndInvokeDelegate<TResult> endDelegate, object tag) {
return Begin<TResult>(callback, state, beginDelegate, endDelegate, tag, Timeout.Infinite);
}
public static IAsyncResult Begin<TResult>(AsyncCallback callback, object state, BeginInvokeDelegate beginDelegate, EndInvokeDelegate<TResult> endDelegate, object tag, int timeout) {
WrappedAsyncResult<TResult> asyncResult = new WrappedAsyncResult<TResult>(beginDelegate, endDelegate, tag);
asyncResult.Begin(callback, state, timeout);
return asyncResult;
}
public static IAsyncResult Begin(AsyncCallback callback, object state, BeginInvokeDelegate beginDelegate, EndInvokeDelegate endDelegate) {
return Begin(callback, state, beginDelegate, endDelegate, null /* tag */);
}
public static IAsyncResult Begin(AsyncCallback callback, object state, BeginInvokeDelegate beginDelegate, EndInvokeDelegate endDelegate, object tag) {
return Begin(callback, state, beginDelegate, endDelegate, tag, Timeout.Infinite);
}
public static IAsyncResult Begin(AsyncCallback callback, object state, BeginInvokeDelegate beginDelegate, EndInvokeDelegate endDelegate, object tag, int timeout) {
return Begin<AsyncVoid>(callback, state, beginDelegate, MakeVoidDelegate(endDelegate), tag, timeout);
}
// wraps a synchronous operation in an asynchronous wrapper, but still completes synchronously
public static IAsyncResult BeginSynchronous<TResult>(AsyncCallback callback, object state, Func<TResult> func) {
return BeginSynchronous<TResult>(callback, state, func, null /* tag */);
}
public static IAsyncResult BeginSynchronous<TResult>(AsyncCallback callback, object state, Func<TResult> func, object tag) {
// Begin() doesn't perform any work on its own and returns immediately.
BeginInvokeDelegate beginDelegate = (asyncCallback, asyncState) =>
{
SimpleAsyncResult innerAsyncResult = new SimpleAsyncResult(asyncState);
innerAsyncResult.MarkCompleted(true /* completedSynchronously */, asyncCallback);
return innerAsyncResult;
};
// The End() method blocks.
EndInvokeDelegate<TResult> endDelegate = _ =>
{
return func();
};
WrappedAsyncResult<TResult> asyncResult = new WrappedAsyncResult<TResult>(beginDelegate, endDelegate, tag);
asyncResult.Begin(callback, state, Timeout.Infinite);
return asyncResult;
}
public static IAsyncResult BeginSynchronous(AsyncCallback callback, object state, Action action) {
return BeginSynchronous(callback, state, action, null /* tag */);
}
public static IAsyncResult BeginSynchronous(AsyncCallback callback, object state, Action action, object tag) {
return BeginSynchronous<AsyncVoid>(callback, state, MakeVoidDelegate(action), tag);
}
// completes an asynchronous operation
public static TResult End<TResult>(IAsyncResult asyncResult) {
return End<TResult>(asyncResult, null /* tag */);
}
public static TResult End<TResult>(IAsyncResult asyncResult, object tag) {
return WrappedAsyncResult<TResult>.Cast(asyncResult, tag).End();
}
public static void End(IAsyncResult asyncResult) {
End(asyncResult, null /* tag */);
}
public static void End(IAsyncResult asyncResult, object tag) {
End<AsyncVoid>(asyncResult, tag);
}
[SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable", Justification = "The Timer will be disposed of either when it fires or when the operation completes successfully.")]
private sealed class WrappedAsyncResult<TResult> : IAsyncResult {
private readonly BeginInvokeDelegate _beginDelegate;
private readonly object _beginDelegateLockObj = new object();
private readonly EndInvokeDelegate<TResult> _endDelegate;
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 IAsyncResult _innerAsyncResult;
private AsyncCallback _originalCallback;
private readonly object _tag; // prevent an instance of this type from being passed to the wrong End() method
private volatile bool _timedOut;
private Timer _timer;
public WrappedAsyncResult(BeginInvokeDelegate beginDelegate, EndInvokeDelegate<TResult> endDelegate, object tag) {
_beginDelegate = beginDelegate;
_endDelegate = endDelegate;
_tag = tag;
}
public object AsyncState {
get {
return _innerAsyncResult.AsyncState;
}
}
public WaitHandle AsyncWaitHandle {
get {
return _innerAsyncResult.AsyncWaitHandle;
}
}
public bool CompletedSynchronously {
get {
return _innerAsyncResult.CompletedSynchronously;
}
}
public bool IsCompleted {
get {
return _innerAsyncResult.IsCompleted;
}
}
// kicks off the process, instantiates a timer if requested
public void Begin(AsyncCallback callback, object state, int timeout) {
_originalCallback = callback;
bool completedSynchronously;
// 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 = _beginDelegate(HandleAsynchronousCompletion, state);
completedSynchronously = _innerAsyncResult.CompletedSynchronously;
if (!completedSynchronously) {
if (timeout > Timeout.Infinite) {
CreateTimer(timeout);
}
}
}
if (completedSynchronously) {
if (callback != null) {
callback(this);
}
}
}
public static WrappedAsyncResult<TResult> Cast(IAsyncResult asyncResult, object tag) {
if (asyncResult == null) {
throw new ArgumentNullException("asyncResult");
}
WrappedAsyncResult<TResult> castResult = asyncResult as WrappedAsyncResult<TResult>;
if (castResult != null && Object.Equals(castResult._tag, tag)) {
return castResult;
}
else {
throw Error.AsyncCommon_InvalidAsyncResult("asyncResult");
}
}
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 _endDelegate(_innerAsyncResult);
}
private void ExecuteAsynchronousCallback(bool timedOut) {
WaitForBeginToCompleteAndDestroyTimer();
if (_handleCallbackGate.TryEnter()) {
_timedOut = timedOut;
if (_originalCallback != null) {
_originalCallback(this);
}
}
}
private void HandleAsynchronousCompletion(IAsyncResult asyncResult) {
if (asyncResult.CompletedSynchronously) {
// If the operation completed synchronously, the WrappedAsyncResult.Begin() method will handle it.
return;
}
ExecuteAsynchronousCallback(false /* timedOut */);
}
private void HandleTimeout(object state) {
ExecuteAsynchronousCallback(true /* timedOut */);
}
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;
}
}
}
}
}
|