|  | @@ -1,13 +1,34 @@
 | 
	
		
			
				|  |  |  using System;
 | 
	
		
			
				|  |  |  using System.Threading;
 | 
	
		
			
				|  |  | +using System.Threading.Tasks;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  namespace Renci.SshNet.Sftp
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  |      /// <summary>
 | 
	
		
			
				|  |  |      /// Represents the status of an asynchronous SFTP operation.
 | 
	
		
			
				|  |  |      /// </summary>
 | 
	
		
			
				|  |  | -    public class SftpAsyncResult : IAsyncResult, IDisposable
 | 
	
		
			
				|  |  | +    public class SftpAsyncResult : 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 Exception _exception;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        private ManualResetEvent _asyncWaitHandle;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        private int _completedState = _statePending;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        private SftpSession _sftpSession;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        private TimeSpan _commandTimeout;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |          /// <summary>
 | 
	
		
			
				|  |  |          /// Gets or sets the uploaded bytes.
 | 
	
		
			
				|  |  |          /// </summary>
 | 
	
	
		
			
				|  | @@ -20,35 +41,61 @@ namespace Renci.SshNet.Sftp
 | 
	
		
			
				|  |  |          /// <value>The downloaded bytes.</value>
 | 
	
		
			
				|  |  |          public ulong DownloadedBytes { get; internal set; }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        private SftpCommand _command;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |          /// <summary>
 | 
	
		
			
				|  |  |          /// Initializes a new instance of the <see cref="SftpAsyncResult"/> class.
 | 
	
		
			
				|  |  |          /// </summary>
 | 
	
		
			
				|  |  | -        /// <param name="command">The command.</param>
 | 
	
		
			
				|  |  | +        /// <param name="sftpSession">The SFTP session.</param>
 | 
	
		
			
				|  |  | +        /// <param name="commandTimeout">The command timeout.</param>
 | 
	
		
			
				|  |  | +        /// <param name="asyncCallback">The async callback.</param>
 | 
	
		
			
				|  |  |          /// <param name="state">The state.</param>
 | 
	
		
			
				|  |  | -        internal SftpAsyncResult(SftpCommand command, object state)
 | 
	
		
			
				|  |  | +        internal SftpAsyncResult(SftpSession sftpSession, TimeSpan commandTimeout, AsyncCallback asyncCallback, object state)
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  | -            this._command = command;
 | 
	
		
			
				|  |  | -            this.AsyncState = state;
 | 
	
		
			
				|  |  | -            this.AsyncWaitHandle = new ManualResetEvent(false);
 | 
	
		
			
				|  |  | +            this._sftpSession = sftpSession;
 | 
	
		
			
				|  |  | +            this._commandTimeout = commandTimeout;
 | 
	
		
			
				|  |  | +            this._asyncCallback = asyncCallback;
 | 
	
		
			
				|  |  | +            this._asyncState = state;
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          /// <summary>
 | 
	
		
			
				|  |  | -        /// Gets the command.
 | 
	
		
			
				|  |  | +        /// Marks result as completed.
 | 
	
		
			
				|  |  |          /// </summary>
 | 
	
		
			
				|  |  | -        /// <typeparam name="T">Type of the command</typeparam>
 | 
	
		
			
				|  |  | -        /// <returns></returns>
 | 
	
		
			
				|  |  | -        internal T GetCommand<T>() where T : SftpCommand
 | 
	
		
			
				|  |  | +        /// <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)
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  | -            T cmd = this._command as T;
 | 
	
		
			
				|  |  | +            // Passing null for exception means no error occurred. 
 | 
	
		
			
				|  |  | +            // This is the common case
 | 
	
		
			
				|  |  | +            this._exception = exception;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            // The _completedState field MUST be set prior calling the callback
 | 
	
		
			
				|  |  | +            Int32 prevState = Interlocked.Exchange(ref this._completedState, completedSynchronously ? _stateCompletedSynchronously : _stateCompletedAsynchronously);
 | 
	
		
			
				|  |  | +            if (prevState != _statePending)
 | 
	
		
			
				|  |  | +                throw new InvalidOperationException("You can set a result only once");
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            // If the event exists, set it
 | 
	
		
			
				|  |  | +            if (this._asyncWaitHandle != null)
 | 
	
		
			
				|  |  | +                this._asyncWaitHandle.Set();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            // If a callback method was set, call it on different thread
 | 
	
		
			
				|  |  | +            if (this._asyncCallback != null)
 | 
	
		
			
				|  |  | +                Task.Factory.StartNew(() => { this._asyncCallback(this); });
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            if (cmd == null)
 | 
	
		
			
				|  |  | +        public void EndInvoke()
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            // This method assumes that only 1 thread calls EndInvoke 
 | 
	
		
			
				|  |  | +            // for this object
 | 
	
		
			
				|  |  | +            if (!this.IsCompleted)
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  | -                throw new InvalidOperationException("Not valid IAsyncResult object.");
 | 
	
		
			
				|  |  | +                // If the operation isn't done, wait for it
 | 
	
		
			
				|  |  | +                this._sftpSession.WaitHandle(this.AsyncWaitHandle, this._commandTimeout);
 | 
	
		
			
				|  |  | +                this.AsyncWaitHandle.Close();
 | 
	
		
			
				|  |  | +                this._asyncWaitHandle = null;  // Allow early GC
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            return cmd;
 | 
	
		
			
				|  |  | +            // Operation is done: if an exception occurred, throw it
 | 
	
		
			
				|  |  | +            if (this._exception != null)
 | 
	
		
			
				|  |  | +                throw _exception;
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          #region IAsyncResult Members
 | 
	
	
		
			
				|  | @@ -57,89 +104,64 @@ namespace Renci.SshNet.Sftp
 | 
	
		
			
				|  |  |          /// 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; private set; }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        /// <summary>
 | 
	
		
			
				|  |  | -        /// Gets a <see cref="T:System.Threading.WaitHandle"/> that is used to wait for an asynchronous operation to complete.
 | 
	
		
			
				|  |  | -        /// </summary>
 | 
	
		
			
				|  |  | -        /// <returns>A <see cref="T:System.Threading.WaitHandle"/> that is used to wait for an asynchronous operation to complete.</returns>
 | 
	
		
			
				|  |  | -        public WaitHandle AsyncWaitHandle { get; private set; }
 | 
	
		
			
				|  |  | +        public Object AsyncState { get { return this._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; private set; }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        /// <summary>
 | 
	
		
			
				|  |  | -        /// Gets a value that indicates whether the asynchronous operation has completed.
 | 
	
		
			
				|  |  | -        /// </summary>
 | 
	
		
			
				|  |  | -        /// <returns>true if the operation is complete; otherwise, false.</returns>
 | 
	
		
			
				|  |  | -        public bool IsCompleted { get; private set; }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        #endregion
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        #region IDisposable Members
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        private bool _disposed = false;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        /// <summary>
 | 
	
		
			
				|  |  | -        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
 | 
	
		
			
				|  |  | -        /// </summary>
 | 
	
		
			
				|  |  | -        public void Dispose()
 | 
	
		
			
				|  |  | +        public Boolean CompletedSynchronously
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  | -            Dispose(true);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            GC.SuppressFinalize(this);
 | 
	
		
			
				|  |  | +            get
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                return Thread.VolatileRead(ref this._completedState) == _stateCompletedSynchronously;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          /// <summary>
 | 
	
		
			
				|  |  | -        /// Releases unmanaged and - optionally - managed resources
 | 
	
		
			
				|  |  | +        /// Gets a <see cref="T:System.Threading.WaitHandle"/> that is used to wait for an asynchronous operation to complete.
 | 
	
		
			
				|  |  |          /// </summary>
 | 
	
		
			
				|  |  | -        /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
 | 
	
		
			
				|  |  | -        protected virtual void Dispose(bool disposing)
 | 
	
		
			
				|  |  | +        /// <returns>A <see cref="T:System.Threading.WaitHandle"/> that is used to wait for an asynchronous operation to complete.</returns>
 | 
	
		
			
				|  |  | +        public WaitHandle AsyncWaitHandle
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  | -            // Check to see if Dispose has already been called.
 | 
	
		
			
				|  |  | -            if (!this._disposed)
 | 
	
		
			
				|  |  | +            get
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  | -                // If disposing equals true, dispose all managed
 | 
	
		
			
				|  |  | -                // and unmanaged resources.
 | 
	
		
			
				|  |  | -                if (disposing)
 | 
	
		
			
				|  |  | +                if (this._asyncWaitHandle == null)
 | 
	
		
			
				|  |  |                  {
 | 
	
		
			
				|  |  | -                    // Dispose managed resources.
 | 
	
		
			
				|  |  | -                    if (this.AsyncWaitHandle != null)
 | 
	
		
			
				|  |  | +                    Boolean done = this.IsCompleted;
 | 
	
		
			
				|  |  | +                    ManualResetEvent mre = new ManualResetEvent(done);
 | 
	
		
			
				|  |  | +                    if (Interlocked.CompareExchange(ref this._asyncWaitHandle, mre, null) != null)
 | 
	
		
			
				|  |  |                      {
 | 
	
		
			
				|  |  | -                        this.AsyncWaitHandle.Dispose();
 | 
	
		
			
				|  |  | -                        this.AsyncWaitHandle = null;
 | 
	
		
			
				|  |  | +                        // Another thread created this object's event; dispose 
 | 
	
		
			
				|  |  | +                        // the event we just created
 | 
	
		
			
				|  |  | +                        mre.Close();
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  | +                    else
 | 
	
		
			
				|  |  | +                    {
 | 
	
		
			
				|  |  | +                        if (!done && this.IsCompleted)
 | 
	
		
			
				|  |  | +                        {
 | 
	
		
			
				|  |  | +                            // If the operation wasn't done when we created 
 | 
	
		
			
				|  |  | +                            // the event but now it is done, set the event
 | 
	
		
			
				|  |  | +                            this._asyncWaitHandle.Set();
 | 
	
		
			
				|  |  | +                        }
 | 
	
		
			
				|  |  |                      }
 | 
	
		
			
				|  |  |                  }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                // Note disposing has been done.
 | 
	
		
			
				|  |  | -                this._disposed = true;
 | 
	
		
			
				|  |  | +                return this._asyncWaitHandle;
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          /// <summary>
 | 
	
		
			
				|  |  | -        /// Releases unmanaged resources and performs other cleanup operations before the
 | 
	
		
			
				|  |  | -        /// <see cref="SftpAsyncResult"/> is reclaimed by garbage collection.
 | 
	
		
			
				|  |  | +        /// Gets a value that indicates whether the asynchronous operation has completed.
 | 
	
		
			
				|  |  |          /// </summary>
 | 
	
		
			
				|  |  | -        ~SftpAsyncResult()
 | 
	
		
			
				|  |  | +        /// <returns>true if the operation is complete; otherwise, false.</returns>
 | 
	
		
			
				|  |  | +        public Boolean IsCompleted
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  | -            // Do not re-create Dispose clean-up code here.
 | 
	
		
			
				|  |  | -            // Calling Dispose(false) is optimal in terms of
 | 
	
		
			
				|  |  | -            // readability and maintainability.
 | 
	
		
			
				|  |  | -            Dispose(false);
 | 
	
		
			
				|  |  | +            get
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                return Thread.VolatileRead(ref this._completedState) != _statePending;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          #endregion
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        /// <summary>
 | 
	
		
			
				|  |  | -        /// Completes asynchronous operation.
 | 
	
		
			
				|  |  | -        /// </summary>
 | 
	
		
			
				|  |  | -        internal void Complete()
 | 
	
		
			
				|  |  | -        {
 | 
	
		
			
				|  |  | -            this.IsCompleted = true;
 | 
	
		
			
				|  |  | -            ((EventWaitHandle)this.AsyncWaitHandle).Set();
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  }
 |