I am trying to cancel a Socket.ReceiveAsync() call by calling Socket.Close() and handling the inevitable SocketException.
My problem is that the exception never seems to make it to the try/catch in the calling function. Like this:
public async Task DoSomethingAsync()
{
Try
{
while(true)
{
await DoSomethingMoreSpecificAsync();
....
}
}
catch(Exception ex)
{
do some logging... <-- exception never makes it here
}
finally
{
cleanup stuff(); <-- or here...
}
}
public async Task DoSomethingMoreSpecificAsync()
{
var read = await _sock.ReceiveAsync(...); <-- Exception is thrown here
}
So when the exception is thrown, the application never makes it to the catch or finally blocks. It just complains of an unhandled exception. AFAIK async Task methods should propagate their exceptions upwards like any other method. I know that this would be normal if I was using async void...
In the console, after the app has crashed, I see this:
terminate called after throwing an instance of 'PAL_SEHException'
Here is the actual code:
private async Task DoReceiveAsync()
{
var writer = _inputPipe.Writer;
Exception ex = null;
try
{
while (true)
{
var mem = writer.GetMemory(TLS_MAX_RECORD_SIZE);
MemoryMarshal.TryGetArray(mem, out ArraySegment<byte> buf);
//Since OpenSSL will only gve us a complete record, we need a func that will piece one back together from the stream
await ReadRecord(mem);
var res = _ssl.Read(buf.Array, TLS_MAX_RECORD_SIZE);
writer.Advance(res);
var flush = await writer.FlushAsync();
if (flush.IsCanceled || flush.IsCompleted)
break;
}
}
catch (Exception e) { ex = e; }
finally { writer.Complete(ex); }
}
private async Task ReadRecord(Memory<byte> mem)
{
MemoryMarshal.TryGetArray(mem, out ArraySegment<byte> buf);
SslError res;
do
{
var sockRead = await _socket.ReceiveAsync(buf, SocketFlags.None);
if (sockRead == 0) break;
_ssl.ReadBio.Write(buf.Array, sockRead);
var sslRead = _ssl.Peek(peekBuf, TLS_MAX_RECORD_SIZE);
res = _ssl.GetError(sslRead);
} while (res != SslError.SSL_ERROR_NONE);
return;
}
Related
I have two instances of a class which creates a UDP socket to receive data from UDP clients. If one of the instances throws an exception I want to handle it immediately in a higher layer. In my program they're started with await Task.WhenAll(recv1.StartAsync(), recv2.StartAsync). This however waits for all tasks to finish before the first exception is thrown. Any ideas on how to resolve this problem?
static async Task Main(string[] args)
{
var udpReceiver1 = new UdpReceiver(localEndpoint1);
var udpReceiver2 = new UdpReceiver(localEndpoint2);
var cts = new CancellationTokenSource();
try
{
await Task.WhenAll(udpReceiver1.StartAsync(cts.Token), udpReceiver2.StartAsync(cts.Token));
}
catch (Exception e)
{
// Handle Exception...
cts.Cancel();
}
}
class UdpReceiver
{
public UdpReceiver(IPEndPoint endpoint)
{
udpClient = new UdpClient(endpoint);
}
public async Task StartAsync(CancellationToken cancellationToken)
{
try
{
while (!cancellationToken.IsCancellationRequested)
{
var result = await ReceiveAsync(cancellationToken);
var message = Encoding.UTF8.GetString(result.Buffer);
Trace.WriteLine($"UdpClient1 received message:{Encoding.UTF8.GetString(result.Buffer)}");
// throw new Exception("UdpClient1 raising exception");
}
}
}
private async Task<UdpReceiveResult> ReceiveAsync(CancellationToken cancellationToken)
{
var tcs = new TaskCompletionSource<UdpReceiveResult>();
using (cancellationToken.Register(() => tcs.TrySetCanceled(), false))
{
var task = udpClient.ReceiveAsync();
var completedTask = await Task.WhenAny(task, tcs.Task);
var result = await completedTask.ConfigureAwait(false);
return result;
}
}
private UdpClient udpClient;
}
Update 1: Task.WhenAny would be a viable solution. Thanks #CamiloTerevinto
try
{
await await Task.WhenAny(udpReceiver1.StartAsync(cts.Token), udpReceiver2.StartAsync(cts.Token));
}
catch (Exception e)
{
// Handle Exception...
cts.Cancel();
}
Update 2: For a more fine grained exception handling of all tasks I'd go with my own adapted implementation of Task.WhenAll proposed by #Servy.
The behavior is different enough from the framework WhenAll implementation that you're probably best just writing your own adapted version, fortunately it's not particularly hard to implement. Just attach a continuation to every single task, if any is cancelled or faulted, the resulting task does the same, if it succeeds store the result, and if the last task was the one to succeed, complete the task with all of the stored results.
public static Task<IEnumerable<TResult>> WhenAll<TResult>(IEnumerable<Task<TResult>> tasks)
{
var listOfTasks = tasks.ToList();
if (listOfTasks.Count == 0)
{
return Task.FromResult(Enumerable.Empty<TResult>());
}
var tcs = new TaskCompletionSource<IEnumerable<TResult>>();
var results = new TResult[listOfTasks.Count];
int completedTasks = 0;
for (int i = 0; i < listOfTasks.Count; i++)
{
int taskIndex = i;
Task<TResult> task = listOfTasks[i];
task.ContinueWith(_ =>
{
if (task.IsCanceled)
tcs.TrySetCanceled();
else if (task.IsFaulted)
tcs.TrySetException(task.Exception.InnerExceptions);
else
{
results[taskIndex] = task.Result;
if (Interlocked.Increment(ref completedTasks) == listOfTasks.Count)
{
tcs.TrySetResult(results);
}
}
});
}
return tcs.Task;
}
As with so many task based generic operations, you also need a version without the results, and if you don't want to deal with a notable overhead, you really need to just copy-paste the result-based approach but with all of the results ripped out, which isn't hard, just inelegant. Turning all of these tasks into tasks with a result would also work, but for an operation like this the overhead is likely problematic.
public static Task WhenAll(IEnumerable<Task> tasks)
{
var listOfTasks = tasks.ToList();
if (listOfTasks.Count == 0)
{
return Task.CompletedTask;
}
var tcs = new TaskCompletionSource<bool>();
int completedTasks = 0;
for (int i = 0; i < listOfTasks.Count; i++)
{
int taskIndex = i;
Task task = listOfTasks[i];
task.ContinueWith(_ =>
{
if (task.IsCanceled)
tcs.TrySetCanceled();
else if (task.IsFaulted)
tcs.TrySetException(task.Exception.InnerExceptions);
else
{
if (Interlocked.Increment(ref completedTasks) == listOfTasks.Count)
{
tcs.TrySetResult(true);
}
}
});
}
return tcs.Task;
}
There's probably a way to do it but I can't think of one without making your code very messy. It'd be better to handle the exception in the actual task. If you need to handle it with common code, use a handler delegate.
static async Task Main(string[] args)
{
var cts = new CancellationTokenSource();
//This is our common error handler
void HandleException(Exception ex)
{
Log("Exception!" + ex.Message);
cts.Cancel();
}
var udpReceiver1 = new UdpReceiver(localEndpoint1);
var udpReceiver2 = new UdpReceiver(localEndpoint1);
//We pass the handler as one of the arguments
await Task.WhenAll(udpReceiver1.StartAsync(cts.Token, HandleException), udpReceiver2.StartAsync(cts.Token, HandleException));
}
class UdpReceiver
{
public async Task StartAsync(CancellationToken cancellationToken, Action<Exception> errorHandler)
{
try
{
while (!cancellationToken.IsCancellationRequested)
{
//Main logic goes here
}
}
catch(Exception ex)
{
errorHandler(ex); //Call common error handling code
}
}
You could await the tasks in two steps. In the first step await any of them to complete, and in case of failure initiate the cancellation. Don't handle the exception in this step. Delay the exception handling until the second step, after awaiting all of the tasks to complete. Both tasks may have failed, so you may want to handle the exception of each one separately.
Task task1 = udpReceiver1.StartAsync(cts.Token);
Task task2 = udpReceiver2.StartAsync(cts.Token);
// Await any task to complete
Task firstCompletedTask = await Task.WhenAny(task1, task2);
if (firstCompletedTask.IsFaulted) cts.Cancel();
try
{
// Await them all to complete
await Task.WhenAll(task1, task2);
}
catch
{
if (task1.IsFaulted) HandleException(task1.Exception.InnerException);
if (task2.IsFaulted) HandleException(task2.Exception.InnerException);
}
The solution works, but is there a better way to handle multiple requests like this with the errorhandling.
The below code describes what i want to do, and absolutely works. But I'm sure there is a better way to go about the issue?
I've tried other options as well but it fails as some of the requests will return a 404.
public async Task<List<Bruker>> TryGetContactsByContactIds(List<AZContact> contacts)
{
var tasks = contacts.Select(c => TryGetContactAsync(c.Email)).Where(c => c.Result != null);
try
{
var tasksresult = await Task.WhenAll(tasks);
return tasksresult.ToList();
}
catch (Exception e)
{
_logger.Error("unable to fetch all", e);
}
return new List<Bruker>();
}
public async Task<Bruker> TryGetContactAsync(string userId)
{
try
{
var user = await _brukereClient.GetAsync(userId);
return user;
}
catch (SwaggerException e)
{
if (e.StatusCode == 404)
{
_logger.Info($"user with Id {userId} does not exist");
}
else
{
_logger.Error("Unable to fetch user", e);
}
}
return null;
}
You are probably dealing with a feature/limitation of await, that throws only one of the aggregated exceptions of the awaited task (the WhenAll task in this case). You must enumerate all the tasks to handle each individual exception.
try
{
var tasksresult = await Task.WhenAll(tasks);
return tasksresult.ToList();
}
catch (Exception e)
{
foreach (var task in tasks)
{
if (task.IsFaulted)
{
var taskException = task.Exception.InnerException;
// ^^ Assuming that each task cannot have more than one exception inside its AggregateException.
if (taskException is SwaggerException swaggerException)
{
if (swaggerException.StatusCode == 404)
{
_logger.Info($"user with Id {userId} does not exist");
}
else
{
_logger.Error("Unable to fetch user", swaggerException);
}
}
else
{
_logger.Error("An unexpected task error occurred", taskException);
}
}
}
if (!tasks.Any(t => t.IsFaulted))
{
_logger.Error("A non task-related error occurred", e);
}
}
In my case, I created tasks by:
IList<Task> Tasks = new List<Task>();
Tasks.Add(Task.Run(async () => { await functionAsync();}));
I need these tasks to run infinitely in order to continuously processing some incoming data. However, in cases when some fatal error/exceptions happen, I need to end the program by canceling all the tasks. I put together an simple example to simulate what I intended to do , but it does not work. I thought a task will considered to be end when throw an exception, and the WaitAny should return with AggregatedException, but it doesn't seem to be how it actually works. So, how can I make it correct?
public static void Main(string[] args)
{
Console.WriteLine(nameof(Main));
for (int i = 1; i < 5; i++)
{
var i1 = i;
_tasks.Add(Task.Run(async () => { await Tryme(i1); }));
}
try
{
Task.WaitAny(_tasks.ToArray());
}
catch (Exception e)
{
Console.WriteLine("Stop my program if any of the task in _tasks throw exception");
Console.WriteLine(e);
}
Console.ReadLine();
}
private static async Task Tryme(int i)
{
Console.WriteLine($"I'm {i}");
if (i == 3)
{
Console.WriteLine($"{i} is throwing the exception");
throw new Exception("fake one");
}
await Task.Delay(TimeSpan.MaxValue);
}
Instead of manually canceling the entire task-chain, you can use TPL Dataflow which falts the entire block for you if an unhandeled exception occurs, or can be configured to behave in other modes if needed:
var actionBlock = new ActionBlock<int>(i => TryMe(i));
foreach (var num in Enumerable.Range(0, 100))
{
actionBlock.Post(num);
}
try
{
await actionBlock.Completion();
}
catch (Exception e)
{
// Block finished prematurely, handle exception.
}
Note Dataflow takes care of parralisation for you, no need to manually create tasks.
Got some clue from this post . It looks that WaitAny will not throw any exception. I got the exception by:
int faultIndex = Task.WaitAny(_tasks.ToArray());
if (_tasks[faultIndex].IsFaulted)
{
Console.WriteLine($"{faultIndex} end");
throw _tasks[faultIndex].Exception;
}
I have an application that runs a background thread which communicates to multiple devices. The devices send me data based off an external trigger that I do not control. I have to wait for the devices to send me data and operate on them. If an exception occurs, I need to display that on the UI.
I am attempting to continuously read data over the network stream. When data comes, I need to raise it as an event and then start reading again. I need to be able to handle if an exception is thrown such as the device being disconnected.
At the base I have a network stream reading asynchronously
public Task<string> ReadLinesAsync(CancellationToken token)
{
_readBuffer = new byte[c_readBufferSize];
// start asynchronously reading data
Task<int> streamTask = _networkstream.ReadAsync(_readBuffer, 0, c_readBufferSize, token);
// wait for data to arrive
Task<String> resultTask = streamTask.ContinueWith<String>(antecedent =>
{
// resize the result to the size of the data that was returned
Array.Resize(ref _readBuffer, streamTask.Result);
// convert returned data to string
var result = Encoding.ASCII.GetString(_readBuffer);
return result; // return read string
}, token);
return resultTask;
}
So the way I am trying to do this is on start up, I start a thread that is reading by running the Start() method. But when an exception is thrown it kills my program even though I put some error trapping around it. I have been attempting to trap it at different places just to see what happens but I have not been able to figure out the proper way to do this so that I can raise the error to the UI without bombing out my application.
async public override void Start()
{
try
{
await _client.ReadLinesAsync(_cts.Token).ContinueWith(ReadLinesOnContinuationAction, _cts.Token);
}
catch (AggregateException ae)
{
ae.Handle((exc) =>
{
if (exc is TaskCanceledException)
{
_log.Info(Name + " - Start() - AggregateException"
+ " - OperationCanceledException Handled.");
return true;
}
else
{
_log.Error(Name + " - Start() - AggregateException - Unhandled Exception"
+ exc.Message, ae);
return false;
}
});
}
catch (Exception ex)
{
_log.Error(Name + " - Start() - unhandled exception.", ex);
}
}
async private void ReadLinesOnContinuationAction(Task<String> text)
{
try
{
DataHasBeenReceived = true;
IsConnected = true;
_readLines.Append(text.Result);
if (OnLineRead != null) OnLineRead(Name, _readLines.ToString());
_readLines.Clear();
await _client.ReadLinesAsync(_cts.Token).ContinueWith(ReadLinesOnContinuationAction, _cts.Token);
}
catch (Exception)
{
_log.Error(Name + " - ReadLinesOnContinuationAction()");
}
}
The debugger usually stops on the line:
_readLines.Append(text.Result);
I tried putting this within a check for the text.IsFaulted flag but then I bombed out on the .ContinueWith.
Does anyone have any ideas on what I need to fix this so that I can properly catch the error and raise the even to the UI? This code has all kinds of bad smells to it but I am learning this as I go along. Thanks for any help you can give.
Bombed out with which ContinueWith? You have at least two that I see.
Personally, I would not mix async/await with ContinueWith. The await is already giving you a convenient way to do a ContinueWith, so just use that. For example:
public async Task<string> ReadLinesAsync(CancellationToken token)
{
_readBuffer = new byte[c_readBufferSize];
// start asynchronously reading data
int readResult = await _networkstream.ReadAsync(_readBuffer, 0, c_readBufferSize, token);
// after data arrives...
// resize the result to the size of the data that was returned
Array.Resize(ref _readBuffer, streamTask.Result);
// convert returned data to string
return Encoding.ASCII.GetString(_readBuffer);
}
async public override void Start()
{
try
{
string text = await _client.ReadLinesAsync(_cts.Token);
await ReadLinesOnContinuationAction(text);
}
catch (AggregateException ae)
{
ae.Handle((exc) =>
{
if (exc is TaskCanceledException)
{
_log.Info(Name + " - Start() - AggregateException"
+ " - OperationCanceledException Handled.");
return true;
}
else
{
_log.Error(Name + " - Start() - AggregateException - Unhandled Exception"
+ exc.Message, ae);
return false;
}
});
}
catch (Exception ex)
{
_log.Error(Name + " - Start() - unhandled exception.", ex);
}
}
async private Task ReadLinesOnContinuationAction(Task<String> text)
{
try
{
DataHasBeenReceived = true;
IsConnected = true;
_readLines.Append(text.Result);
if (OnLineRead != null) OnLineRead(Name, _readLines.ToString());
_readLines.Clear();
string text = await _client.ReadLinesAsync(_cts.Token);
await ReadLinesOnContinuationAction(text);
}
catch (Exception)
{
_log.Error(Name + " - ReadLinesOnContinuationAction()");
}
}
Or something like that. The above is obviously not compiled and might require a little bit of massaging to get right. But hopefully the general idea is clear enough.
I have the following function:
private async Task DoSomething(NamespaceConnectionInfo nci)
{
var session = await m_sessionProvider.GetSessionAsync(nci);
SomeLegacySynchronousCode(session);
await m_sessionProvider.EndSessionAsync(session);
}
where EndSessionAsync logs and swallows any exception (like a good destructor).
The problem is that SomeLegacySynchronousCode may throw an exception and then the session leaks.
It is clear to me perfectly why the following code is illegal:
private async Task DoSomething(NamespaceConnectionInfo nci)
{
var session = await m_sessionProvider.GetSessionAsync(nci);
try
{
SomeLegacySynchronousCode(session);
}
finally
{
await m_sessionProvider.EndSessionAsync(session);
}
}
So, I am looking for an alternative that would be both correct and elegant.
Variant I
private async Task DoSomething(NamespaceConnectionInfo nci)
{
var session = await m_sessionProvider.GetSessionAsync(nci);
Exception exc = null;
try
{
SomeLegacySynchronousCode(session);
}
catch (Exception e)
{
exc = e;
}
await m_sessionProvider.EndSessionAsync(session);
if (exc != null)
{
// Wrap to preserve the original stack trace.
throw new AggregateException(exc);
}
}
Variant II
private Task DoSomething(NamespaceConnectionInfo nci)
{
return m_sessionProvider.GetSessionAsync(nci).ContinueWith(t =>
{
Task result = null;
try
{
SomeLegacySynchronousCode(t.Result);
}
finally
{
if (t.Exception == null)
{
result = m_sessionProvider.EndSessionAsync(t.Result);
}
}
return result;
}).Unwrap();
}
Neither are as elegant as the aforementioned illegal async/await version.
I am looking to improve over the two variants that I have proposed, because both are ugly, frankly.
Any ideas?
The commonly-accepted answer appears to be similar to your Variation 1:
You can move the logic outside of the catch block and rethrow the
exception after, if needed, by using ExceptionDispatchInfo.
static async Task f()
{
ExceptionDispatchInfo capturedException = null;
try
{
await TaskThatFails();
}
catch (MyException ex)
{
capturedException = ExceptionDispatchInfo.Capture(ex);
}
if (capturedException != null)
{
await ExceptionHandler();
capturedException.Throw();
}
}
This way, when the caller inspects the exception's StackTrace
property, it still records where inside TaskThatFails it was thrown.