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 |