I use renci.sshnet in a multi-threaded/multi-concurrent-connections environment. Under heavy testing we were seeing a leakage of around 1,500 event handles a minute. Under normal use, the leak was still eveident; with heavy use causing the system to run out of resources and crash after weeks of continuous operations.
Initially it was puzzling why the dispose was not being called on several of the classes creating event handles for synchronization. With the hope of clarity I will divide the proposed code changes out, file by file, discussing only the fix rational relevant to the file being discussed:
> Using Revision 28765 - BaseClient.cs
At first I thought this change was significant because we were taking the approach of explicity calling the __Dispose()__ method as objects went out of scope. This turned out to not be the main cause of the handle leak, and most of our changes to explicitly call the __Dispose()__ method were retracted. Though an optional and minor change, I left this one in because the session object's life span is longer, warrenting an explicit call to __Dispose()__. Please consider the following code block (lines 93 through 120):
```
public BaseClient(ConnectionInfo connectionInfo)
{
if (connectionInfo == null)
throw new ArgumentNullException("connectionInfo");
this.ConnectionInfo = connectionInfo;
this.Session = new Session(connectionInfo);
//! The above session object is immediately replaced upon Connect()
}
/// <summary>
/// Connects client to the server.
/// </summary>
public void Connect()
{
this.OnConnecting();
if (this.IsConnected)
{
this.Session.Disconnect();
}
//! Session object created in the constructor is abandoned
//! Cleanup session resources prior to replacement.
if (this.Session != null)
this.Session.Dispose();
this.Session = new Session(this.ConnectionInfo);
this.Session.HostKeyReceived += Session_HostKeyReceived;
this.Session.ErrorOccured += Session_ErrorOccured;
this.Session.Connect();
this.OnConnected();
}
```
Note, the more agressive fix would be to either keep the session object instantiated from the constructor or wait to instantiate the session object from the __Connect()__ method. I chose another option to simply call the __Session.Dispose()__ method prior to the Session object's replacement in order to return system resources more quickly. This is the only benefit, as it was found that the event handle leak was not related to this double construction.
Initially it was puzzling why the dispose was not being called on several of the classes creating event handles for synchronization. With the hope of clarity I will divide the proposed code changes out, file by file, discussing only the fix rational relevant to the file being discussed:
> Using Revision 28765 - BaseClient.cs
At first I thought this change was significant because we were taking the approach of explicity calling the __Dispose()__ method as objects went out of scope. This turned out to not be the main cause of the handle leak, and most of our changes to explicitly call the __Dispose()__ method were retracted. Though an optional and minor change, I left this one in because the session object's life span is longer, warrenting an explicit call to __Dispose()__. Please consider the following code block (lines 93 through 120):
```
public BaseClient(ConnectionInfo connectionInfo)
{
if (connectionInfo == null)
throw new ArgumentNullException("connectionInfo");
this.ConnectionInfo = connectionInfo;
this.Session = new Session(connectionInfo);
//! The above session object is immediately replaced upon Connect()
}
/// <summary>
/// Connects client to the server.
/// </summary>
public void Connect()
{
this.OnConnecting();
if (this.IsConnected)
{
this.Session.Disconnect();
}
//! Session object created in the constructor is abandoned
//! Cleanup session resources prior to replacement.
if (this.Session != null)
this.Session.Dispose();
this.Session = new Session(this.ConnectionInfo);
this.Session.HostKeyReceived += Session_HostKeyReceived;
this.Session.ErrorOccured += Session_ErrorOccured;
this.Session.Connect();
this.OnConnected();
}
```
Note, the more agressive fix would be to either keep the session object instantiated from the constructor or wait to instantiate the session object from the __Connect()__ method. I chose another option to simply call the __Session.Dispose()__ method prior to the Session object's replacement in order to return system resources more quickly. This is the only benefit, as it was found that the event handle leak was not related to this double construction.