/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Diagnostics;
using System.Threading;
namespace Apache.NMS.WCF
{
///
/// A generic base class for IAsyncResult implementations
/// that wraps a ManualResetEvent.
///
internal abstract class AsyncResult : IAsyncResult
{
private readonly AsyncCallback _callback;
private readonly object _state;
private readonly object _thisLock;
private bool _completedSynchronously;
private bool _endCalled;
private Exception _exception;
private bool _isCompleted;
private ManualResetEvent _manualResetEvent;
///
/// Initializes a new instance of the class.
///
/// The callback.
/// The state.
protected AsyncResult(AsyncCallback callback, object state)
{
_callback = callback;
_state = state;
_thisLock = new object();
}
///
/// Gets a user-defined object that qualifies or contains information about an asynchronous operation.
///
///
///
/// A user-defined object that qualifies or contains information about an asynchronous operation.
///
public object AsyncState
{
get { return _state; }
}
///
/// Gets a that is used to wait for an asynchronous operation to complete.
///
///
///
/// A that is used to wait for an asynchronous operation to complete.
///
public WaitHandle AsyncWaitHandle
{
get
{
if(_manualResetEvent != null)
{
return _manualResetEvent;
}
lock(ThisLock)
{
if(_manualResetEvent == null)
{
_manualResetEvent = new ManualResetEvent(_isCompleted);
}
}
return _manualResetEvent;
}
}
///
/// Gets a value that indicates whether the asynchronous operation completed synchronously.
///
///
/// true if the asynchronous operation completed synchronously; otherwise, false.
///
public bool CompletedSynchronously
{
get { return _completedSynchronously; }
}
///
/// Gets a value that indicates whether the asynchronous operation has completed.
///
///
/// true if the operation is complete; otherwise, false.
///
public bool IsCompleted
{
get { return _isCompleted; }
}
///
/// Gets an object lock.
///
/// The object lock.
private object ThisLock
{
get { return _thisLock; }
}
// Call this version of complete when your asynchronous operation is complete. This will update the state
// of the operation and notify the callback.
///
/// Completes the specified completed synchronously.
///
/// if set to [completed synchronously].
protected void Complete(bool completedSynchronously)
{
if(_isCompleted)
{
throw new InvalidOperationException("Cannot call Complete twice");
}
_completedSynchronously = completedSynchronously;
if(completedSynchronously)
{
// 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(ThisLock)
{
_isCompleted = true;
if(_manualResetEvent != null)
{
_manualResetEvent.Set();
}
}
}
// If the callback throws, there is an error in the callback implementation
if(_callback != null)
{
_callback(this);
}
}
///
/// Call this version of complete if you raise an exception during processing. In addition to notifying
/// the callback, it will capture the exception and store it to be thrown during AsyncResult.End.
///
/// if set to [completed synchronously].
/// The exception.
protected void Complete(bool completedSynchronously, Exception exception)
{
_exception = exception;
Complete(completedSynchronously);
}
///
/// End should be called when the End function for the asynchronous operation is complete. It
/// ensures the asynchronous operation is complete, and does some common validation.
///
/// The type of the async result.
/// The result.
///
protected static TAsyncResult End(IAsyncResult result) where TAsyncResult : AsyncResult
{
if(result == null)
{
throw new ArgumentNullException("result");
}
TAsyncResult asyncResult = result as TAsyncResult;
if(asyncResult == null)
{
throw new ArgumentException("Invalid async messageBody.", "result");
}
if(asyncResult._endCalled)
{
throw new InvalidOperationException("Async object already ended.");
}
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;
}
}
///
/// An AsyncResult that completes as soon as it is instantiated.
///
internal class CompletedAsyncResult : AsyncResult
{
///
/// Initializes a new instance of the class.
///
/// The callback.
/// The state.
public CompletedAsyncResult(AsyncCallback callback, object state)
: base(callback, state)
{
Complete(true);
}
///
/// Ends the specified result.
///
/// The result.
public static void End(IAsyncResult result)
{
End(result);
}
}
//A strongly typed AsyncResult
internal abstract class TypedAsyncResult : AsyncResult
{
private T _data;
///
/// Initializes a new instance of the class.
///
/// The callback.
/// The state.
protected TypedAsyncResult(AsyncCallback callback, object state)
: base(callback, state)
{
}
///
/// Gets the data.
///
/// The data.
public T Data
{
get { return _data; }
}
///
/// Completes the specified data.
///
/// The data.
/// if set to [completed synchronously].
protected void Complete(T data, bool completedSynchronously)
{
_data = data;
Complete(completedSynchronously);
}
///
/// Ends the specified result.
///
/// The result.
///
public static T End(IAsyncResult result)
{
TypedAsyncResult typedResult = End>(result);
return typedResult.Data;
}
}
//A strongly typed AsyncResult that completes as soon as it is instantiated.
internal class TypedCompletedAsyncResult : TypedAsyncResult
{
///
/// Initializes a new instance of the class.
///
/// The data.
/// The callback.
/// The state.
public TypedCompletedAsyncResult(T data, AsyncCallback callback, object state)
: base(callback, state)
{
Complete(data, true);
}
///
/// Finishes the async request.
///
/// The result.
///
public new static T End(IAsyncResult result)
{
TypedCompletedAsyncResult completedResult = result as TypedCompletedAsyncResult;
if(completedResult == null)
{
throw new ArgumentException("Invalid async messageBody.", "messageBody");
}
return TypedAsyncResult.End(completedResult);
}
}
}