Files
linux-packaging-mono/external/aspnetwebstack/src/System.Web.Http.SelfHost/ServiceModel/Channels/AsyncResult.cs
Jo Shields a575963da9 Imported Upstream version 3.6.0
Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
2014-08-13 10:39:27 +01:00

349 lines
12 KiB
C#

// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Threading;
using System.Web.Http.SelfHost.Properties;
namespace System.Web.Http.SelfHost.ServiceModel.Channels
{
// AsyncResult starts acquired; Complete releases.
[SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable", Justification = "Ported from WCF")]
internal abstract class AsyncResult : IAsyncResult
{
private static AsyncCallback _asyncCompletionWrapperCallback;
private readonly AsyncCallback _completionCallback;
private bool _completedSynchronously;
private bool _endCalled;
private Exception _exception;
private bool _isCompleted;
private AsyncCompletion _nextAsyncCompletion;
private readonly object _state;
private Action _beforePrepareAsyncCompletionAction;
private Func<IAsyncResult, bool> _checkSyncValidationFunc;
private ManualResetEvent _manualResetEvent;
private readonly object _manualResetEventLock = new object();
protected AsyncResult(AsyncCallback callback, object state)
{
_completionCallback = callback;
_state = state;
}
/// <summary>
/// Can be utilized by subclasses to write core completion code for both the sync and async paths
/// in one location, signalling chainable synchronous completion with the boolean result,
/// and leveraging PrepareAsyncCompletion for conversion to an AsyncCallback.
/// </summary>
/// <remarks>NOTE: requires that "this" is passed in as the state object to the asynchronous sub-call being used with a completion routine.</remarks>
protected delegate bool AsyncCompletion(IAsyncResult result);
public object AsyncState
{
get { return _state; }
}
public WaitHandle AsyncWaitHandle
{
get
{
lock (_manualResetEventLock)
{
if (_manualResetEvent == null)
{
_manualResetEvent = new ManualResetEvent(_isCompleted);
}
}
return _manualResetEvent;
}
}
public bool CompletedSynchronously
{
get { return _completedSynchronously; }
}
public bool HasCallback
{
get { return _completionCallback != null; }
}
public bool IsCompleted
{
get { return _isCompleted; }
}
// used in conjunction with PrepareAsyncCompletion to allow for finally blocks
protected Action<AsyncResult, Exception> OnCompleting { get; set; }
// subclasses like TraceAsyncResult can use this to wrap the callback functionality in a scope
protected Action<AsyncCallback, IAsyncResult> VirtualCallback { get; set; }
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Exception is propagated or FailFast")]
protected void Complete(bool didCompleteSynchronously)
{
if (_isCompleted)
{
throw Error.InvalidOperation(SRResources.AsyncResultCompletedTwice, GetType());
}
_completedSynchronously = didCompleteSynchronously;
if (OnCompleting != null)
{
// Allow exception replacement, like a catch/throw pattern.
try
{
OnCompleting(this, _exception);
}
catch (Exception e)
{
_exception = e;
}
}
if (didCompleteSynchronously)
{
// If we completedSynchronously, then there's no chance that the manualResetEvent was created so
// we don't need to worry about a race
Debug.Assert(_manualResetEvent == null, "No ManualResetEvent should be created for a synchronous AsyncResult.");
_isCompleted = true;
}
else
{
lock (_manualResetEventLock)
{
_isCompleted = true;
if (_manualResetEvent != null)
{
_manualResetEvent.Set();
}
}
}
if (_completionCallback != null)
{
try
{
if (VirtualCallback != null)
{
VirtualCallback(_completionCallback, this);
}
else
{
_completionCallback(this);
}
}
#pragma warning disable 1634
#pragma warning suppress 56500 // transferring exception to another thread
catch (Exception e)
{
// String is not resourced because it occurs only in debug build, only with a fatal assert,
// and because it appears as an unused resource in a release build
Contract.Assert(false, Error.Format("{0}{1}{2}", "Async Callback threw an exception.", Environment.NewLine, e.ToString()));
}
#pragma warning restore 1634
}
}
protected void Complete(bool didCompleteSynchronously, Exception error)
{
_exception = error;
Complete(didCompleteSynchronously);
}
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Exception is propagated")]
private static void AsyncCompletionWrapperCallback(IAsyncResult result)
{
if (result == null)
{
throw Error.InvalidOperation(SRResources.InvalidNullAsyncResult);
}
if (result.CompletedSynchronously)
{
return;
}
AsyncResult thisPtr = (AsyncResult)result.AsyncState;
if (!thisPtr.OnContinueAsyncCompletion(result))
{
return;
}
AsyncCompletion callback = thisPtr.GetNextCompletion();
if (callback == null)
{
ThrowInvalidAsyncResult(result);
}
bool completeSelf = false;
Exception completionException = null;
try
{
completeSelf = callback(result);
}
catch (Exception e)
{
completeSelf = true;
completionException = e;
}
if (completeSelf)
{
thisPtr.Complete(false, completionException);
}
}
// Note: this should be only derived by the TransactedAsyncResult
protected virtual bool OnContinueAsyncCompletion(IAsyncResult result)
{
return true;
}
// Note: this should be used only by the TransactedAsyncResult
protected void SetBeforePrepareAsyncCompletionAction(Action completionAction)
{
_beforePrepareAsyncCompletionAction = completionAction;
}
// Note: this should be used only by the TransactedAsyncResult
protected void SetCheckSyncValidationFunc(Func<IAsyncResult, bool> validationFunc)
{
_checkSyncValidationFunc = validationFunc;
}
protected AsyncCallback PrepareAsyncCompletion(AsyncCompletion callback)
{
if (_beforePrepareAsyncCompletionAction != null)
{
_beforePrepareAsyncCompletionAction();
}
_nextAsyncCompletion = callback;
if (AsyncResult._asyncCompletionWrapperCallback == null)
{
AsyncResult._asyncCompletionWrapperCallback = new AsyncCallback(AsyncCompletionWrapperCallback);
}
return AsyncResult._asyncCompletionWrapperCallback;
}
protected bool CheckSyncContinue(IAsyncResult result)
{
AsyncCompletion dummy;
return TryContinueHelper(result, out dummy);
}
protected bool SyncContinue(IAsyncResult result)
{
AsyncCompletion callback;
if (TryContinueHelper(result, out callback))
{
return callback(result);
}
else
{
return false;
}
}
private bool TryContinueHelper(IAsyncResult result, out AsyncCompletion callback)
{
if (result == null)
{
throw Error.InvalidOperation(SRResources.InvalidNullAsyncResult);
}
callback = null;
if (_checkSyncValidationFunc != null)
{
if (!_checkSyncValidationFunc(result))
{
return false;
}
}
else if (!result.CompletedSynchronously)
{
return false;
}
callback = GetNextCompletion();
if (callback == null)
{
ThrowInvalidAsyncResult("Only call Check/SyncContinue once per async operation (once per PrepareAsyncCompletion).");
}
return true;
}
private AsyncCompletion GetNextCompletion()
{
AsyncCompletion result = _nextAsyncCompletion;
_nextAsyncCompletion = null;
return result;
}
protected static void ThrowInvalidAsyncResult(IAsyncResult result)
{
if (result == null)
{
throw Error.ArgumentNull("result");
}
throw Error.InvalidOperation(SRResources.InvalidAsyncResultImplementation, result.GetType());
}
protected static void ThrowInvalidAsyncResult(string debugText)
{
string message = SRResources.InvalidAsyncResultImplementationGeneric;
if (debugText != null)
{
#if DEBUG
message += " " + debugText;
#endif
}
throw Error.InvalidOperation(message);
}
[SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "Existing API")]
protected static TAsyncResult End<TAsyncResult>(IAsyncResult result)
where TAsyncResult : AsyncResult
{
if (result == null)
{
throw Error.ArgumentNull("result");
}
TAsyncResult asyncResult = result as TAsyncResult;
if (asyncResult == null)
{
throw Error.Argument("result", SRResources.InvalidAsyncResult);
}
if (asyncResult._endCalled)
{
throw Error.InvalidOperation(SRResources.AsyncResultAlreadyEnded);
}
asyncResult._endCalled = true;
if (!asyncResult._isCompleted)
{
asyncResult.AsyncWaitHandle.WaitOne();
}
if (asyncResult._manualResetEvent != null)
{
asyncResult._manualResetEvent.Close();
}
if (asyncResult._exception != null)
{
throw asyncResult._exception;
}
return asyncResult;
}
}
}