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); }
private static bool HasCircularReferences(this CakeGraph graph, string nodeName, Stack <string> visited = null) { visited = visited ?? new Stack <string>(); if (visited.Contains(nodeName)) { return(true); } visited.Push(nodeName); var hasCircularReference = graph.Edges .Where(_ => _.End.Equals(nodeName, StringComparison.OrdinalIgnoreCase)) .Any(_ => graph.HasCircularReferences(_.Start, visited)); visited.Pop(); return(hasCircularReference); }
public static CakeGraph Build(List <CakeTask> tasks) { var graph = new CakeGraph(); foreach (var task in tasks) { graph.Add(task.Name); } foreach (var task in tasks) { foreach (var dependency in task.Dependencies) { if (!graph.Exist(dependency.Name)) { const string format = "Task '{0}' is dependent on task '{1}' which does not exist."; var message = string.Format(CultureInfo.InvariantCulture, format, task.Name, dependency); throw new CakeException(message); } graph.Connect(dependency.Name, task.Name); } foreach (var dependee in task.Dependees) { if (!graph.Exist(dependee.Name)) { if (dependee.Required) { const string format = "Task '{0}' has specified that it's a dependency for task '{1}' which does not exist."; var message = string.Format(CultureInfo.InvariantCulture, format, task.Name, dependee.Name); throw new CakeException(message); } } else { graph.Connect(task.Name, dependee.Name); } } } return(graph); }