All solutions I found so far are based on WaitOne: How to configure socket connect timeout or spawning a worker thread
For me, blocking the thread with WaitOne defeats the purpose of async methods. Spawning another thread is not much better (as async model strives to use as less threads as possible).
Is there any other solution which would still let me abort the connection attempt without blocking the current thread or spawning another one?
I'm developing a library used by external code which has no knowledge of what's going on inside my library (the code just calls my ConnectAsync method and I do the rest including TcpClient.ConnectAsync and so on). The external code can be anything (web app, desktop app, Windows service, whatever). Ideally, the solution should not require the external code to do anything to abort the operation beyond setting .Timeout property of my class. However, if it's the only the way to avoid blocks or worker threads when implementing custom connection timeout, I'd be grateful to see which options I have (in terms of async/await model).
TcpClient.SendAsync doesn't receive a CancellationToken so it can't really be canceled (How do I cancel non-cancelable async operations?). You can use the WithTimeout extensions method:
public static Task<TResult> WithTimeout<TResult>(this Task<TResult> task, TimeSpan timeout)
{
var timeoutTask = Task.Delay(timeout).ContinueWith(_ => default(TResult), TaskContinuationOptions.ExecuteSynchronously);
return Task.WhenAny(task, timeoutTask).Unwrap();
}
This doesn't cancel the original operation though, only allows your code to behave as if it did. The abandoned operation will linger forever unless handled explicitly.
To actually cancel the underlying operation you should make sure to call Dispose on the TcpClient (preferably via a using scope). That would make the abandoned task throw an ObjectDisposedException (or others) so be aware of that.
You can take a look at my answer here about using a TimeoutScope:
try
{
var client = new TcpClient();
using (client.CreateTimeoutScope(TimeSpan.FromSeconds(2)))
{
var result = await client.ConnectAsync();
// Handle result
}
}
catch (ObjectDisposedException)
{
return null;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With