C
C#3y ago
Hulkstance

Task.Run swallows the exceptions [Answered]

Pretty self explanatory. The minimal reproducible example throws an exception in the Task.Run(...) and it doesn't rethrow it out of the Task.Run(...) scope.
public static class PollyRetry
{
public static T Do<T>(Func<T> action, TimeSpan retryWait, int retryCount = 0)
{
var policyResult = Policy
.Handle<Exception>()
.WaitAndRetry(retryCount, retryAttempt => retryWait)
.ExecuteAndCapture(action);

if (policyResult.Outcome == OutcomeType.Failure)
{
throw policyResult.FinalException;
}

return policyResult.Result;
}

public static async Task<T> DoAsync<T>(Func<Task<T>> action, TimeSpan retryWait, int retryCount = 0)
{
var policyResult = await Policy
.Handle<Exception>()
.WaitAndRetryAsync(retryCount, retryAttempt => retryWait)
.ExecuteAndCaptureAsync(action);

if (policyResult.Outcome == OutcomeType.Failure)
{
throw policyResult.FinalException;
}

return policyResult.Result;
}
}

public sealed class MinimalReproducibleCode
{
public async Task StartWithRetry()
{
await Retry.DoAsync(() => Task.FromResult(StartAsync()), TimeSpan.FromSeconds(5), 5);
}

public Task StartAsync()
{
Console.WriteLine("This has just started");

_ = Task.Run(() =>
{
while (true)
{
Console.WriteLine("Code is working");

throw new DivideByZeroException();
}
});

return Task.CompletedTask;
}
}
public static class PollyRetry
{
public static T Do<T>(Func<T> action, TimeSpan retryWait, int retryCount = 0)
{
var policyResult = Policy
.Handle<Exception>()
.WaitAndRetry(retryCount, retryAttempt => retryWait)
.ExecuteAndCapture(action);

if (policyResult.Outcome == OutcomeType.Failure)
{
throw policyResult.FinalException;
}

return policyResult.Result;
}

public static async Task<T> DoAsync<T>(Func<Task<T>> action, TimeSpan retryWait, int retryCount = 0)
{
var policyResult = await Policy
.Handle<Exception>()
.WaitAndRetryAsync(retryCount, retryAttempt => retryWait)
.ExecuteAndCaptureAsync(action);

if (policyResult.Outcome == OutcomeType.Failure)
{
throw policyResult.FinalException;
}

return policyResult.Result;
}
}

public sealed class MinimalReproducibleCode
{
public async Task StartWithRetry()
{
await Retry.DoAsync(() => Task.FromResult(StartAsync()), TimeSpan.FromSeconds(5), 5);
}

public Task StartAsync()
{
Console.WriteLine("This has just started");

_ = Task.Run(() =>
{
while (true)
{
Console.WriteLine("Code is working");

throw new DivideByZeroException();
}
});

return Task.CompletedTask;
}
}
12 Replies
Scratch
Scratch3y ago
uh yeah, that code is a little sus you're firing and forgetting the task. You need to await it for the exception to be thrown
Hulkstance
HulkstanceOP3y ago
yeah, but isn't there another way? since in my real use case I cannot remove the Task.Run
Scratch
Scratch3y ago
No. The exception is happening outside the call stack what are you trying to do at a high level? I know Polly can be used for retrying operations and such
Hulkstance
HulkstanceOP3y ago
this is my real use case scenario
Hulkstance
HulkstanceOP3y ago
it shouldn't block the SendAsync is currently one statement but it should be a Task.Run loop too I was thinking of
var sending = Task.Run(...);
var processing = Task.Run(...);
var receiving = Task.Run(...);

await await Task.WhenAny(sending, processing, receiving);
var sending = Task.Run(...);
var processing = Task.Run(...);
var receiving = Task.Run(...);

await await Task.WhenAny(sending, processing, receiving);
and wrapping the StartAsync logic in a Task.Run(...);
Scratch
Scratch3y ago
here we go, wew. Those Task.Runs should instead be Polly policies or whatever they're called. You need to break it down into what specifically needs to be retried. oh I see you're using Task.Run because you want it to run stuff in the background?
Hulkstance
HulkstanceOP3y ago
ye
Scratch
Scratch3y ago
put your polly retries inside of those tasks around whatever operation you need retried the body of the while loop maybe
Hulkstance
HulkstanceOP3y ago
yeah, but I need to recreate the whole ClientWebSocket instance at each reconnect wouldn't that retry the loop only? ClientWebSocket has to be reinstantiated once it throws, stops or whatever
Scratch
Scratch3y ago
yeah. You'll essentially need a Task.Run for a watchdog. I guess it'll await those worker tasks (also I recommend passing around cancellation tokens, otherwise you can't stop the execution)
Hulkstance
HulkstanceOP3y ago
true, I didn't implement those yet https://github.com/teamhitori/mulplay-container-web/blob/eb39e75e74c44d3c290bcc30737f8b861031fb37/dotnet/Components/WebSocketService.cs quick search on github gave me this result someone did quite the same
Accord
Accord3y ago
✅ This post has been marked as answered!

Did you find this page helpful?