using System; using System.Threading; namespace Renci.SshNet.Common { /// /// Base class to encapsulates the results of an asynchronous operation. /// public abstract class AsyncResult : IAsyncResult { private const int StatePending = 0; private const int StateCompletedSynchronously = 1; private const int StateCompletedAsynchronously = 2; private readonly AsyncCallback _asyncCallback; private readonly object _asyncState; private int _completedState = StatePending; private ManualResetEvent _asyncWaitHandle; private Exception _exception; /// /// Initializes a new instance of the class. /// /// The async callback. /// The state. protected AsyncResult(AsyncCallback asyncCallback, object state) { _asyncCallback = asyncCallback; _asyncState = state; } /// /// Gets a value indicating whether has been called on the current . /// /// /// true if has been called on the current ; /// otherwise, false. /// public bool EndInvokeCalled { get; private set; } /// /// Marks asynchronous operation as completed. /// /// The exception. /// If set to , completed synchronously. public void SetAsCompleted(Exception exception, bool completedSynchronously) { // Passing null for exception means no error occurred; this is the common case _exception = exception; // The '_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 _ = _asyncWaitHandle?.Set(); // If a callback method was set, call it _asyncCallback?.Invoke(this); } /// /// Waits until the asynchronous operation completes, and then returns. /// 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; } } /// /// 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 _asyncState; } } /// /// Gets a value indicating whether the asynchronous operation completed synchronously. /// /// /// if the asynchronous operation completed synchronously; otherwise, . /// public bool CompletedSynchronously { get { return _completedState == StateCompletedSynchronously; } } /// /// 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 (_asyncWaitHandle is null) { var done = IsCompleted; var mre = new ManualResetEvent(done); if (Interlocked.CompareExchange(ref _asyncWaitHandle, mre, comparand: 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; } } /// /// Gets a value indicating whether the asynchronous operation has completed. /// /// /// if the operation is complete; otherwise, . /// public bool IsCompleted { get { return _completedState != StatePending; } } } /// /// Base class to encapsulates the results of an asynchronous operation that returns result. /// /// The type of the result. public abstract class AsyncResult : AsyncResult { // Field set when operation completes private TResult _result; /// /// Initializes a new instance of the class. /// /// The async callback. /// The state. protected AsyncResult(AsyncCallback asyncCallback, object state) : base(asyncCallback, state) { } /// /// Marks asynchronous operation as completed. /// /// The result. /// if set to true [completed synchronously]. 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(exception: null, completedSynchronously); } /// /// Waits until the asynchronous operation completes, and then returns the value generated by the asynchronous operation. /// /// /// The invocation result. /// public new TResult EndInvoke() { base.EndInvoke(); // Wait until operation has completed return _result; // Return the result (if above didn't throw) } } }