| 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 part of dart.async; | |
| 6 | |
| 7 typedef void _AsyncCallback(); | |
| 8 | |
| 9 class _AsyncCallbackEntry { | |
| 10 final _AsyncCallback callback; | |
| 11 _AsyncCallbackEntry next; | |
| 12 _AsyncCallbackEntry(this.callback); | |
| 13 } | |
| 14 | |
| 15 /** Head of single linked list of pending callbacks. */ | |
| 16 _AsyncCallbackEntry _nextCallback; | |
| 17 /** Tail of single linked list of pending callbacks. */ | |
| 18 _AsyncCallbackEntry _lastCallback; | |
| 19 /** | |
| 20 * Tail of priority callbacks added by the currently executing callback. | |
| 21 * | |
| 22 * Priority callbacks are put at the beginning of the | |
| 23 * callback queue, so that if one callback schedules more than one | |
| 24 * priority callback, they are still enqueued in scheduling order. | |
| 25 */ | |
| 26 _AsyncCallbackEntry _lastPriorityCallback; | |
| 27 /** | |
| 28 * Whether we are currently inside the callback loop. | |
| 29 * | |
| 30 * If we are inside the loop, we never need to schedule the loop, | |
| 31 * even if adding a first element. | |
| 32 */ | |
| 33 bool _isInCallbackLoop = false; | |
| 34 | |
| 35 void _microtaskLoop() { | |
| 36 while (_nextCallback != null) { | |
| 37 _lastPriorityCallback = null; | |
| 38 _AsyncCallbackEntry entry = _nextCallback; | |
| 39 _nextCallback = entry.next; | |
| 40 if (_nextCallback == null) _lastCallback = null; | |
| 41 (entry.callback)(); | |
| 42 } | |
| 43 } | |
| 44 | |
| 45 void _startMicrotaskLoop() { | |
| 46 _isInCallbackLoop = true; | |
| 47 try { | |
| 48 // Moved to separate function because try-finally prevents | |
| 49 // good optimization. | |
| 50 _microtaskLoop(); | |
| 51 } finally { | |
| 52 _lastPriorityCallback = null; | |
| 53 _isInCallbackLoop = false; | |
| 54 if (_nextCallback != null) { | |
| 55 _AsyncRun._scheduleImmediate(_startMicrotaskLoop); | |
| 56 } | |
| 57 } | |
| 58 } | |
| 59 | |
| 60 /** | |
| 61 * Schedules a callback to be called as a microtask. | |
| 62 * | |
| 63 * The microtask is called after all other currently scheduled | |
| 64 * microtasks, but as part of the current system event. | |
| 65 */ | |
| 66 void _scheduleAsyncCallback(_AsyncCallback callback) { | |
| 67 _AsyncCallbackEntry newEntry = new _AsyncCallbackEntry(callback); | |
| 68 if (_nextCallback == null) { | |
| 69 _nextCallback = _lastCallback = newEntry; | |
| 70 if (!_isInCallbackLoop) { | |
| 71 _AsyncRun._scheduleImmediate(_startMicrotaskLoop); | |
| 72 } | |
| 73 } else { | |
| 74 _lastCallback.next = newEntry; | |
| 75 _lastCallback = newEntry; | |
| 76 } | |
| 77 } | |
| 78 | |
| 79 /** | |
| 80 * Schedules a callback to be called before all other currently scheduled ones. | |
| 81 * | |
| 82 * This callback takes priority over existing scheduled callbacks. | |
| 83 * It is only used internally to give higher priority to error reporting. | |
| 84 * | |
| 85 * Is always run in the root zone. | |
| 86 */ | |
| 87 void _schedulePriorityAsyncCallback(_AsyncCallback callback) { | |
| 88 if (_nextCallback == null) { | |
| 89 _scheduleAsyncCallback(callback); | |
| 90 _lastPriorityCallback = _lastCallback; | |
| 91 return; | |
| 92 } | |
| 93 _AsyncCallbackEntry entry = new _AsyncCallbackEntry(callback); | |
| 94 if (_lastPriorityCallback == null) { | |
| 95 entry.next = _nextCallback; | |
| 96 _nextCallback = _lastPriorityCallback = entry; | |
| 97 } else { | |
| 98 entry.next = _lastPriorityCallback.next; | |
| 99 _lastPriorityCallback.next = entry; | |
| 100 _lastPriorityCallback = entry; | |
| 101 if (entry.next == null) { | |
| 102 _lastCallback = entry; | |
| 103 } | |
| 104 } | |
| 105 } | |
| 106 | |
| 107 /** | |
| 108 * Runs a function asynchronously. | |
| 109 * | |
| 110 * Callbacks registered through this function are always executed in order and | |
| 111 * are guaranteed to run before other asynchronous events (like [Timer] events, | |
| 112 * or DOM events). | |
| 113 * | |
| 114 * **Warning:** it is possible to starve the DOM by registering asynchronous | |
| 115 * callbacks through this method. For example the following program runs | |
| 116 * the callbacks without ever giving the Timer callback a chance to execute: | |
| 117 * | |
| 118 * main() { | |
| 119 * Timer.run(() { print("executed"); }); // Will never be executed. | |
| 120 * foo() { | |
| 121 * scheduleMicrotask(foo); // Schedules [foo] in front of other events. | |
| 122 * } | |
| 123 * foo(); | |
| 124 * } | |
| 125 * | |
| 126 * ## Other resources | |
| 127 * | |
| 128 * * [The Event Loop and Dart](https://www.dartlang.org/articles/event-loop/): | |
| 129 * Learn how Dart handles the event queue and microtask queue, so you can write | |
| 130 * better asynchronous code with fewer surprises. | |
| 131 */ | |
| 132 void scheduleMicrotask(void callback()) { | |
| 133 _Zone currentZone = Zone.current; | |
| 134 if (identical(_ROOT_ZONE, currentZone)) { | |
| 135 // No need to bind the callback. We know that the root's scheduleMicrotask | |
| 136 // will be invoked in the root zone. | |
| 137 _rootScheduleMicrotask(null, null, _ROOT_ZONE, callback); | |
| 138 return; | |
| 139 } | |
| 140 _ZoneFunction implementation = currentZone._scheduleMicrotask; | |
| 141 if (identical(_ROOT_ZONE, implementation.zone) && | |
| 142 _ROOT_ZONE.inSameErrorZone(currentZone)) { | |
| 143 _rootScheduleMicrotask(null, null, currentZone, | |
| 144 currentZone.registerCallback(callback)); | |
| 145 return; | |
| 146 } | |
| 147 Zone.current.scheduleMicrotask( | |
| 148 Zone.current.bindCallback(callback, runGuarded: true)); | |
| 149 } | |
| 150 | |
| 151 class _AsyncRun { | |
| 152 /** Schedule the given callback before any other event in the event-loop. */ | |
| 153 external static void _scheduleImmediate(void callback()); | |
| 154 } | |
| OLD | NEW |