SftpAsyncResult.cs 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. using System;
  2. using System.Threading;
  3. using System.Threading.Tasks;
  4. namespace Renci.SshNet.Sftp
  5. {
  6. /// <summary>
  7. /// Represents the status of an asynchronous SFTP operation.
  8. /// </summary>
  9. public class SftpAsyncResult : IAsyncResult
  10. {
  11. private const int _statePending = 0;
  12. private const int _stateCompletedSynchronously = 1;
  13. private const int _stateCompletedAsynchronously = 2;
  14. private readonly AsyncCallback _asyncCallback;
  15. private readonly Object _asyncState;
  16. private Exception _exception;
  17. private ManualResetEvent _asyncWaitHandle;
  18. private int _completedState = _statePending;
  19. private SftpSession _sftpSession;
  20. private TimeSpan _commandTimeout;
  21. /// <summary>
  22. /// Gets or sets the uploaded bytes.
  23. /// </summary>
  24. /// <value>The uploaded bytes.</value>
  25. public ulong UploadedBytes { get; internal set; }
  26. /// <summary>
  27. /// Gets or sets the downloaded bytes.
  28. /// </summary>
  29. /// <value>The downloaded bytes.</value>
  30. public ulong DownloadedBytes { get; internal set; }
  31. /// <summary>
  32. /// Initializes a new instance of the <see cref="SftpAsyncResult"/> class.
  33. /// </summary>
  34. /// <param name="sftpSession">The SFTP session.</param>
  35. /// <param name="commandTimeout">The command timeout.</param>
  36. /// <param name="asyncCallback">The async callback.</param>
  37. /// <param name="state">The state.</param>
  38. internal SftpAsyncResult(SftpSession sftpSession, TimeSpan commandTimeout, AsyncCallback asyncCallback, object state)
  39. {
  40. this._sftpSession = sftpSession;
  41. this._commandTimeout = commandTimeout;
  42. this._asyncCallback = asyncCallback;
  43. this._asyncState = state;
  44. }
  45. /// <summary>
  46. /// Marks result as completed.
  47. /// </summary>
  48. /// <param name="exception">The exception.</param>
  49. /// <param name="completedSynchronously">if set to <c>true</c> [completed synchronously].</param>
  50. public void SetAsCompleted(Exception exception, bool completedSynchronously)
  51. {
  52. // Passing null for exception means no error occurred.
  53. // This is the common case
  54. this._exception = exception;
  55. // The _completedState field MUST be set prior calling the callback
  56. Int32 prevState = Interlocked.Exchange(ref this._completedState, completedSynchronously ? _stateCompletedSynchronously : _stateCompletedAsynchronously);
  57. if (prevState != _statePending)
  58. throw new InvalidOperationException("You can set a result only once");
  59. // If the event exists, set it
  60. if (this._asyncWaitHandle != null)
  61. this._asyncWaitHandle.Set();
  62. // If a callback method was set, call it on different thread
  63. if (this._asyncCallback != null)
  64. Task.Factory.StartNew(() => { this._asyncCallback(this); });
  65. }
  66. public void EndInvoke()
  67. {
  68. // This method assumes that only 1 thread calls EndInvoke
  69. // for this object
  70. if (!this.IsCompleted)
  71. {
  72. // If the operation isn't done, wait for it
  73. this._sftpSession.WaitHandle(this.AsyncWaitHandle, this._commandTimeout);
  74. this.AsyncWaitHandle.Close();
  75. this._asyncWaitHandle = null; // Allow early GC
  76. }
  77. // Operation is done: if an exception occurred, throw it
  78. if (this._exception != null)
  79. throw _exception;
  80. }
  81. #region IAsyncResult Members
  82. /// <summary>
  83. /// Gets a user-defined object that qualifies or contains information about an asynchronous operation.
  84. /// </summary>
  85. /// <returns>A user-defined object that qualifies or contains information about an asynchronous operation.</returns>
  86. public Object AsyncState { get { return this._asyncState; } }
  87. /// <summary>
  88. /// Gets a value that indicates whether the asynchronous operation completed synchronously.
  89. /// </summary>
  90. /// <returns>true if the asynchronous operation completed synchronously; otherwise, false.</returns>
  91. public Boolean CompletedSynchronously
  92. {
  93. get
  94. {
  95. return Thread.VolatileRead(ref this._completedState) == _stateCompletedSynchronously;
  96. }
  97. }
  98. /// <summary>
  99. /// Gets a <see cref="T:System.Threading.WaitHandle"/> that is used to wait for an asynchronous operation to complete.
  100. /// </summary>
  101. /// <returns>A <see cref="T:System.Threading.WaitHandle"/> that is used to wait for an asynchronous operation to complete.</returns>
  102. public WaitHandle AsyncWaitHandle
  103. {
  104. get
  105. {
  106. if (this._asyncWaitHandle == null)
  107. {
  108. Boolean done = this.IsCompleted;
  109. ManualResetEvent mre = new ManualResetEvent(done);
  110. if (Interlocked.CompareExchange(ref this._asyncWaitHandle, mre, null) != null)
  111. {
  112. // Another thread created this object's event; dispose
  113. // the event we just created
  114. mre.Close();
  115. }
  116. else
  117. {
  118. if (!done && this.IsCompleted)
  119. {
  120. // If the operation wasn't done when we created
  121. // the event but now it is done, set the event
  122. this._asyncWaitHandle.Set();
  123. }
  124. }
  125. }
  126. return this._asyncWaitHandle;
  127. }
  128. }
  129. /// <summary>
  130. /// Gets a value that indicates whether the asynchronous operation has completed.
  131. /// </summary>
  132. /// <returns>true if the operation is complete; otherwise, false.</returns>
  133. public Boolean IsCompleted
  134. {
  135. get
  136. {
  137. return Thread.VolatileRead(ref this._completedState) != _statePending;
  138. }
  139. }
  140. #endregion
  141. }
  142. }