private static async Task Traverse( this CakeGraph graph, string nodeName, Func <string, CancellationTokenSource, Task> executeTask, CancellationTokenSource cancellationTokenSource, IDictionary <string, Task> visitedNodes) { if (visitedNodes.ContainsKey(nodeName)) { await visitedNodes[nodeName].ConfigureAwait(false); return; } var token = cancellationTokenSource.Token; var dependentTasks = graph.Edges .Where(_ => _.End.Equals(nodeName, StringComparison.OrdinalIgnoreCase)) .Select(_ => { var task = graph.Traverse(_.Start, executeTask, cancellationTokenSource, visitedNodes); visitedNodes[_.Start] = task; return(task); }) .ToArray(); if (dependentTasks.Any()) { TaskCompletionSource <object> tcs = new TaskCompletionSource <object>(); token.Register(() => tcs.TrySetCanceled(), false); await Task.WhenAny(Task.WhenAll(dependentTasks), tcs.Task).ConfigureAwait(false); } // The below line does work correctly, but does not bubble up the TaskCanceledException // await executeTask(nodeName, cancellationTokenSource).ConfigureAwait(false); await Task.Run(() => executeTask(nodeName, cancellationTokenSource), token).ConfigureAwait(false); }
public static async Task Traverse(this CakeGraph graph, string target, Func <string, CancellationTokenSource, Task> executeTask) { if (!graph.Exist(target)) { return; } if (graph.HasCircularReferences(target)) { throw new CakeException("Graph contains circular references."); } var cancellationTokenSource = new CancellationTokenSource(); var visitedNodes = new Dictionary <string, Task>(); await graph.Traverse(target, executeTask, cancellationTokenSource, visitedNodes); }