You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

204 lines
7.7 KiB
C#

9 months ago
using System;
using System.Threading;
namespace Renci.SshNet.Common
{
/// <summary>
/// Base class to encapsulates the results of an asynchronous operation.
/// </summary>
public abstract class AsyncResult : IAsyncResult
{
// Fields set at construction which never change while operation is pending
private readonly AsyncCallback _asyncCallback;
private readonly object _asyncState;
// Field set at construction which do change after operation completes
private const int StatePending = 0;
private const int StateCompletedSynchronously = 1;
private const int StateCompletedAsynchronously = 2;
private int _completedState = StatePending;
// Field that may or may not get set depending on usage
private ManualResetEvent _asyncWaitHandle;
// Fields set when operation completes
private Exception _exception;
/// <summary>
/// Gets or sets a value indicating whether <see cref="EndInvoke()"/> has been called on the current
/// <see cref="AsyncResult"/>.
/// </summary>
/// <value>
/// <c>true</c> if <see cref="EndInvoke()"/> has been called on the current <see cref="AsyncResult"/>;
/// otherwise, <c>false</c>.
/// </value>
public bool EndInvokeCalled { get; private set; }
/// <summary>
/// Initializes a new instance of the <see cref="AsyncResult"/> class.
/// </summary>
/// <param name="asyncCallback">The async callback.</param>
/// <param name="state">The state.</param>
protected AsyncResult(AsyncCallback asyncCallback, object state)
{
_asyncCallback = asyncCallback;
_asyncState = state;
}
/// <summary>
/// Marks asynchronous operation as completed.
/// </summary>
/// <param name="exception">The exception.</param>
/// <param name="completedSynchronously">if set to <c>true</c> [completed synchronously].</param>
public void SetAsCompleted(Exception exception, bool completedSynchronously)
{
// Passing null for exception means no error occurred; this is the common case
_exception = exception;
// The m_CompletedState field MUST be set prior calling the callback
var prevState = Interlocked.Exchange(ref _completedState,
completedSynchronously ? StateCompletedSynchronously : StateCompletedAsynchronously);
if (prevState != StatePending)
throw new InvalidOperationException("You can set a result only once");
// If the event exists, set it
if (_asyncWaitHandle != null)
_asyncWaitHandle.Set();
// If a callback method was set, call it
if (_asyncCallback != null)
_asyncCallback(this);
}
/// <summary>
/// Waits until the asynchronous operation completes, and then returns.
/// </summary>
internal void EndInvoke()
{
// This method assumes that only 1 thread calls EndInvoke for this object
if (!IsCompleted)
{
// If the operation isn't done, wait for it
AsyncWaitHandle.WaitOne();
_asyncWaitHandle = null; // Allow early GC
AsyncWaitHandle.Dispose();
}
EndInvokeCalled = true;
// Operation is done: if an exception occurred, throw it
if (_exception != null)
throw _exception;
}
#region Implementation of IAsyncResult
/// <summary>
/// Gets a user-defined object that qualifies or contains information about an asynchronous operation.
/// </summary>
/// <returns>A user-defined object that qualifies or contains information about an asynchronous operation.</returns>
public object AsyncState { get { return _asyncState; } }
/// <summary>
/// Gets a value that indicates whether the asynchronous operation completed synchronously.
/// </summary>
/// <returns>true if the asynchronous operation completed synchronously; otherwise, false.</returns>
public bool CompletedSynchronously
{
get { return _completedState == StateCompletedSynchronously; }
}
/// <summary>
/// Gets a <see cref="WaitHandle"/> that is used to wait for an asynchronous operation to complete.
/// </summary>
/// <returns>A <see cref="WaitHandle"/> that is used to wait for an asynchronous operation to complete.</returns>
public WaitHandle AsyncWaitHandle
{
get
{
if (_asyncWaitHandle == null)
{
var done = IsCompleted;
var mre = new ManualResetEvent(done);
if (Interlocked.CompareExchange(ref _asyncWaitHandle, mre, null) != null)
{
// Another thread created this object's event; dispose the event we just created
mre.Dispose();
}
else
{
if (!done && IsCompleted)
{
// If the operation wasn't done when we created
// the event but now it is done, set the event
_asyncWaitHandle.Set();
}
}
}
return _asyncWaitHandle;
}
}
/// <summary>
/// Gets a value that indicates whether the asynchronous operation has completed.
/// </summary>
/// <returns>
/// <c>true</c> if the operation is complete; otherwise, <c>false</c>.</returns>
public bool IsCompleted
{
get { return _completedState != StatePending; }
}
#endregion
}
/// <summary>
/// Base class to encapsulates the results of an asynchronous operation that returns result.
/// </summary>
/// <typeparam name="TResult">The type of the result.</typeparam>
public abstract class AsyncResult<TResult> : AsyncResult
{
// Field set when operation completes
private TResult _result;
/// <summary>
/// Initializes a new instance of the <see cref="AsyncResult{TResult}"/> class.
/// </summary>
/// <param name="asyncCallback">The async callback.</param>
/// <param name="state">The state.</param>
protected AsyncResult(AsyncCallback asyncCallback, object state)
: base(asyncCallback, state)
{
}
/// <summary>
/// Marks asynchronous operation as completed.
/// </summary>
/// <param name="result">The result.</param>
/// <param name="completedSynchronously">if set to <c>true</c> [completed synchronously].</param>
public void SetAsCompleted(TResult result, bool completedSynchronously)
{
// Save the asynchronous operation's result
_result = result;
// Tell the base class that the operation completed successfully (no exception)
SetAsCompleted(null, completedSynchronously);
}
/// <summary>
/// Waits until the asynchronous operation completes, and then returns the value generated by the asynchronous operation.
/// </summary>
/// <returns>
/// The invocation result.
/// </returns>
public new TResult EndInvoke()
{
base.EndInvoke(); // Wait until operation has completed
return _result; // Return the result (if above didn't throw)
}
}
}