瀏覽代碼

Bug fix: Hang when the stream returned by Session.GetFile is closed before the download finished

Source commit: da8e7f9243b6815c31e57b70103c951e299b7659
Martin Prikryl 4 年之前
父節點
當前提交
70c74e7a2b
共有 3 個文件被更改,包括 40 次插入22 次删除
  1. 4 1
      dotnet/Session.cs
  2. 21 11
      dotnet/internal/ExeSessionProcess.cs
  3. 15 10
      dotnet/internal/PipeStream.cs

+ 4 - 1
dotnet/Session.cs

@@ -1119,6 +1119,7 @@ namespace WinSCP
                     }
                     if (downloadFound)
                     {
+                        Logger.WriteLine("Download stream started");
                         callstackAndLock.DisarmLock();
                         stream.OnDispose = onGetEndWithExit;
                         return stream;
@@ -2295,7 +2296,9 @@ namespace WinSCP
             if (_throwStdOut && (_process.StdOut != null) && _process.StdOut.ReadAvailable(1))
             {
                 // This is here to return from GetFile asap (?)
-                throw Logger.WriteException(new StdOutException());
+                // Not logging the exception, as it's not really an exception.
+                Logger.WriteLine("Got data");
+                throw new StdOutException();
             }
         }
 

+ 21 - 11
dotnet/internal/ExeSessionProcess.cs

@@ -278,22 +278,31 @@ namespace WinSCP
         {
             using (_logger.CreateCallstack())
             {
-                while (!AbortedOrExited())
+                try
                 {
-                    _logger.WriteLineLevel(1, "Waiting for request event");
-                    // Keep in sync with a delay in SessionLogReader.DoRead
-                    if (_requestEvent.WaitOne(100, false))
+                    while (!AbortedOrExited())
                     {
-                        _logger.WriteLineLevel(1, "Got request event");
-                        ProcessEvent();
-                    }
+                        _logger.WriteLineLevel(1, "Waiting for request event");
+                        // Keep in sync with a delay in SessionLogReader.DoRead
+                        if (_requestEvent.WaitOne(100, false))
+                        {
+                            _logger.WriteLineLevel(1, "Got request event");
+                            ProcessEvent();
+                        }
 
-                    if (_logger.LogLevel >= 1)
-                    {
-                        _logger.WriteLine(string.Format(CultureInfo.InvariantCulture, "2nd generation collection count: {0}", GC.CollectionCount(2)));
-                        _logger.WriteLine(string.Format(CultureInfo.InvariantCulture, "Total memory allocated: {0}", GC.GetTotalMemory(false)));
+                        if (_logger.LogLevel >= 1)
+                        {
+                            _logger.WriteLine(string.Format(CultureInfo.InvariantCulture, "2nd generation collection count: {0}", GC.CollectionCount(2)));
+                            _logger.WriteLine(string.Format(CultureInfo.InvariantCulture, "Total memory allocated: {0}", GC.GetTotalMemory(false)));
+                        }
                     }
                 }
+                catch (Exception e)
+                {
+                    _logger.WriteLine("Error while processing events");
+                    _logger.WriteException(e);
+                    throw;
+                }
             }
         }
 
@@ -343,6 +352,7 @@ namespace WinSCP
                 }
 
                 _responseEvent.Set();
+                _logger.WriteLineLevel(1, "Response event set");
             }
         }
 

+ 15 - 10
dotnet/internal/PipeStream.cs

@@ -219,7 +219,6 @@ namespace WinSCP
                 throw new ArgumentException("The sum of offset and count is greater than the buffer length.");
             if (offset < 0 || count < 0)
                 throw new ArgumentOutOfRangeException("offset", "offset or count is negative.");
-            CheckDisposed();
             if (count == 0)
                 return;
             if (_closedWrite)
@@ -231,15 +230,18 @@ namespace WinSCP
                 while (_buffer.Count >= MaxBufferLength)
                     Monitor.Wait(_buffer);
 
-                _isFlushed = false; // if it were flushed before, it soon will not be.
-
-                // queue up the buffer data
-                for (var i = offset; i < offset + count; i++)
+                if (!_isDisposed)
                 {
-                    _buffer.Enqueue(buffer[i]);
-                }
+                    _isFlushed = false; // if it were flushed before, it soon will not be.
 
-                Monitor.Pulse(_buffer); // signal that write has occurred
+                    // queue up the buffer data
+                    for (var i = offset; i < offset + count; i++)
+                    {
+                        _buffer.Enqueue(buffer[i]);
+                    }
+
+                    Monitor.Pulse(_buffer); // signal that write has occurred
+                }
             }
         }
 
@@ -259,8 +261,10 @@ namespace WinSCP
                 lock (_buffer)
                 {
                     _isDisposed = true;
+                    _buffer.Clear();
                     Monitor.Pulse(_buffer);
                 }
+
                 Closed();
             }
         }
@@ -343,8 +347,9 @@ namespace WinSCP
         {
             lock (_buffer)
             {
-                CheckDisposed();
-                if (!_closedWrite)
+                // This can be called while the stream is disposed, when the download has finishes
+                // (TransferOut with Len=0 is processed) only after Close/Dispose is called
+                if (!_closedWrite && !_isDisposed)
                 {
                     _closedWrite = true;
                     Monitor.Pulse(_buffer);