|  | @@ -2419,102 +2419,110 @@ namespace Renci.SshNet
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              ulong offset = 0;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            // create buffer of optimal length
 | 
	
		
			
				|  |  | -            var buffer = new byte[_sftpSession.CalculateOptimalWriteLength(_bufferSize, handle)];
 | 
	
		
			
				|  |  | +            // create buffer of optimal length using ArrayPool
 | 
	
		
			
				|  |  | +            var bufferLength = (int)_sftpSession.CalculateOptimalWriteLength(_bufferSize, handle);
 | 
	
		
			
				|  |  | +            var buffer = ArrayPool<byte>.Shared.Rent(bufferLength);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            var expectedResponses = 0;
 | 
	
		
			
				|  |  | +            try
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                var expectedResponses = 0;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            // We will send out all the write requests without waiting for each response.
 | 
	
		
			
				|  |  | -            // Afterwards, we may wait on this handle until all responses are received
 | 
	
		
			
				|  |  | -            // or an error has occurred.
 | 
	
		
			
				|  |  | -            using var mres = new ManualResetEventSlim(initialState: false);
 | 
	
		
			
				|  |  | +                // We will send out all the write requests without waiting for each response.
 | 
	
		
			
				|  |  | +                // Afterwards, we may wait on this handle until all responses are received
 | 
	
		
			
				|  |  | +                // or an error has occurred.
 | 
	
		
			
				|  |  | +                using var mres = new ManualResetEventSlim(initialState: false);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            ExceptionDispatchInfo? exception = null;
 | 
	
		
			
				|  |  | +                ExceptionDispatchInfo? exception = null;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            while (true)
 | 
	
		
			
				|  |  | -            {
 | 
	
		
			
				|  |  | -                var bytesRead = isAsync
 | 
	
		
			
				|  |  | +                while (true)
 | 
	
		
			
				|  |  | +                {
 | 
	
		
			
				|  |  | +                    var bytesRead = isAsync
 | 
	
		
			
				|  |  |  #if NET
 | 
	
		
			
				|  |  | -                    ? await input.ReadAsync(buffer, cancellationToken).ConfigureAwait(false)
 | 
	
		
			
				|  |  | +                        ? await input.ReadAsync(buffer.AsMemory(0, bufferLength), cancellationToken).ConfigureAwait(false)
 | 
	
		
			
				|  |  |  #else
 | 
	
		
			
				|  |  | -                    ? await input.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)
 | 
	
		
			
				|  |  | +                        ? await input.ReadAsync(buffer, 0, bufferLength, cancellationToken).ConfigureAwait(false)
 | 
	
		
			
				|  |  |  #endif
 | 
	
		
			
				|  |  | -                    : input.Read(buffer, 0, buffer.Length);
 | 
	
		
			
				|  |  | +                        : input.Read(buffer, 0, bufferLength);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                if (bytesRead == 0)
 | 
	
		
			
				|  |  | -                {
 | 
	
		
			
				|  |  | -                    break;
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                if (asyncResult is not null && asyncResult.IsUploadCanceled)
 | 
	
		
			
				|  |  | -                {
 | 
	
		
			
				|  |  | -                    break;
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | +                    if (bytesRead == 0)
 | 
	
		
			
				|  |  | +                    {
 | 
	
		
			
				|  |  | +                        break;
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                exception?.Throw();
 | 
	
		
			
				|  |  | +                    if (asyncResult is not null && asyncResult.IsUploadCanceled)
 | 
	
		
			
				|  |  | +                    {
 | 
	
		
			
				|  |  | +                        break;
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                var writtenBytes = offset + (ulong)bytesRead;
 | 
	
		
			
				|  |  | +                    exception?.Throw();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                _ = Interlocked.Increment(ref expectedResponses);
 | 
	
		
			
				|  |  | -                mres.Reset();
 | 
	
		
			
				|  |  | +                    var writtenBytes = offset + (ulong)bytesRead;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                _sftpSession.RequestWrite(handle, offset, buffer, offset: 0, bytesRead, wait: null, s =>
 | 
	
		
			
				|  |  | -                {
 | 
	
		
			
				|  |  | -                    var setHandle = false;
 | 
	
		
			
				|  |  | +                    _ = Interlocked.Increment(ref expectedResponses);
 | 
	
		
			
				|  |  | +                    mres.Reset();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                    try
 | 
	
		
			
				|  |  | +                    _sftpSession.RequestWrite(handle, offset, buffer, offset: 0, bytesRead, wait: null, s =>
 | 
	
		
			
				|  |  |                      {
 | 
	
		
			
				|  |  | -                        if (Sftp.SftpSession.GetSftpException(s) is Exception ex)
 | 
	
		
			
				|  |  | -                        {
 | 
	
		
			
				|  |  | -                            exception = ExceptionDispatchInfo.Capture(ex);
 | 
	
		
			
				|  |  | -                        }
 | 
	
		
			
				|  |  | +                        var setHandle = false;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                        if (exception is not null)
 | 
	
		
			
				|  |  | +                        try
 | 
	
		
			
				|  |  |                          {
 | 
	
		
			
				|  |  | -                            setHandle = true;
 | 
	
		
			
				|  |  | -                            return;
 | 
	
		
			
				|  |  | -                        }
 | 
	
		
			
				|  |  | +                            if (Sftp.SftpSession.GetSftpException(s) is Exception ex)
 | 
	
		
			
				|  |  | +                            {
 | 
	
		
			
				|  |  | +                                exception = ExceptionDispatchInfo.Capture(ex);
 | 
	
		
			
				|  |  | +                            }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                        Debug.Assert(s.StatusCode == StatusCode.Ok);
 | 
	
		
			
				|  |  | +                            if (exception is not null)
 | 
	
		
			
				|  |  | +                            {
 | 
	
		
			
				|  |  | +                                setHandle = true;
 | 
	
		
			
				|  |  | +                                return;
 | 
	
		
			
				|  |  | +                            }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                        asyncResult?.Update(writtenBytes);
 | 
	
		
			
				|  |  | +                            Debug.Assert(s.StatusCode == StatusCode.Ok);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                        // Call callback to report number of bytes written
 | 
	
		
			
				|  |  | -                        if (uploadCallback is not null)
 | 
	
		
			
				|  |  | -                        {
 | 
	
		
			
				|  |  | -                            // Execute callback on different thread
 | 
	
		
			
				|  |  | -                            ThreadAbstraction.ExecuteThread(() => uploadCallback(writtenBytes));
 | 
	
		
			
				|  |  | +                            asyncResult?.Update(writtenBytes);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                            // Call callback to report number of bytes written
 | 
	
		
			
				|  |  | +                            if (uploadCallback is not null)
 | 
	
		
			
				|  |  | +                            {
 | 
	
		
			
				|  |  | +                                // Execute callback on different thread
 | 
	
		
			
				|  |  | +                                ThreadAbstraction.ExecuteThread(() => uploadCallback(writtenBytes));
 | 
	
		
			
				|  |  | +                            }
 | 
	
		
			
				|  |  |                          }
 | 
	
		
			
				|  |  | -                    }
 | 
	
		
			
				|  |  | -                    finally
 | 
	
		
			
				|  |  | -                    {
 | 
	
		
			
				|  |  | -                        if (Interlocked.Decrement(ref expectedResponses) == 0 || setHandle)
 | 
	
		
			
				|  |  | +                        finally
 | 
	
		
			
				|  |  |                          {
 | 
	
		
			
				|  |  | -                            mres.Set();
 | 
	
		
			
				|  |  | +                            if (Interlocked.Decrement(ref expectedResponses) == 0 || setHandle)
 | 
	
		
			
				|  |  | +                            {
 | 
	
		
			
				|  |  | +                                mres.Set();
 | 
	
		
			
				|  |  | +                            }
 | 
	
		
			
				|  |  |                          }
 | 
	
		
			
				|  |  | -                    }
 | 
	
		
			
				|  |  | -                });
 | 
	
		
			
				|  |  | +                    });
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                offset += (ulong)bytesRead;
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | +                    offset += (ulong)bytesRead;
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            // Make sure the read of exception cannot be executed ahead of
 | 
	
		
			
				|  |  | -            // the read of expectedResponses so that we do not miss an
 | 
	
		
			
				|  |  | -            // exception.
 | 
	
		
			
				|  |  | +                // Make sure the read of exception cannot be executed ahead of
 | 
	
		
			
				|  |  | +                // the read of expectedResponses so that we do not miss an
 | 
	
		
			
				|  |  | +                // exception.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            if (Volatile.Read(ref expectedResponses) != 0)
 | 
	
		
			
				|  |  | -            {
 | 
	
		
			
				|  |  | -                if (isAsync)
 | 
	
		
			
				|  |  | +                if (Volatile.Read(ref expectedResponses) != 0)
 | 
	
		
			
				|  |  |                  {
 | 
	
		
			
				|  |  | -                    await _sftpSession.WaitOnHandleAsync(mres.WaitHandle, _operationTimeout, cancellationToken).ConfigureAwait(false);
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | -                else
 | 
	
		
			
				|  |  | -                {
 | 
	
		
			
				|  |  | -                    _sftpSession.WaitOnHandle(mres.WaitHandle, _operationTimeout);
 | 
	
		
			
				|  |  | +                    if (isAsync)
 | 
	
		
			
				|  |  | +                    {
 | 
	
		
			
				|  |  | +                        await _sftpSession.WaitOnHandleAsync(mres.WaitHandle, _operationTimeout, cancellationToken).ConfigureAwait(false);
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  | +                    else
 | 
	
		
			
				|  |  | +                    {
 | 
	
		
			
				|  |  | +                        _sftpSession.WaitOnHandle(mres.WaitHandle, _operationTimeout);
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  |                  }
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            exception?.Throw();
 | 
	
		
			
				|  |  | +                exception?.Throw();
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            finally
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                ArrayPool<byte>.Shared.Return(buffer);
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              if (isAsync)
 | 
	
		
			
				|  |  |              {
 |