Regarding your specific SSH use case, it still may be possible to cancel a pending SSH request naturally, depending on what method your're using to issue SSH command. For example, SSH.NET library provides SshCommand.BeginExecute
/EndExecute
/CancelAsync
methods, known as Asynchronous Programming Model (APM) pattern. An APM like this can easily be wrapped as an await-able and cancel-able Task
, using TaskCompletionSource
and CancellationTokenSource
.
For example, executing an asynchronous SSH command with SSH.NET may look like this:
Imports System.Threading
Imports System.Threading.Tasks
Imports Renci.SshNet
Module Module1
Async Function ExecSshCommandAsync(command As Renci.SshNet.SshCommand, ct As CancellationToken) As Task(Of String)
Dim tcs As TaskCompletionSource(Of String) = New TaskCompletionSource(Of String)
Dim doCancel As Action = Sub()
command.CancelAsync()
tcs.TrySetCanceled()
End Sub
Using ct.Register(doCancel)
Dim asyncCallback As System.AsyncCallback = Sub(iar As IAsyncResult) tcs.TrySetResult(command.EndExecute(iar))
command.BeginExecute(asyncCallback)
Await tcs.Task
End Using
Return tcs.Task.Result
End Function
Sub Main()
Dim command As Renci.SshNet.SshCommand
' Initialze the SSH session etc
' signal cancellation in 10 sec
Dim cts As CancellationTokenSource = New CancellationTokenSource(10000)
' Blocking wait for result with 10 sec timeout
Dim Result = ExecSshCommandAsync(command, cts.Token).Result
Console.WriteLine(Result)
' If Main was async too, we could await ExecSshCommandAsync here:
' Dim result = Await ExecSshCommandAsync(command, cts.Token)
End Sub
End Module
If you're executing SSH commands with a separate console process (like Putty), you still may use almost the same technique. It would asynchronously read and parse the child process's console output, and register a cancellation routine which would kill the process with Process.Kill
(or do something more nice like GenerateConsoleCtrlEvent
to terminate it, more info).
Furthermore, if you're only interested in the child SSH process's exit code, there's yet another approach. You can turn Process.Handle
into an await-able task, which result you can await in a similar way. The cancellation callback (registered via CancellationToken.Register
) would kill the process, and make the task cancelled. Or the process may complete naturally. In both cases the task will reach the completed state and the asynchronous wait will be over.
Just keep in mind, calling TaskCompletionSource.TrySetCanceled
will lead to an exception being thrown at the point were you await
for the TaskCompletionSource.Task
.