| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | |
| 2 // for details. All rights reserved. Use of this source code is governed by a | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 library task; | |
| 6 | |
| 7 import 'dart:async'; | |
| 8 import 'dart:collection'; | |
| 9 | |
| 10 import 'package:stack_trace/stack_trace.dart'; | |
| 11 | |
| 12 import 'future_group.dart'; | |
| 13 import 'schedule.dart'; | |
| 14 import 'utils.dart'; | |
| 15 | |
| 16 typedef Future TaskBody(); | |
| 17 | |
| 18 /// A single task to be run as part of a [TaskQueue]. | |
| 19 /// | |
| 20 /// There are two levels of tasks. **Top-level tasks** are created by calling | |
| 21 /// [TaskQueue.schedule] before the queue in question is running. They're run in | |
| 22 /// sequence as part of that [TaskQueue]. **Nested tasks** are created by | |
| 23 /// calling [TaskQueue.schedule] once the queue is already running, and are run | |
| 24 /// in parallel as part of a top-level task. | |
| 25 class Task { | |
| 26 /// The queue to which this [Task] belongs. | |
| 27 final TaskQueue queue; | |
| 28 | |
| 29 /// Child tasks that have been spawned while running this task. This will be | |
| 30 /// empty if this task is a nested task. | |
| 31 List<Task> get children => new UnmodifiableListView(_children); | |
| 32 final _children = new Queue<Task>(); | |
| 33 | |
| 34 /// A [FutureGroup] that will complete once all current child tasks are | |
| 35 /// finished running. This will be null if no child tasks are currently | |
| 36 /// running. | |
| 37 FutureGroup _childGroup; | |
| 38 | |
| 39 /// A description of this task. Used for debugging. May be `null`. | |
| 40 final String description; | |
| 41 | |
| 42 /// The parent task, if this is a nested task that was started while another | |
| 43 /// task was running. This will be `null` for top-level tasks. | |
| 44 final Task parent; | |
| 45 | |
| 46 /// The body of the task. | |
| 47 TaskBody fn; | |
| 48 | |
| 49 /// The current state of [this]. | |
| 50 TaskState get state => _state; | |
| 51 var _state = TaskState.WAITING; | |
| 52 | |
| 53 /// The identifier of the task. For top-level tasks, this is the index of the | |
| 54 /// task within [queue]; for nested tasks, this is the index within | |
| 55 /// [parent.children]. It's used for debugging when [description] isn't | |
| 56 /// provided. | |
| 57 int _id; | |
| 58 | |
| 59 /// A Future that will complete to the return value of [fn] once this task | |
| 60 /// finishes running. | |
| 61 Future get result => _resultCompleter.future; | |
| 62 final _resultCompleter = new Completer(); | |
| 63 | |
| 64 final Chain stackChain; | |
| 65 | |
| 66 Task(fn(), String description, TaskQueue queue) | |
| 67 : this._(fn, description, queue, null, queue.contents.length); | |
| 68 | |
| 69 Task._child(fn(), String description, Task parent) | |
| 70 : this._(fn, description, parent.queue, parent, parent.children.length); | |
| 71 | |
| 72 Task._(fn(), this.description, TaskQueue queue, this.parent, this._id) | |
| 73 : queue = queue, | |
| 74 stackChain = new Chain.current() { | |
| 75 this.fn = () { | |
| 76 if (state != TaskState.WAITING) { | |
| 77 throw new StateError("Can't run $state task '$this'."); | |
| 78 } | |
| 79 | |
| 80 _state = TaskState.RUNNING; | |
| 81 var future = new Future.value().then((_) => fn()) | |
| 82 .whenComplete(() { | |
| 83 if (_childGroup == null || _childGroup.completed) return null; | |
| 84 return _childGroup.future; | |
| 85 }); | |
| 86 chainToCompleter(future, _resultCompleter); | |
| 87 return future; | |
| 88 }; | |
| 89 | |
| 90 // If the parent queue experiences an error before this task has started | |
| 91 // running, pipe that error out through [result]. This ensures that we don't | |
| 92 // get deadlocked by something like `expect(schedule(...), completes)`. | |
| 93 queue.onTasksComplete.catchError((e) { | |
| 94 if (state == TaskState.WAITING) _resultCompleter.completeError(e); | |
| 95 }); | |
| 96 | |
| 97 // catchError makes sure any error thrown by fn isn't top-leveled by virtue | |
| 98 // of being passed to the result future. | |
| 99 result.then((_) { | |
| 100 _state = TaskState.SUCCESS; | |
| 101 }).catchError((e) { | |
| 102 _state = TaskState.ERROR; | |
| 103 throw e; | |
| 104 }).catchError((_) {}); | |
| 105 } | |
| 106 | |
| 107 /// Run [fn] as a child of this task. Returns a Future that will complete with | |
| 108 /// the result of the child task. This task will not complete until [fn] has | |
| 109 /// finished. | |
| 110 Future runChild(fn(), String description) { | |
| 111 var task = new Task._child(fn, description, this); | |
| 112 _children.add(task); | |
| 113 if (_childGroup == null || _childGroup.completed) { | |
| 114 _childGroup = new FutureGroup(); | |
| 115 } | |
| 116 // Ignore errors in the FutureGroup; they'll get picked up via wrapFuture, | |
| 117 // and we don't want them to short-circuit the other Futures. | |
| 118 _childGroup.add(task.result.catchError((_) {})); | |
| 119 task.fn(); | |
| 120 return task.result; | |
| 121 } | |
| 122 | |
| 123 String toString() => description == null ? "#$_id" : description; | |
| 124 | |
| 125 String toStringWithStackTrace() { | |
| 126 var stackString = prefixLines(terseTraceString(stackChain)); | |
| 127 return "$this\n\nStack chain:\n$stackString"; | |
| 128 } | |
| 129 | |
| 130 /// Returns a detailed representation of [queue] with this task highlighted. | |
| 131 String generateTree() => queue.generateTree(this); | |
| 132 } | |
| 133 | |
| 134 /// An enum of states for a [Task]. | |
| 135 class TaskState { | |
| 136 /// The task is waiting to be run. | |
| 137 static const WAITING = const TaskState._("WAITING"); | |
| 138 | |
| 139 /// The task is currently running. | |
| 140 static const RUNNING = const TaskState._("RUNNING"); | |
| 141 | |
| 142 /// The task has finished running successfully. | |
| 143 static const SUCCESS = const TaskState._("SUCCESS"); | |
| 144 | |
| 145 /// The task has finished running with an error. | |
| 146 static const ERROR = const TaskState._("ERROR"); | |
| 147 | |
| 148 /// The name of the state. | |
| 149 final String name; | |
| 150 | |
| 151 /// Whether the state indicates that the task has finished running. This is | |
| 152 /// true for both the [SUCCESS] and [ERROR] states. | |
| 153 bool get isDone => this == SUCCESS || this == ERROR; | |
| 154 | |
| 155 const TaskState._(this.name); | |
| 156 | |
| 157 String toString() => name; | |
| 158 } | |
| OLD | NEW |