Skip to content

Commit f7f50a4

Browse files
committed
Fix CancelAsync Cause Deadlock
1 parent 0876e88 commit f7f50a4

File tree

2 files changed

+39
-10
lines changed

2 files changed

+39
-10
lines changed

src/Renci.SshNet/SshCommand.cs

+13-8
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ public class SshCommand : IDisposable
2626
private CommandAsyncResult _asyncResult;
2727
private AsyncCallback _callback;
2828
private EventWaitHandle _sessionErrorOccuredWaitHandle;
29+
private EventWaitHandle _commandCancelledWaitHandle;
2930
private Exception _exception;
3031
private StringBuilder _result;
3132
private StringBuilder _error;
@@ -186,7 +187,7 @@ internal SshCommand(ISession session, string commandText, Encoding encoding)
186187
_encoding = encoding;
187188
CommandTimeout = Session.InfiniteTimeSpan;
188189
_sessionErrorOccuredWaitHandle = new AutoResetEvent(initialState: false);
189-
190+
_commandCancelledWaitHandle = new AutoResetEvent(initialState: false);
190191
_session.Disconnected += Session_Disconnected;
191192
_session.ErrorOccured += Session_ErrorOccured;
192193
}
@@ -356,13 +357,12 @@ public string EndExecute(IAsyncResult asyncResult)
356357
/// <summary>
357358
/// Cancels command execution in asynchronous scenarios.
358359
/// </summary>
359-
public void CancelAsync()
360+
/// <param name="forceKill">if true send SIGKILL instead of SIGTERM.</param>
361+
public void CancelAsync(bool forceKill = false)
360362
{
361-
if (_channel is not null && _channel.IsOpen && _asyncResult is not null)
362-
{
363-
// TODO: check with Oleg if we shouldn't dispose the channel and uninitialize it ?
364-
_channel.Dispose();
365-
}
363+
var signal = forceKill ? "KILL" : "TERM";
364+
_ = _channel?.SendExitSignalRequest(signal, coreDumped: false, "Command execution has been cancelled.", "en");
365+
_ = _commandCancelledWaitHandle.Set();
366366
}
367367

368368
/// <summary>
@@ -506,6 +506,7 @@ private void WaitOnHandle(WaitHandle waitHandle)
506506
var waitHandles = new[]
507507
{
508508
_sessionErrorOccuredWaitHandle,
509+
_commandCancelledWaitHandle,
509510
waitHandle
510511
};
511512

@@ -515,7 +516,8 @@ private void WaitOnHandle(WaitHandle waitHandle)
515516
case 0:
516517
ExceptionDispatchInfo.Capture(_exception).Throw();
517518
break;
518-
case 1:
519+
case 1: // Command cancelled
520+
case 2:
519521
// Specified waithandle was signaled
520522
break;
521523
case WaitHandle.WaitTimeout:
@@ -620,6 +622,9 @@ protected virtual void Dispose(bool disposing)
620622
_sessionErrorOccuredWaitHandle = null;
621623
}
622624

625+
_commandCancelledWaitHandle?.Dispose();
626+
_commandCancelledWaitHandle = null;
627+
623628
_isDisposed = true;
624629
}
625630
}

test/Renci.SshNet.IntegrationTests/OldIntegrationTests/SshCommandTest.cs

+26-2
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,30 @@ public void Test_Execute_SingleCommand()
5151
}
5252
}
5353

54+
[TestMethod]
55+
[Timeout(5000)]
56+
public void Test_CancelAsync_Running_Command()
57+
{
58+
using var client = new SshClient(SshServerHostName, SshServerPort, User.UserName, User.Password);
59+
#region Example SshCommand CancelAsync
60+
client.Connect();
61+
var testValue = Guid.NewGuid().ToString();
62+
var command = $"sleep 10s; echo {testValue}";
63+
using var cmd = client.CreateCommand(command);
64+
try
65+
{
66+
var asyncResult = cmd.BeginExecute();
67+
cmd.CancelAsync();
68+
cmd.EndExecute(asyncResult);
69+
}
70+
catch (OperationCanceledException)
71+
{
72+
}
73+
client.Disconnect();
74+
Assert.AreNotEqual(cmd.Result.Trim(), testValue);
75+
#endregion
76+
}
77+
5478
[TestMethod]
5579
public void Test_Execute_OutputStream()
5680
{
@@ -222,7 +246,7 @@ public void Test_Execute_Command_ExitStatus()
222246
client.Connect();
223247

224248
var cmd = client.RunCommand("exit 128");
225-
249+
226250
Console.WriteLine(cmd.ExitStatus);
227251

228252
client.Disconnect();
@@ -443,7 +467,7 @@ public void Test_Execute_Invalid_Command()
443467
}
444468

445469
[TestMethod]
446-
470+
447471
public void Test_MultipleThread_100_MultipleConnections()
448472
{
449473
try

0 commit comments

Comments
 (0)