1 /* ****************************************************************************
\r
3 * Copyright (c) Microsoft Corporation. All rights reserved.
\r
5 * This software is subject to the Microsoft Public License (Ms-PL).
\r
6 * A copy of the license can be found in the license.htm file included
\r
7 * in this distribution.
\r
9 * You must not remove this notice, or any other, from this software.
\r
11 * ***************************************************************************/
\r
13 namespace System.Web.Mvc.Async {
\r
15 using System.Diagnostics.CodeAnalysis;
\r
16 using System.Threading;
\r
18 // This class is used for the following pattern:
\r
20 // public IAsyncResult BeginInner(..., callback, state);
\r
21 // public TInnerResult EndInner(asyncResult);
\r
22 // public IAsyncResult BeginOuter(..., callback, state);
\r
23 // public TOuterResult EndOuter(asyncResult);
\r
25 // That is, Begin/EndOuter() wrap Begin/EndInner(), potentially with pre- and post-processing.
\r
27 internal static class AsyncResultWrapper {
\r
31 private static Func<AsyncVoid> MakeVoidDelegate(Action action) {
\r
34 return default(AsyncVoid);
\r
38 private static EndInvokeDelegate<AsyncVoid> MakeVoidDelegate(EndInvokeDelegate endDelegate) {
\r
41 return default(AsyncVoid);
\r
45 // kicks off an asynchronous operation
\r
47 public static IAsyncResult Begin<TResult>(AsyncCallback callback, object state, BeginInvokeDelegate beginDelegate, EndInvokeDelegate<TResult> endDelegate) {
\r
48 return Begin<TResult>(callback, state, beginDelegate, endDelegate, null /* tag */);
\r
51 public static IAsyncResult Begin<TResult>(AsyncCallback callback, object state, BeginInvokeDelegate beginDelegate, EndInvokeDelegate<TResult> endDelegate, object tag) {
\r
52 return Begin<TResult>(callback, state, beginDelegate, endDelegate, tag, Timeout.Infinite);
\r
55 public static IAsyncResult Begin<TResult>(AsyncCallback callback, object state, BeginInvokeDelegate beginDelegate, EndInvokeDelegate<TResult> endDelegate, object tag, int timeout) {
\r
56 WrappedAsyncResult<TResult> asyncResult = new WrappedAsyncResult<TResult>(beginDelegate, endDelegate, tag);
\r
57 asyncResult.Begin(callback, state, timeout);
\r
61 public static IAsyncResult Begin(AsyncCallback callback, object state, BeginInvokeDelegate beginDelegate, EndInvokeDelegate endDelegate) {
\r
62 return Begin(callback, state, beginDelegate, endDelegate, null /* tag */);
\r
65 public static IAsyncResult Begin(AsyncCallback callback, object state, BeginInvokeDelegate beginDelegate, EndInvokeDelegate endDelegate, object tag) {
\r
66 return Begin(callback, state, beginDelegate, endDelegate, tag, Timeout.Infinite);
\r
69 public static IAsyncResult Begin(AsyncCallback callback, object state, BeginInvokeDelegate beginDelegate, EndInvokeDelegate endDelegate, object tag, int timeout) {
\r
70 return Begin<AsyncVoid>(callback, state, beginDelegate, MakeVoidDelegate(endDelegate), tag, timeout);
\r
73 // wraps a synchronous operation in an asynchronous wrapper, but still completes synchronously
\r
75 public static IAsyncResult BeginSynchronous<TResult>(AsyncCallback callback, object state, Func<TResult> func) {
\r
76 return BeginSynchronous<TResult>(callback, state, func, null /* tag */);
\r
79 public static IAsyncResult BeginSynchronous<TResult>(AsyncCallback callback, object state, Func<TResult> func, object tag) {
\r
80 // Begin() doesn't perform any work on its own and returns immediately.
\r
81 BeginInvokeDelegate beginDelegate = (asyncCallback, asyncState) => {
\r
82 SimpleAsyncResult innerAsyncResult = new SimpleAsyncResult(asyncState);
\r
83 innerAsyncResult.MarkCompleted(true /* completedSynchronously */, asyncCallback);
\r
84 return innerAsyncResult;
\r
87 // The End() method blocks.
\r
88 EndInvokeDelegate<TResult> endDelegate = _ => {
\r
92 WrappedAsyncResult<TResult> asyncResult = new WrappedAsyncResult<TResult>(beginDelegate, endDelegate, tag);
\r
93 asyncResult.Begin(callback, state, Timeout.Infinite);
\r
97 public static IAsyncResult BeginSynchronous(AsyncCallback callback, object state, Action action) {
\r
98 return BeginSynchronous(callback, state, action, null /* tag */);
\r
101 public static IAsyncResult BeginSynchronous(AsyncCallback callback, object state, Action action, object tag) {
\r
102 return BeginSynchronous<AsyncVoid>(callback, state, MakeVoidDelegate(action), tag);
\r
105 // completes an asynchronous operation
\r
107 public static TResult End<TResult>(IAsyncResult asyncResult) {
\r
108 return End<TResult>(asyncResult, null /* tag */);
\r
111 public static TResult End<TResult>(IAsyncResult asyncResult, object tag) {
\r
112 return WrappedAsyncResult<TResult>.Cast(asyncResult, tag).End();
\r
115 public static void End(IAsyncResult asyncResult) {
\r
116 End(asyncResult, null /* tag */);
\r
119 public static void End(IAsyncResult asyncResult, object tag) {
\r
120 End<AsyncVoid>(asyncResult, tag);
\r
123 [SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable",
\r
124 Justification = "The Timer will be disposed of either when it fires or when the operation completes successfully.")]
\r
125 private sealed class WrappedAsyncResult<TResult> : IAsyncResult {
\r
127 private readonly BeginInvokeDelegate _beginDelegate;
\r
128 private readonly object _beginDelegateLockObj = new object();
\r
129 private readonly EndInvokeDelegate<TResult> _endDelegate;
\r
130 private readonly SingleEntryGate _endExecutedGate = new SingleEntryGate(); // prevent End() from being called twice
\r
131 private readonly SingleEntryGate _handleCallbackGate = new SingleEntryGate(); // prevent callback from being handled multiple times
\r
132 private IAsyncResult _innerAsyncResult;
\r
133 private AsyncCallback _originalCallback;
\r
134 private readonly object _tag; // prevent an instance of this type from being passed to the wrong End() method
\r
135 private volatile bool _timedOut;
\r
136 private Timer _timer;
\r
138 public WrappedAsyncResult(BeginInvokeDelegate beginDelegate, EndInvokeDelegate<TResult> endDelegate, object tag) {
\r
139 _beginDelegate = beginDelegate;
\r
140 _endDelegate = endDelegate;
\r
144 public object AsyncState {
\r
146 return _innerAsyncResult.AsyncState;
\r
150 public WaitHandle AsyncWaitHandle {
\r
152 return _innerAsyncResult.AsyncWaitHandle;
\r
156 public bool CompletedSynchronously {
\r
158 return _innerAsyncResult.CompletedSynchronously;
\r
162 public bool IsCompleted {
\r
164 return _innerAsyncResult.IsCompleted;
\r
168 // kicks off the process, instantiates a timer if requested
\r
169 public void Begin(AsyncCallback callback, object state, int timeout) {
\r
170 _originalCallback = callback;
\r
171 bool completedSynchronously;
\r
173 // Force the target Begin() operation to complete before the callback can continue,
\r
174 // since the target operation might perform post-processing of the data.
\r
175 lock (_beginDelegateLockObj) {
\r
176 _innerAsyncResult = _beginDelegate(HandleAsynchronousCompletion, state);
\r
178 completedSynchronously = _innerAsyncResult.CompletedSynchronously;
\r
179 if (!completedSynchronously) {
\r
180 if (timeout > Timeout.Infinite) {
\r
181 CreateTimer(timeout);
\r
186 if (completedSynchronously) {
\r
187 if (callback != null) {
\r
193 public static WrappedAsyncResult<TResult> Cast(IAsyncResult asyncResult, object tag) {
\r
194 if (asyncResult == null) {
\r
195 throw new ArgumentNullException("asyncResult");
\r
198 WrappedAsyncResult<TResult> castResult = asyncResult as WrappedAsyncResult<TResult>;
\r
199 if (castResult != null && Object.Equals(castResult._tag, tag)) {
\r
203 throw Error.AsyncCommon_InvalidAsyncResult("asyncResult");
\r
207 private void CreateTimer(int timeout) {
\r
208 // this method should be called within a lock(_beginDelegateLockObj)
\r
209 _timer = new Timer(HandleTimeout, null, timeout, Timeout.Infinite /* disable periodic signaling */);
\r
212 public TResult End() {
\r
213 if (!_endExecutedGate.TryEnter()) {
\r
214 throw Error.AsyncCommon_AsyncResultAlreadyConsumed();
\r
218 throw new TimeoutException();
\r
220 WaitForBeginToCompleteAndDestroyTimer();
\r
222 return _endDelegate(_innerAsyncResult);
\r
225 private void ExecuteAsynchronousCallback(bool timedOut) {
\r
226 WaitForBeginToCompleteAndDestroyTimer();
\r
228 if (_handleCallbackGate.TryEnter()) {
\r
229 _timedOut = timedOut;
\r
230 if (_originalCallback != null) {
\r
231 _originalCallback(this);
\r
236 private void HandleAsynchronousCompletion(IAsyncResult asyncResult) {
\r
237 if (asyncResult.CompletedSynchronously) {
\r
238 // If the operation completed synchronously, the WrappedAsyncResult.Begin() method will handle it.
\r
242 ExecuteAsynchronousCallback(false /* timedOut */);
\r
245 private void HandleTimeout(object state) {
\r
246 ExecuteAsynchronousCallback(true /* timedOut */);
\r
249 private void WaitForBeginToCompleteAndDestroyTimer() {
\r
250 lock (_beginDelegateLockObj) {
\r
251 // Wait for the target Begin() method to complete, as it might be performing
\r
252 // post-processing. This also forces a memory barrier, so _innerAsyncResult
\r
253 // is guaranteed to be non-null at this point.
\r
255 if (_timer != null) {
\r