I have the following method
public async Task<bool> Connect()
{
lock (_connectingLock)
{
if (_connecting)
throw new IOException("Already connecting");
_connecting = true;
}
try {
await tcpClient.ConnectAsync(...);
}
catch (SocketException e)
{
return false;
}
finally
{
lock (_connectingLock)
{
_connecting = false;
}
}
}
Now, I would expect consecutive calls to Connect() to throw an IOException, but it doesn't happen!
What could be the cause?
Calls to Connect() can't throw exceptions directly. Async methods do not throw exceptions. Instead, they will return tasks which, when awaited, will throw IOException. (i.e. the tasks are faulted.)
If that's not what you want, you should separate the calls:
public Task<bool> Connect()
{
// Eager validation of state...
lock (_connectingLock)
{
if (_connecting)
throw new IOException("Already connecting");
_connecting = true;
}
return ConnectImpl();
}
private async Task<bool> ConnectImpl()
{
try {
await tcpClient.ConnectAsync(...);
}
catch (SocketException e)
{
return false;
}
finally
{
lock (_connectingLock)
{
_connecting = false;
}
}
}
It's not clear whether that's appropriate in this case though. It's generally fine to throw things like ArgumentException eagerly, but if the error doesn't represent a bug in the calling code itself, I think returning a faulted task instead is fine.
Related
I don't think this question is a duplicate of "Proper way to deal with exceptions in DisposeAsync".
Let's say my class that implements IAsynsDisposable because it has a long-running background task, and DisposeAsync terminates that task. A familiar pattern might be the Completion property, e.g. ChannelReader<T>.Completion (despite ChannelReader doesn't implement IAsynsDisposable).
Is it considered a good practice to propagate the Completion task's exceptions outside DisposeAsync?
Here is a complete example that can be copied/pasted into a dotnet new console project. Note await this.Completion inside DisposeAsync:
try
{
await using var service = new BackgroundService(TimeSpan.FromSeconds(2));
await Task.Delay(TimeSpan.FromSeconds(3));
}
catch (Exception ex)
{
Console.WriteLine(ex);
Console.ReadLine();
}
class BackgroundService: IAsyncDisposable
{
public Task Completion { get; }
private CancellationTokenSource _diposalCts = new();
public BackgroundService(TimeSpan timeSpan)
{
this.Completion = Run(timeSpan);
}
public async ValueTask DisposeAsync()
{
_diposalCts.Cancel();
try
{
await this.Completion;
}
finally
{
_diposalCts.Dispose();
}
}
private async Task Run(TimeSpan timeSpan)
{
try
{
await Task.Delay(timeSpan, _diposalCts.Token);
throw new InvalidOperationException("Boo!");
}
catch (OperationCanceledException)
{
}
}
}
Alternatively, I can observe service.Completion explicitly in the client code (and ignore its exceptions inside DiposeAsync to avoid them being potentially thrown twice), like below:
try
{
await using var service = new BackgroundService(TimeSpan.FromSeconds(2));
await Task.Delay(TimeSpan.FromSeconds(3));
await service.Completion;
}
catch (Exception ex)
{
Console.WriteLine(ex);
Console.ReadLine();
}
class BackgroundService: IAsyncDisposable
{
public Task Completion { get; }
private CancellationTokenSource _diposalCts = new();
public BackgroundService(TimeSpan timeSpan)
{
this.Completion = Run(timeSpan);
}
public async ValueTask DisposeAsync()
{
_diposalCts.Cancel();
try
{
await this.Completion;
}
catch
{
// the client should observe this.Completion
}
finally
{
_diposalCts.Dispose();
}
}
private async Task Run(TimeSpan timeSpan)
{
try
{
await Task.Delay(timeSpan, _diposalCts.Token);
throw new InvalidOperationException("Boo!");
}
catch (OperationCanceledException)
{
}
}
}
Is there a concensus about which option is better?
For now, I've settled on a reusable helper class LongRunningAsyncDisposable (here's a gist, warning: barely tested yet), which allows:
to start a background task;
stop this task (via a cancellation token) by calling IAsyncDisposable.DisposeAsync at any time, in a thread-safe, concurrency-friendly way;
configure whether DisposeAsync should re-throw the task's exceptions (DisposeAsync will await the task's completion either way, before doing a cleanup);
observe the task's status, result and exceptions at any time via LongRunningAsyncDisposable.Completion property.
I have a class called SearchProbe for I'm writing unit tests. One unit test is for testing the ability of my class's main processing method (called RunSearchProbe) to be able to respond to CancellationTokens correctly. My class's main processing method executes async submethods which all throw an OperationCanceledException when a CancellationToken is cancelled. Then in my main method RunSearchProbe, I'm trying to catch this exception and respond to it.
Problem: The problem is that for some reason, OperationCanceledException is NOT being caught in the main method RunSearchProbe, and it comes all the way upto my unit test's call stack for handling, and I don't know why ?!
Here's my main class:
public class SearchProbe
{
protected async Task RunSearchProbe(CancellationToken cancellationToken) {
try
{
try
{
using (cancellationToken.Register(() => {
//some code here
}))
{
Task<bool> initTask = Initialize(cancellationToken);
await initTask;
//some code here
}
}
catch (Exception exception) when (exception.GetType().Equals(typeof(OperationCanceledException))
|| exception.InnerException.GetType().Equals(typeof(OperationCanceledException)))
{
//some code here // -------->>> (Point 1) This is where the OperationCanceledException SHOULD get caught
}
finally
{
//some code here
}
}
catch (Exception e)
{
//some code here // -------->>> (Point 2) ... Or AT LEAST get caught here
}
}
private async Task<bool> Initialize(CancellationToken cancellationToken) {
try
{
using (cancellationToken.Register(() => {
throw new OperationCanceledException();
}))
{
//some code here
return true;
}
}
catch (Exception exception)
{
//some code here
}
}
}
This is a mock inherited class:
class MockSearchProbe : SearchProbe
{
static MockSearchProbe()
{
//some code here
}
public async Task RunProbeManually()
{
try {
CancellationTokenSource cts = new CancellationTokenSource();
Task probeTask = RunSearchProbe(cts.Token);
cts.Cancel();
await probeTask;
}
catch (Exception exception) when (exception.GetType().Equals(typeof(OperationCanceledException))
|| exception.InnerException.GetType().Equals(typeof(OperationCanceledException)))
{
//do something (Point 3) ... But it actually gets caught here for some reason
}
}
}
This is the test class:
[TestClass]
public class SearchProbeTests
{
[TestMethod]
public async Task TestProbe_Cancellation()
{
MockSearchProbe probe = new MockSearchProbe();
Task result = probe.RunProbeManually();
await result;
}
}
Please see steps 1, 2 and 3 commented above to see what I mean ... Why is the catch block inside my main class's RunSearchProbe method NOT catching the OperationCanceledException ??
The documentation for CancellationToken.Regsiter states that the method:
Registers a delegate that will be called when this CancellationToken is canceled.
Based on that description, I would expect that the registration callback defined in the Initialize method should execute when cts.Cancel() is called in RunProbeManually. The exception is not instantiated or thrown until that point, which is in the scope of the try/catch block labeled "Point 3."
Here's a simplified illustration:
using System;
class MainClass {
public static void Main (string[] args) {
Action throwException = null;
try {
Console.WriteLine("Defining delegate");
throwException = () => {
Console.WriteLine("Throwing exception");
throw new Exception();
};
} catch (Exception) {
Console.WriteLine("Exception caught at point 1");
}
try {
Console.WriteLine("Invoking delegate");
throwException.Invoke();
} catch (Exception) {
Console.WriteLine ("Exception caught at point 2");
}
}
}
Output:
Defining delegate
Invoking delegate
Throwing exception
Exception caught at point 2
I'm new to TPL.
I need to handle exception when the SendEmailAlert() method throws any error.Is the following code correct please?
public Task MyMethod()
{
DoSomething();
try
{
string emailBody = "TestBody";
string emailSubject = "TestSubject";
Task.Run(()=> SendEmailAlert(arrEmailInfo));
}
catch (AggregateException ex)
{
ex.Handle((e) =>
{
log.Error("Error occured while sending email...", e);
return true;
}
);
}
}
private void SendEmailAlert(string[] arrEmailInfo)
{
MyClassX.SendAlert(arrEmailnfo[0], arrEmailnfo[1]);
}
I forced an error from within SendEmailAlert() method.But the exception is not getting caught. Could someone advise?
Thanks.
Your Task.Run runs in a different context (you would need a try/catch inside it; or check if the task is done). You could change to use async/await.
Example:
public async void MyMethod()
{
try
{
await ExceptionMethod();
}
catch (Exception ex)
{
// got it
}
}
public async Task ExceptionMethod()
{
throw new Exception();
}
New to async await integration in C# 5. I'm working with some basic Task based methods to explore async await and the TPL. In this example below I'm calling a web service with a timeout of 5 seconds. If the timeout expires it should throw an exception so I can return false from the method. However, the timeout never occurs, or maybe it does but the Task never returns.
public static Task<bool> IsConnectedAsync()
{
return Task.Run(() =>
{
try
{
using (WSAppService.AppService svc = new NCSoftware.Common.WSAppService.AppService(GetServiceUrl(WebService.app)){Timeout = 5000})
{
return svc.PingB();
}
}
catch (Exception ex)
{
Logger.LogException(ex.Message, ex, "IsConnectedAsync");
}
return false;
});
}
If you could please help with how to properly handle this so that if the timeout occurs or even better, an exception occurs, the Task does return.
In general, you shouldn't use Task.Run if you're wrapping async services. Since this is a service reference, you should be able to expose an async method (returning Task) directly from the service, in which case you could use:
public async static Task<bool> IsConnectedAsync()
{
try
{
using (WSAppService.AppService svc = new NCSoftware.Common.WSAppService.AppService(GetServiceUrl(WebService.app)){Timeout = 5000})
{
return await svc.PingBAsync();
}
}
catch (Exception ex)
{
Logger.LogException(ex.Message, ex, "IsConnectedAsync");
}
return false;
}
If you must wrap via Task.Run (again, this is not suggested, as it's turning synchronous code into async via the thread pool, which is typically better handled by the user at the top level), you could do:
public async static Task<bool> IsConnectedAsync()
{
try
{
return await Task.Run(() =>
{
using (WSAppService.AppService svc = new NCSoftware.Common.WSAppService.AppService(GetServiceUrl(WebService.app)){Timeout = 5000})
{
return svc.PingB();
}
}
}
catch (Exception ex)
{
Logger.LogException(ex.Message, ex, "IsConnectedAsync");
return false;
}
}
This example "fails":
static async void Main(string[] args)
{
try
{
await TaskEx.Run(() => { throw new Exception("failure"); });
}
catch (Exception)
{
throw new Exception("success");
}
}
That is, the exception with the text "failure" bubbles up.
Then I tried this workaround:
static async void Main(string[] args)
{
try
{
await SafeRun(() => { throw new Exception("failure"); });
}
catch (Exception)
{
throw new Exception("success");
}
}
static async Task SafeRun(Action action)
{
var ex = default(Exception);
await TaskEx.Run(() =>
{
try
{
action();
}
catch (Exception _)
{
ex = _;
}
});
if (ex != default(Exception))
throw ex;
}
That didn't help either.
I suppose my Async CTP refresh installation could be hosed.
Should this code work as I expect ("success" bubbles up, not "failure"), or is this not "supposed" to work that way. And if not, how would you work around it?
The behavior you are seeing is likely an edge case bug or may even be correct, if unintuitive. Normally when you invoke an async method synchronously, it wraps a task around to execute and since there is no one waiting on the task to finish, the exception never makes it to the main thread. If you were to call Main directly it would succeed, but then your runtime would see an exception of "success" on another thread.
Since main is the entrypoint of your application, it is invoked synchronously and likely as the entrypoint doesn't trigger the Task wrapping behavior, so that await isn't run properly and the TaskEx.Run throws on its own thread, which shows up in the runtime as an exception being thrown on another thread.
If you were to run main as an async method, i.e. returning a Task (since an async that returns void can only really be called via await) and blocking on it from your synchronous main context, you would get the appropriate behavior as the below test illustrates:
static async Task Main() {
try {
await TaskEx.Run(() => { throw new Exception("failure"); });
} catch(Exception) {
throw new Exception("success");
}
}
static async Task Main2() {
await Main();
}
[Test]
public void CallViaAwait() {
var t = Main2();
try {
t.Wait();
Assert.Fail("didn't throw");
} catch(AggregateException e) {
Assert.AreEqual("success",e.InnerException.Message);
}
}
[Test]
public void CallDirectly() {
var t = Main();
try {
t.Wait();
Assert.Fail("didn't throw");
} catch(AggregateException e) {
Assert.AreEqual("success", e.InnerException.Message);
}
}
I.e. the Task faults with an AggregateException which contains the success exception as it's inner exception.