79162322

Date: 2024-11-06 11:10:00
Score: 1
Natty:
Report link

I used this example in one of my projects, but there were quite an mount of edge cases that were not covered. Since I needed this operation to serve as a core method, I took the time to make it a bit more product ready. Here you are the version I ended with, it met the expectations of my test battery

/// <summary>
/// https://stackoverflow.com/q/11839959/17780206 - Rethrowing previous exception inside ContinueWith
/// </summary>
public static Task<TResult> ContinueWithEx<T, TResult>(this Task<T> task, Func<Task<T>, TResult> continuation)
{
    // we want to run continuations asynchronously so that we don't block the thread, this way we can avoid deadlocks
    var tcs = new TaskCompletionSource<TResult>(TaskCreationOptions.RunContinuationsAsynchronously);

    // we use the default scheduler to avoid deadlocks, this is mostly important for UI applications
    var taskScheduler = TaskScheduler.Default;

    task.ContinueWith(t => {
        if (t.IsFaulted)
        {
            var exception = t.Exception?.InnerExceptions.Count == 1 ? t.Exception.InnerExceptions[0] : t.Exception;
            if (!tcs.TrySetException(exception!))
            {
                // this is not expected to happen, but if it does, log it
                Logger.Warning("Failed to set exception because state of the task is already set", exception);
            }
        }
        else if (t.IsCanceled)
        {
            if (!tcs.TrySetCanceled())
            {
                // this is not expected to happen, but if it does, log it
                Logger.Warning("Failed to set canceled because state of the task is already set");
            }
        }
        else
        {
            // this try catch is important to catch exceptions that might be thrown by the continuation
            // if an exception is thrown, we want to set the exception on the task completion source
            // if we would not do that, the code will freeze in such occurence because the task is never marked as completed/cancelled/failed
            try
            {
                var result = continuation(t);
                if (!tcs.TrySetResult(result))
                {
                    // this is not expected to happen, but if it does, log it
                    Logger.Warning("Failed to set result because state of the task is already set");
                }
            }
            catch (Exception ex)
            {
                if (!tcs.TrySetException(ex))
                {
                    // this is not expected to happen, but if it does, log it
                    Logger.Warning("Failed to set exception because state of the task is already set", ex);
                }
            }
        }
    }, taskScheduler);
    return tcs.Task;
}
Reasons:
  • Blacklisted phrase (0.5): I need
  • Blacklisted phrase (1): stackoverflow
  • Long answer (-1):
  • Has code block (-0.5):
  • Low reputation (1):
Posted by: Blaise Braye