Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/aspnetwebstack.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/System.Web.Mvc/Async/AsyncResultWrapper.cs')
-rw-r--r--src/System.Web.Mvc/Async/AsyncResultWrapper.cs303
1 files changed, 303 insertions, 0 deletions
diff --git a/src/System.Web.Mvc/Async/AsyncResultWrapper.cs b/src/System.Web.Mvc/Async/AsyncResultWrapper.cs
new file mode 100644
index 00000000..8cc54b7c
--- /dev/null
+++ b/src/System.Web.Mvc/Async/AsyncResultWrapper.cs
@@ -0,0 +1,303 @@
+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 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, tag: null);
+ }
+
+ 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, tag: null);
+ }
+
+ 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, tag: null);
+ }
+
+ 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(completedSynchronously: true, callback: 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, tag: null);
+ }
+
+ 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, tag: null);
+ }
+
+ public static TResult End<TResult>(IAsyncResult asyncResult, object tag)
+ {
+ return WrappedAsyncResult<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);
+ }
+
+ [DebuggerNonUserCode]
+ [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 const int AsyncStateNone = 0;
+ private const int AsyncStateBeginUnwound = 1;
+ private const int AsyncStateCallbackFired = 2;
+
+ private int _asyncState;
+ 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 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;
+
+ 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; private set; }
+
+ 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;
+
+ // 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);
+
+ // 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);
+ }
+ }
+ }
+
+ 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 && 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)
+ {
+ // 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;
+ }
+ }
+ }
+ }
+}