Sfoglia il codice sorgente

Extract thread related operation to NET40 partial classes
Replace SemaphoreSlim with custom SemaphoreLight class

olegkap_cp 14 anni fa
parent
commit
18b6eafaaa

+ 1 - 1
Renci.SshClient/Renci.SshNet/Channels/Channel.cs

@@ -146,7 +146,7 @@ namespace Renci.SshNet.Channels
         /// Gets the session semaphore to control number of session channels
         /// </summary>
         /// <value>The session semaphore.</value>
-        protected SemaphoreSlim SessionSemaphore
+        protected SemaphoreLight SessionSemaphore
         {
             get
             {

+ 16 - 0
Renci.SshClient/Renci.SshNet/Channels/ChannelDirectTcpip.NET40.cs

@@ -0,0 +1,16 @@
+using System.Threading.Tasks;
+using System;
+
+namespace Renci.SshNet.Channels
+{
+    /// <summary>
+    /// Implements "direct-tcpip" SSH channel.
+    /// </summary>
+    internal partial class ChannelDirectTcpip 
+    {
+        partial void ExecuteThread(Action action)
+        {
+            Task.Factory.StartNew(action, TaskCreationOptions.LongRunning);
+        }
+    }
+}

+ 17 - 6
Renci.SshClient/Renci.SshNet/Channels/ChannelDirectTcpip.cs

@@ -3,7 +3,6 @@ using System.Linq;
 using System.Net;
 using System.Net.Sockets;
 using System.Threading;
-using System.Threading.Tasks;
 using Renci.SshNet.Common;
 using Renci.SshNet.Messages.Connection;
 
@@ -12,7 +11,7 @@ namespace Renci.SshNet.Channels
     /// <summary>
     /// Implements "direct-tcpip" SSH channel.
     /// </summary>
-    internal class ChannelDirectTcpip : Channel
+    internal partial class ChannelDirectTcpip : Channel
     {
         public EventWaitHandle _channelEof = new AutoResetEvent(false);
 
@@ -70,7 +69,10 @@ namespace Renci.SshNet.Channels
             //  Start reading data from the port and send to channel
             EventWaitHandle readerTaskError = new AutoResetEvent(false);
 
-            var readerTask = Task.Factory.StartNew(() =>
+            var readerTaskCompleted = new ManualResetEvent(false);
+            Exception exception = null;
+
+            this.ExecuteThread(() =>
             {
                 try
                 {
@@ -109,10 +111,14 @@ namespace Renci.SshNet.Channels
                         }
                     }
                 }
-                catch (Exception)
+                catch (Exception exp)
                 {
                     readerTaskError.Set();
-                    throw;
+                    exception = exp;
+                }
+                finally
+                {
+                    readerTaskCompleted.Set();
                 }
             });
 
@@ -124,7 +130,10 @@ namespace Renci.SshNet.Channels
             this._socket = null;
 
             //  Wait for task to finish and will throw any errors if any
-            readerTask.Wait();
+            readerTaskCompleted.WaitOne();
+
+            if (exception != null)
+                throw exception;
         }
 
         /// <summary>
@@ -161,6 +170,8 @@ namespace Renci.SshNet.Channels
             this._channelEof.Set();
         }
 
+        partial void ExecuteThread(Action action);
+
         protected override void Dispose(bool disposing)
         {
             if (this._socket != null)

+ 80 - 0
Renci.SshClient/Renci.SshNet/Common/SemaphoreLight.cs

@@ -0,0 +1,80 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading;
+
+namespace Renci.SshNet.Common
+{
+    public class SemaphoreLight
+    {
+        private object _lock = new object();
+
+        private int _currentCount;
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="SemaphoreLight"/> class, specifying 
+        /// the initial number of requests that can be granted concurrently.
+        /// </summary>
+        /// <param name="initialCount">The initial number of requests for the semaphore that can be granted concurrently.</param>
+        public SemaphoreLight(int initialCount)
+        {
+            if (initialCount < 0 )
+                throw new ArgumentOutOfRangeException("The initial argument is negative");
+
+            this._currentCount = initialCount;
+        }
+
+        /// <summary>
+        /// Gets the current count of the <see cref="SemaphoreLight"/>.
+        /// </summary>
+        public int CurrentCount { get { return this._currentCount; } }
+
+        /// <summary>
+        /// Exits the <see cref="SemaphoreLight"/> once.
+        /// </summary>
+        /// <returns>The previous count of the <see cref="SemaphoreLight"/>.</returns>
+        public int Release()
+        {
+            return this.Release(1);
+        }
+
+        /// <summary>
+        /// Exits the <see cref="SemaphoreLight"/> a specified number of times.
+        /// </summary>
+        /// <param name="releaseCount">The number of times to exit the semaphore.</param>
+        /// <returns>The previous count of the <see cref="SemaphoreLight"/>.</returns>
+        public int Release(int releaseCount)
+        {
+            var oldCount = this._currentCount;
+
+            lock (this._lock)
+            {
+                this._currentCount += releaseCount;
+
+                Monitor.Pulse(this._lock);
+            }
+
+            return oldCount;
+        }
+
+        /// <summary>
+        /// Blocks the current thread until it can enter the <see cref="SemaphoreLight"/>.
+        /// </summary>
+        public void Wait()
+        {
+
+            lock (this._lock)
+            {
+                while (this._currentCount < 1)
+                {
+                    Monitor.Wait(this._lock);
+                }
+
+                this._currentCount--;
+
+                Monitor.Pulse(this._lock);
+            }
+        }
+    }
+}

+ 16 - 0
Renci.SshClient/Renci.SshNet/ForwardedPortLocal.NET40.cs

@@ -0,0 +1,16 @@
+using System.Threading.Tasks;
+using System;
+
+namespace Renci.SshNet
+{
+    /// <summary>
+    /// Provides functionality for local port forwarding
+    /// </summary>
+    public partial class ForwardedPortLocal 
+    {
+        partial void ExecuteThread(Action action)
+        {
+            Task.Factory.StartNew(action);
+        }
+    }
+}

+ 18 - 9
Renci.SshClient/Renci.SshNet/ForwardedPortLocal.cs

@@ -1,7 +1,6 @@
 using System;
 using System.Net;
 using System.Net.Sockets;
-using System.Threading.Tasks;
 using Renci.SshNet.Channels;
 using System.Threading;
 
@@ -10,11 +9,11 @@ namespace Renci.SshNet
     /// <summary>
     /// Provides functionality for local port forwarding
     /// </summary>
-    public class ForwardedPortLocal : ForwardedPort, IDisposable
+    public partial class ForwardedPortLocal : ForwardedPort, IDisposable
     {
         private TcpListener _listener;
 
-        private Task _listenerTask;
+        private EventWaitHandle _listenerTaskCompleted;
 
         /// <summary>
         /// Starts local port forwarding.
@@ -30,7 +29,8 @@ namespace Renci.SshNet
             this._listener = new TcpListener(ep);
             this._listener.Start();
 
-            this._listenerTask = Task.Factory.StartNew(() =>
+            this._listenerTaskCompleted = new ManualResetEvent(false);
+            this.ExecuteThread(() =>
             {
                 try
                 {
@@ -38,7 +38,7 @@ namespace Renci.SshNet
                     {
                         var socket = this._listener.AcceptSocket();
 
-                        Task.Factory.StartNew(() =>
+                        this.ExecuteThread(() =>
                         {
                             try
                             {
@@ -68,6 +68,10 @@ namespace Renci.SshNet
                 {
                     this.RaiseExceptionEvent(exp);
                 }
+                finally
+                {
+                    this._listenerTaskCompleted.Set();
+                }
             });
 
             this.IsStarted = true;
@@ -85,11 +89,16 @@ namespace Renci.SshNet
                 return;
 
             this._listener.Stop();
-            this._listenerTask.Wait();
+            //  TODO:   Add timeout to WaitOne method
+            this._listenerTaskCompleted.WaitOne();
+            this._listenerTaskCompleted.Dispose();
+            this._listenerTaskCompleted = null;
 
             this.IsStarted = false;
         }
 
+        partial void ExecuteThread(Action action);
+
         #region IDisposable Members
 
         private bool _isDisposed = false;
@@ -118,10 +127,10 @@ namespace Renci.SshNet
                 if (disposing)
                 {
                     // Dispose managed ResourceMessages.
-                    if (this._listenerTask != null)
+                    if (this._listenerTaskCompleted != null)
                     {
-                        this._listenerTask.Dispose();
-                        this._listenerTask = null;
+                        this._listenerTaskCompleted.Dispose();
+                        this._listenerTaskCompleted = null;
                     }
                 }
 

+ 16 - 0
Renci.SshClient/Renci.SshNet/ForwardedPortRemote.NET40.cs

@@ -0,0 +1,16 @@
+using System;
+using System.Threading.Tasks;
+
+namespace Renci.SshNet
+{
+    /// <summary>
+    /// Provides functionality for remote port forwarding
+    /// </summary>
+    public partial class ForwardedPortRemote
+    {
+        partial void ExecuteThread(Action action)
+        {
+            Task.Factory.StartNew(action);
+        }
+    }
+}

+ 4 - 3
Renci.SshClient/Renci.SshNet/ForwardedPortRemote.cs

@@ -1,6 +1,5 @@
 using System;
 using System.Threading;
-using System.Threading.Tasks;
 using Renci.SshNet.Channels;
 using Renci.SshNet.Messages.Connection;
 using Renci.SshNet.Common;
@@ -12,7 +11,7 @@ namespace Renci.SshNet
     /// <summary>
     /// Provides functionality for remote port forwarding
     /// </summary>
-    public class ForwardedPortRemote : ForwardedPort, IDisposable
+    public partial class ForwardedPortRemote : ForwardedPort, IDisposable
     {
         private bool _requestStatus;
 
@@ -86,7 +85,7 @@ namespace Renci.SshNet
             {
                 if (info.ConnectedAddress == this.BoundHost && info.ConnectedPort == this.BoundPort)
                 {
-                    Task.Factory.StartNew(() =>
+                    this.ExecuteThread(() =>
                     {
                         try
                         {
@@ -121,6 +120,8 @@ namespace Renci.SshNet
             this._globalRequestResponse.Set();
         }
 
+        partial void ExecuteThread(Action action);
+
         #region IDisposable Members
 
         private bool _isDisposed = false;

+ 16 - 0
Renci.SshClient/Renci.SshNet/KeyboardInteractiveConnectionInfo.NET40.cs

@@ -0,0 +1,16 @@
+using System.Threading.Tasks;
+using System;
+
+namespace Renci.SshNet
+{
+    /// <summary>
+    /// Provides connection information when keyboard interactive authentication method is used
+    /// </summary>
+    public partial class KeyboardInteractiveConnectionInfo
+    {
+        partial void ExecuteThread(Action action)
+        {
+            Task.Factory.StartNew(action);
+        }
+    }
+}

+ 4 - 3
Renci.SshClient/Renci.SshNet/KeyboardInteractiveConnectionInfo.cs

@@ -6,14 +6,13 @@ using System.Threading;
 using Renci.SshNet.Messages.Authentication;
 using Renci.SshNet.Messages;
 using Renci.SshNet.Common;
-using System.Threading.Tasks;
 
 namespace Renci.SshNet
 {
     /// <summary>
     /// Provides connection information when keyboard interactive authentication method is used
     /// </summary>
-    public class KeyboardInteractiveConnectionInfo : ConnectionInfo, IDisposable
+    public partial class KeyboardInteractiveConnectionInfo : ConnectionInfo, IDisposable
     {
         private EventWaitHandle _authenticationCompleted = new AutoResetEvent(false);
 
@@ -113,7 +112,7 @@ namespace Renci.SshNet
             {
                 var eventArgs = new AuthenticationPromptEventArgs(this.Username, informationRequestMessage.Instruction, informationRequestMessage.Language, informationRequestMessage.Prompts);
 
-                Task.Factory.StartNew(() =>
+                this.ExecuteThread(() =>
                 {
                     try
                     {
@@ -141,6 +140,8 @@ namespace Renci.SshNet
             }
         }
 
+        partial void ExecuteThread(Action action);
+
         #region IDisposable Members
 
         private bool isDisposed = false;

+ 16 - 0
Renci.SshClient/Renci.SshNet/PasswordConnectionInfo.NET40.cs

@@ -0,0 +1,16 @@
+using System.Threading.Tasks;
+using System;
+
+namespace Renci.SshNet
+{
+    /// <summary>
+    /// Provides connection information when password authentication method is used
+    /// </summary>
+    public partial class PasswordConnectionInfo 
+    {
+        partial void ExecuteThread(Action action)
+        {
+            Task.Factory.StartNew(action);
+        }
+    }
+}

+ 4 - 3
Renci.SshClient/Renci.SshNet/PasswordConnectionInfo.cs

@@ -3,7 +3,6 @@ using System.Collections.Generic;
 using System.Linq;
 using System.Text;
 using System.Threading;
-using System.Threading.Tasks;
 using Renci.SshNet.Messages.Authentication;
 using Renci.SshNet.Common;
 using Renci.SshNet.Messages;
@@ -13,7 +12,7 @@ namespace Renci.SshNet
     /// <summary>
     /// Provides connection information when password authentication method is used
     /// </summary>
-    public class PasswordConnectionInfo : ConnectionInfo, IDisposable
+    public partial class PasswordConnectionInfo : ConnectionInfo, IDisposable
     {
         private EventWaitHandle _authenticationCompleted = new AutoResetEvent(false);
 
@@ -120,7 +119,7 @@ namespace Renci.SshNet
             {
                 this.Session.UnRegisterMessage("SSH_MSG_USERAUTH_PASSWD_CHANGEREQ");
 
-                Task.Factory.StartNew(() =>
+                this.ExecuteThread(() =>
                 {
                     try
                     {
@@ -144,6 +143,8 @@ namespace Renci.SshNet
             }
         }
 
+        partial void ExecuteThread(Action action);
+
         #region IDisposable Members
 
         private bool isDisposed = false;

+ 21 - 3
Renci.SshClient/Renci.SshNet/Renci.SshNet.csproj

@@ -62,6 +62,7 @@
     <Reference Include="System.Xml" />
   </ItemGroup>
   <ItemGroup>
+    <Compile Include="Channels\ChannelDirectTcpip.NET40.cs" />
     <Compile Include="Common\AsyncResult.cs" />
     <Compile Include="Common\AuthenticationBannerEventArgs.cs" />
     <Compile Include="Common\AuthenticationEventArgs.cs" />
@@ -75,6 +76,9 @@
     <Compile Include="Common\ChannelEventArgs.cs" />
     <Compile Include="Common\ChannelOpenFailedEventArgs.cs" />
     <Compile Include="Common\ChannelRequestEventArgs.cs" />
+    <Compile Include="Common\SemaphoreLight.cs">
+      <SubType>Code</SubType>
+    </Compile>
     <Compile Include="Common\SftpPathNotFoundException.NET40.cs" />
     <Compile Include="Common\SftpPermissionDeniedException.NET40.cs" />
     <Compile Include="Common\SshAuthenticationException.NET40.cs" />
@@ -91,6 +95,23 @@
     <Compile Include="Common\SshOperationTimeoutException.NET40.cs" />
     <Compile Include="Common\SshPassPhraseNullOrEmptyException.cs" />
     <Compile Include="Common\SftpPermissionDeniedException.cs" />
+    <Compile Include="ForwardedPortLocal.cs">
+      <SubType>Code</SubType>
+    </Compile>
+    <Compile Include="Security\Cryptography\Aes.cs">
+      <SubType>Code</SubType>
+    </Compile>
+    <Compile Include="Security\Cryptography\Serpent.cs">
+      <SubType>Code</SubType>
+    </Compile>
+    <Compile Include="Shell.NET40.cs" />
+    <Compile Include="SftpClient.NET40.cs" />
+    <Compile Include="PasswordConnectionInfo.NET40.cs" />
+    <Compile Include="KeyboardInteractiveConnectionInfo.NET40.cs" />
+    <Compile Include="ForwardedPortRemote.NET40.cs" />
+    <Compile Include="ForwardedPortLocal.NET40.cs" />
+    <Compile Include="SshCommand.NET40.cs" />
+    <Compile Include="Session.NET40.cs" />
     <Compile Include="KeyboardInteractiveConnectionInfo.cs" />
     <Compile Include="Messages\Authentication\RequestMessageKeyboardInteractive.cs" />
     <Compile Include="Messages\Authentication\RequestMessageNone.cs" />
@@ -117,7 +138,6 @@
     <Compile Include="Security\CipherAESCTR.cs" />
     <Compile Include="Security\CipherCast.cs" />
     <Compile Include="Security\CipherSerpent.cs" />
-    <Compile Include="Security\Cryptography\Aes.cs" />
     <Compile Include="Security\Cryptography\Cast.cs" />
     <Compile Include="Security\Cryptography\Ciphers\AesCipher.cs" />
     <Compile Include="Security\Cryptography\Blowfish.cs" />
@@ -131,7 +151,6 @@
     <Compile Include="Security\Cryptography\Modes\ModeBase.cs" />
     <Compile Include="Security\Cryptography\Modes\CbcMode.cs" />
     <Compile Include="Security\Cryptography\Ciphers\CipherBase.cs" />
-    <Compile Include="Security\Cryptography\Serpent.cs" />
     <Compile Include="Security\Cryptography\TripleDes.cs" />
     <Compile Include="Security\Cryptography\Ciphers\TripleDesCipher.cs" />
     <Compile Include="Security\Cryptography\Modes\CtrMode.cs" />
@@ -211,7 +230,6 @@
     <Compile Include="Channels\ChannelSession.cs" />
     <Compile Include="Common\SshException.cs" />
     <Compile Include="ForwardedPort.cs" />
-    <Compile Include="ForwardedPortLocal.cs" />
     <Compile Include="ForwardedPortRemote.cs" />
     <Compile Include="Messages\Connection\GlobalRequestName.cs" />
     <Compile Include="Security\Algorithm.cs" />

+ 0 - 1
Renci.SshClient/Renci.SshNet/Security/Cryptography/Modes/CbcMode.cs

@@ -2,7 +2,6 @@
 using System.Collections.Generic;
 using System.Linq;
 using System.Text;
-using System.Threading.Tasks;
 using System.Globalization;
 
 namespace Renci.SshNet.Security.Cryptography

+ 38 - 0
Renci.SshClient/Renci.SshNet/Session.NET40.cs

@@ -0,0 +1,38 @@
+using System.Threading.Tasks;
+using System.Linq;
+using System;
+
+namespace Renci.SshNet
+{
+    /// <summary>
+    /// Provides functionality to connect and interact with SSH server.
+    /// </summary>
+    public partial class Session
+    {
+        partial void ExecuteThread(Action action)
+        {
+            Task.Factory.StartNew(action, TaskCreationOptions.LongRunning);
+        }
+
+        partial void InternalRegisterMessage(string messageName)
+        {
+            lock (this._messagesMetadata)
+            {
+                Parallel.ForEach(
+                    from m in this._messagesMetadata where m.Name == messageName select m,
+                    (item) => { item.Enabled = true; item.Activated = true; });
+            }
+        }
+
+        partial void InternalUnRegisterMessage(string messageName)
+        {
+            lock (this._messagesMetadata)
+            {
+                Parallel.ForEach(
+                    from m in this._messagesMetadata where m.Name == messageName select m,
+                    (item) => { item.Enabled = false; item.Activated = false; });
+            }
+        }
+
+    }
+}

+ 36 - 34
Renci.SshClient/Renci.SshNet/Session.cs

@@ -8,7 +8,6 @@ using System.Security.Cryptography;
 using System.Text;
 using System.Text.RegularExpressions;
 using System.Threading;
-using System.Threading.Tasks;
 using Renci.SshNet.Channels;
 using Renci.SshNet.Common;
 using Renci.SshNet.Compression;
@@ -24,7 +23,7 @@ namespace Renci.SshNet
     /// <summary>
     /// Provides functionality to connect and interact with SSH server.
     /// </summary>
-    public class Session : IDisposable
+    public partial class Session : IDisposable
     {
         /// <summary>
         /// Specifies maximum packet size defined by the protocol.
@@ -46,7 +45,7 @@ namespace Renci.SshNet
         /// <remarks>
         /// Some server may restrict number to prevent authentication attacks
         /// </remarks>
-        private static SemaphoreSlim _authenticationConnection = new SemaphoreSlim(3);
+        private static SemaphoreLight _authenticationConnection = new SemaphoreLight(3);
 
         /// <summary>
         /// Holds metada about session messages
@@ -61,7 +60,7 @@ namespace Renci.SshNet
         /// <summary>
         /// Holds reference to task that listens for incoming messages
         /// </summary>
-        private Task _messageListener;
+        private EventWaitHandle _messageListenerCompleted;
 
         /// <summary>
         /// Specifies outbound packet number
@@ -117,12 +116,12 @@ namespace Renci.SshNet
 
         private Compressor _clientCompression;
 
-        private SemaphoreSlim _sessionSemaphore;
+        private SemaphoreLight _sessionSemaphore;
         /// <summary>
         /// Gets the session semaphore that controls session channels.
         /// </summary>
         /// <value>The session semaphore.</value>
-        public SemaphoreSlim SessionSemaphore
+        public SemaphoreLight SessionSemaphore
         {
             get
             {
@@ -132,7 +131,7 @@ namespace Renci.SshNet
                     {
                         if (this._sessionSemaphore == null)
                         {
-                            this._sessionSemaphore = new SemaphoreSlim(this.ConnectionInfo.MaxSessions);
+                            this._sessionSemaphore = new SemaphoreLight(this.ConnectionInfo.MaxSessions);
                         }
                     }
                 }
@@ -173,7 +172,7 @@ namespace Renci.SshNet
         {
             get
             {
-                return this._socket != null && this._socket.Connected && this._isAuthenticated && this._messageListener.Status == TaskStatus.Running;
+                return this._socket != null && this._socket.Connected && this._isAuthenticated && this._messageListenerCompleted != null;
             }
         }
 
@@ -492,7 +491,19 @@ namespace Renci.SshNet
                     this.RegisterMessage("SSH_MSG_USERAUTH_BANNER");
 
                     //  Start incoming request listener
-                    this._messageListener = Task.Factory.StartNew(() => { this.MessageListener(); }, TaskCreationOptions.LongRunning);
+                    this._messageListenerCompleted = new ManualResetEvent(false);
+
+                    this.ExecuteThread(() =>
+                    {
+                        try
+                        {
+                            this.MessageListener();
+                        }
+                        finally
+                        {
+                            this._messageListenerCompleted.Set();
+                        }
+                    });
 
                     //  Wait for key exchange to be completed
                     this.WaitHandle(this._keyExchangeCompletedWaitHandle);
@@ -844,11 +855,12 @@ namespace Renci.SshNet
                         this._socket.Disconnect(true);
 
                         //  When socket is disconnected wait for listener to finish
-                        if (this._messageListener != null)
+                        if (this._messageListenerCompleted != null)
                         {
                             //  Wait for listener task to finish
-                            this._messageListener.Wait();
-                            this._messageListener = null;
+                            this._messageListenerCompleted.WaitOne();
+                            this._messageListenerCompleted.Dispose();
+                            this._messageListenerCompleted = null;
                         }
 
                         this._socket.Dispose();
@@ -1495,12 +1507,7 @@ namespace Renci.SshNet
         /// <param name="messageName">Name of the message.</param>
         public void RegisterMessage(string messageName)
         {
-            lock (this._messagesMetadata)
-            {
-                Parallel.ForEach(
-                    from m in this._messagesMetadata where m.Name == messageName select m,
-                    (item) => { item.Enabled = true; item.Activated = true; });
-            }
+            this.InternalRegisterMessage(messageName);
         }
 
         /// <summary>
@@ -1509,12 +1516,7 @@ namespace Renci.SshNet
         /// <param name="messageName">Name of the message.</param>
         public void UnRegisterMessage(string messageName)
         {
-            lock (this._messagesMetadata)
-            {
-                Parallel.ForEach(
-                    from m in this._messagesMetadata where m.Name == messageName select m,
-                    (item) => { item.Enabled = false; item.Activated = false; });
-            }
+            this.InternalUnRegisterMessage(messageName);
         }
 
         /// <summary>
@@ -1537,8 +1539,14 @@ namespace Renci.SshNet
             return message;
         }
 
+        partial void InternalRegisterMessage(string messageName);
+
+        partial void InternalUnRegisterMessage(string messageName);
+
         #endregion
 
+        partial void ExecuteThread(Action action);
+
         /// <summary>
         /// Listens for incoming message from the server and handles them. This method run as a task on separate thread.
         /// </summary>
@@ -1632,12 +1640,12 @@ namespace Renci.SshNet
                         this._socket = null;
                     }
 
-                    if (this._messageListener != null)
+                    if (this._messageListenerCompleted != null)
                     {
                         //  Wait for socket to be closed and for task to complete before disposing a task
-                        this._messageListener.Wait();
-                        this._messageListener.Dispose();
-                        this._messageListener = null;
+                        this._messageListenerCompleted.WaitOne();
+                        this._messageListenerCompleted.Dispose();
+                        this._messageListenerCompleted = null;
                     }
 
                     if (this._serviceAccepted != null)
@@ -1652,12 +1660,6 @@ namespace Renci.SshNet
                         this._exceptionWaitHandle = null;
                     }
 
-                    if (this._sessionSemaphore != null)
-                    {
-                        this._sessionSemaphore.Dispose();
-                        this._sessionSemaphore = null;
-                    }
-
                     if (this._keyExchangeCompletedWaitHandle != null)
                     {
                         this._keyExchangeCompletedWaitHandle.Dispose();

+ 16 - 0
Renci.SshClient/Renci.SshNet/SftpClient.NET40.cs

@@ -0,0 +1,16 @@
+using System;
+using System.Threading.Tasks;
+
+namespace Renci.SshNet
+{
+    /// <summary>
+    /// 
+    /// </summary>
+    public partial class SftpClient
+    {
+        partial void ExecuteThread(Action action)
+        {
+            Task.Factory.StartNew(action);
+        }
+    }
+}

+ 6 - 5
Renci.SshClient/Renci.SshNet/SftpClient.cs

@@ -5,7 +5,6 @@ using System.IO;
 using Renci.SshNet.Sftp;
 using System.Text;
 using Renci.SshNet.Common;
-using System.Threading.Tasks;
 using System.Globalization;
 
 namespace Renci.SshNet
@@ -13,7 +12,7 @@ namespace Renci.SshNet
     /// <summary>
     /// 
     /// </summary>
-    public class SftpClient : BaseClient
+    public partial class SftpClient : BaseClient
     {
         /// <summary>
         /// Holds SftpSession instance that used to communicate to the SFTP server
@@ -266,7 +265,7 @@ namespace Renci.SshNet
         {
             var asyncResult = new SftpListDirectoryAsyncResult(asyncCallback, state);
 
-            Task.Factory.StartNew(() =>
+            this.ExecuteThread(() =>
             {
                 try
                 {
@@ -383,7 +382,7 @@ namespace Renci.SshNet
 
             var asyncResult = new SftpDownloadAsyncResult(asyncCallback, state);
 
-            Task.Factory.StartNew(() =>
+            this.ExecuteThread(() =>
             {
                 try
                 {
@@ -446,7 +445,7 @@ namespace Renci.SshNet
 
             var asyncResult = new SftpUploadAsyncResult(asyncCallback, state);
 
-            Task.Factory.StartNew(() =>
+            this.ExecuteThread(() =>
             {
                 try
                 {
@@ -1104,6 +1103,8 @@ namespace Renci.SshNet
             this._sftpSession.RequestClose(handle);
         }
 
+        partial void ExecuteThread(Action action);
+
         /// <summary>
         /// Called when client is connected to the server.
         /// </summary>

+ 16 - 0
Renci.SshClient/Renci.SshNet/Shell.NET40.cs

@@ -0,0 +1,16 @@
+using System;
+using System.Threading.Tasks;
+
+namespace Renci.SshNet
+{
+    /// <summary>
+    /// Represents instance of the SSH shell object
+    /// </summary>
+    public partial class Shell 
+    {
+        partial void ExecuteThread(Action action)
+        {
+            Task.Factory.StartNew(action);
+        }
+    }
+}

+ 25 - 11
Renci.SshClient/Renci.SshNet/Shell.cs

@@ -4,7 +4,6 @@ using System.Diagnostics;
 using System.IO;
 using System.Text;
 using System.Threading;
-using System.Threading.Tasks;
 using Renci.SshNet.Channels;
 using Renci.SshNet.Common;
 using Renci.SshNet.Messages.Connection;
@@ -14,7 +13,7 @@ namespace Renci.SshNet
     /// <summary>
     /// Represents instance of the SSH shell object
     /// </summary>
-    public class Shell : IDisposable
+    public partial class Shell : IDisposable
     {
         private readonly Session _session;
 
@@ -36,7 +35,7 @@ namespace Renci.SshNet
 
         private string _terminalMode;
 
-        private Task _dataReaderTask;
+        private EventWaitHandle _dataReaderTaskCompleted;
 
         private Stream _outputStream;
 
@@ -135,15 +134,16 @@ namespace Renci.SshNet
             this._channelClosedWaitHandle = new AutoResetEvent(false);
 
             //  Start input stream listener
-            this._dataReaderTask = Task.Factory.StartNew(() =>
+            this._dataReaderTaskCompleted = new ManualResetEvent(false);
+            this.ExecuteThread(() =>
             {
                 try
                 {
                     var buffer = new byte[this._bufferSize];
-                    
+
                     while (this._channel.IsOpen)
                     {
-                        var asyncResult = this._input.BeginRead(buffer, 0, buffer.Length, delegate(IAsyncResult result) 
+                        var asyncResult = this._input.BeginRead(buffer, 0, buffer.Length, delegate(IAsyncResult result)
                         {
                             //  If input stream is closed and disposed already dont finish reading the stream
                             if (this._input == null)
@@ -157,7 +157,7 @@ namespace Renci.SshNet
 
                         }, null);
 
-                        EventWaitHandle.WaitAny(new WaitHandle[] {asyncResult.AsyncWaitHandle, this._channelClosedWaitHandle});
+                        EventWaitHandle.WaitAny(new WaitHandle[] { asyncResult.AsyncWaitHandle, this._channelClosedWaitHandle });
 
                         if (asyncResult.IsCompleted)
                             continue;
@@ -169,6 +169,10 @@ namespace Renci.SshNet
                 {
                     this.RaiseError(new ExceptionEventArgs(exp));
                 }
+                finally
+                {
+                    this._dataReaderTaskCompleted.Set();
+                }
             });
 
             this.IsStarted = true;
@@ -235,18 +239,21 @@ namespace Renci.SshNet
             if (this.Stopping != null)
             {
                 //  Handle event on different thread
-                Task.Factory.StartNew(() => { this.Stopping(this, new EventArgs()); });                
+                this.ExecuteThread(() => { this.Stopping(this, new EventArgs()); });
             }
 
             if (this._channel.IsOpen)
                 this._channel.Close();
 
             this._channelClosedWaitHandle.Set();
-            
+
             this._input.Dispose();
             this._input = null;
 
-            this._dataReaderTask.Wait();            
+            //  TODO:   Add timeout to WaitOne method
+            this._dataReaderTaskCompleted.WaitOne();
+            this._dataReaderTaskCompleted.Dispose();
+            this._dataReaderTaskCompleted = null;
 
             this._channel.DataReceived -= Channel_DataReceived;
             this._channel.ExtendedDataReceived -= Channel_ExtendedDataReceived;
@@ -257,12 +264,13 @@ namespace Renci.SshNet
             if (this.Stopped != null)
             {
                 //  Handle event on different thread
-                Task.Factory.StartNew(() => { this.Stopped(this, new EventArgs()); });                
+                this.ExecuteThread(() => { this.Stopped(this, new EventArgs()); });
             }
 
             this._channel = null;
         }
 
+        partial void ExecuteThread(Action action);
 
         #region IDisposable Members
 
@@ -296,6 +304,12 @@ namespace Renci.SshNet
                         this._channelClosedWaitHandle.Dispose();
                         this._channelClosedWaitHandle = null;
                     }
+
+                    if (this._dataReaderTaskCompleted != null)
+                    {
+                        this._dataReaderTaskCompleted.Dispose();
+                        this._dataReaderTaskCompleted = null;
+                    }
                 }
 
                 // Note disposing has been done.

+ 16 - 0
Renci.SshClient/Renci.SshNet/SshCommand.NET40.cs

@@ -0,0 +1,16 @@
+using System;
+using System.Threading.Tasks;
+
+namespace Renci.SshNet
+{
+    /// <summary>
+    /// Represents SSH command that can be executed.
+    /// </summary>
+    public partial class SshCommand 
+    {
+        partial void ExecuteThread(Action action)
+        {
+            Task.Factory.StartNew(action);
+        }
+    }
+}

+ 5 - 4
Renci.SshClient/Renci.SshNet/SshCommand.cs

@@ -3,7 +3,6 @@ using System.IO;
 using System.Linq;
 using System.Text;
 using System.Threading;
-using System.Threading.Tasks;
 using Renci.SshNet.Channels;
 using Renci.SshNet.Common;
 using Renci.SshNet.Messages;
@@ -16,7 +15,7 @@ namespace Renci.SshNet
     /// <summary>
     /// Represents SSH command that can be executed.
     /// </summary>
-    public class SshCommand : IDisposable
+    public partial class SshCommand : IDisposable
     {
         private Encoding _encoding;
 
@@ -338,8 +337,8 @@ namespace Renci.SshNet
 
             if (this._callback != null)
             {
-                //  Execute callback on different thread
-                Task.Factory.StartNew(() => { this._callback(this._asyncResult); });
+                //  Execute callback on different thread                
+                this.ExecuteThread(() => { this._callback(this._asyncResult); });
             }
             ((EventWaitHandle)this._asyncResult.AsyncWaitHandle).Set();
         }
@@ -415,6 +414,8 @@ namespace Renci.SshNet
                 throw new SshOperationTimeoutException(string.Format(CultureInfo.CurrentCulture, "Command '{0}' has timed out.", this.CommandText));
             }
         }
+        
+        partial void ExecuteThread(Action action);
 
         #region IDisposable Members