Переглянути джерело

Avoid event-based memory leak when client is not disposing.

drieseng 9 роки тому
батько
коміт
e09239ef0e
2 змінених файлів з 45 додано та 30 видалено
  1. 1 1
      src/Renci.SshNet/ShellStream.cs
  2. 44 29
      src/Renci.SshNet/SubsystemSession.cs

+ 1 - 1
src/Renci.SshNet/ShellStream.cs

@@ -698,7 +698,7 @@ namespace Renci.SshNet
         }
 
         /// <summary>
-        /// Unsubscribes the current <see cref="Shell"/> from session events.
+        /// Unsubscribes the current <see cref="ShellStream"/> from session events.
         /// </summary>
         /// <param name="session">The session.</param>
         /// <remarks>

+ 44 - 29
src/Renci.SshNet/SubsystemSession.cs

@@ -126,11 +126,7 @@ namespace Renci.SshNet
         /// </summary>
         public void Disconnect()
         {
-            if (_session != null)
-            {
-                _session.ErrorOccured -= Session_ErrorOccured;
-                _session.Disconnected -= Session_Disconnected;
-            }
+            UnsubscribeFromSessionEvents(_session);
 
             if (_channel != null)
             {
@@ -274,6 +270,22 @@ namespace Renci.SshNet
                 throw new InvalidOperationException("The session is not open.");
         }
 
+        /// <summary>
+        /// Unsubscribes the current <see cref="SubsystemSession"/> from session events.
+        /// </summary>
+        /// <param name="session">The session.</param>
+        /// <remarks>
+        /// Does nothing when <paramref name="session"/> is <c>null</c>.
+        /// </remarks>
+        private void UnsubscribeFromSessionEvents(ISession session)
+        {
+            if (session == null)
+                return;
+
+            session.Disconnected -= Session_Disconnected;
+            session.ErrorOccured -= Session_ErrorOccured;
+        }
+
         #region IDisposable Members
 
         private bool _isDisposed;
@@ -293,36 +305,39 @@ namespace Renci.SshNet
         /// <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)
         {
-            // Check to see if Dispose has already been called.
-            if (!_isDisposed)
+            if (_isDisposed)
+                return;
+
+            if (disposing)
             {
-                if (disposing)
+                Disconnect();
+
+                _session = null;
+
+                if (_errorOccuredWaitHandle != null)
+                {
+                    _errorOccuredWaitHandle.Dispose();
+                    _errorOccuredWaitHandle = null;
+                }
+
+                if (_sessionDisconnectedWaitHandle != null)
                 {
-                    Disconnect();
-
-                    _session = null;
-
-                    if (_errorOccuredWaitHandle != null)
-                    {
-                        _errorOccuredWaitHandle.Dispose();
-                        _errorOccuredWaitHandle = null;
-                    }
-
-                    if (_sessionDisconnectedWaitHandle != null)
-                    {
-                        _sessionDisconnectedWaitHandle.Dispose();
-                        _sessionDisconnectedWaitHandle = null;
-                    }
-
-                    if (_channelClosedWaitHandle != null)
-                    {
-                        _channelClosedWaitHandle.Dispose();
-                        _channelClosedWaitHandle = null;
-                    }
+                    _sessionDisconnectedWaitHandle.Dispose();
+                    _sessionDisconnectedWaitHandle = null;
+                }
+
+                if (_channelClosedWaitHandle != null)
+                {
+                    _channelClosedWaitHandle.Dispose();
+                    _channelClosedWaitHandle = null;
                 }
 
                 _isDisposed = true;
             }
+            else
+            {
+                UnsubscribeFromSessionEvents(_session);
+            }
         }
 
         /// <summary>