OLD | NEW |
| (Empty) |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 // This code is adapted from the Timer implementation in the standalone Dart VM | |
6 // for use in the Mojo embedder. | |
7 | |
8 part of core; | |
9 | |
10 // Timer heap implemented as a array-based binary heap[0]. | |
11 // This allows for O(1) `first`, O(log(n)) `remove`/`removeFirst` and O(log(n)) | |
12 // `add`. | |
13 // | |
14 // To ensure the timers are ordered by insertion time, the _Timer class has a | |
15 // `_id` field set when added to the heap. | |
16 // | |
17 // [0] http://en.wikipedia.org/wiki/Binary_heap | |
18 class _TimerHeap { | |
19 List<_Timer> _list; | |
20 int _used = 0; | |
21 | |
22 _TimerHeap([int initSize = 7]) | |
23 : _list = new List<_Timer>(initSize); | |
24 | |
25 bool get isEmpty => _used == 0; | |
26 bool get isNotEmpty => _used > 0; | |
27 | |
28 _Timer get first => _list[0]; | |
29 | |
30 bool isFirst(_Timer timer) => timer._indexOrNext == 0; | |
31 | |
32 void add(_Timer timer) { | |
33 if (_used == _list.length) { | |
34 _resize(); | |
35 } | |
36 timer._indexOrNext = _used++; | |
37 _list[timer._indexOrNext] = timer; | |
38 _bubbleUp(timer); | |
39 } | |
40 | |
41 _Timer removeFirst() { | |
42 var f = first; | |
43 remove(f); | |
44 return f; | |
45 } | |
46 | |
47 void remove(_Timer timer) { | |
48 _used--; | |
49 timer._id = -1; | |
50 if (isEmpty) { | |
51 _list[0] = null; | |
52 timer._indexOrNext = null; | |
53 return; | |
54 } | |
55 var last = _list[_used]; | |
56 if (!identical(last, timer)) { | |
57 last._indexOrNext = timer._indexOrNext; | |
58 _list[last._indexOrNext] = last; | |
59 if (last._compareTo(timer) < 0) { | |
60 _bubbleUp(last); | |
61 } else { | |
62 _bubbleDown(last); | |
63 } | |
64 } | |
65 _list[_used] = null; | |
66 timer._indexOrNext = null; | |
67 } | |
68 | |
69 void _resize() { | |
70 var newList = new List(_list.length * 2 + 1); | |
71 newList.setRange(0, _used, _list); | |
72 _list = newList; | |
73 } | |
74 | |
75 void _bubbleUp(_Timer timer) { | |
76 while (!isFirst(timer)) { | |
77 Timer parent = _parent(timer); | |
78 if (timer._compareTo(parent) < 0) { | |
79 _swap(timer, parent); | |
80 } else { | |
81 break; | |
82 } | |
83 } | |
84 } | |
85 | |
86 void _bubbleDown(_Timer timer) { | |
87 while (true) { | |
88 int leftIndex = _leftChildIndex(timer._indexOrNext); | |
89 int rightIndex = _rightChildIndex(timer._indexOrNext); | |
90 _Timer newest = timer; | |
91 if (leftIndex < _used && _list[leftIndex]._compareTo(newest) < 0) { | |
92 newest = _list[leftIndex]; | |
93 } | |
94 if (rightIndex < _used && _list[rightIndex]._compareTo(newest) < 0) { | |
95 newest = _list[rightIndex]; | |
96 } | |
97 if (identical(newest, timer)) { | |
98 // We are where we should be, break. | |
99 break; | |
100 } | |
101 _swap(newest, timer); | |
102 } | |
103 } | |
104 | |
105 void _swap(_Timer first, _Timer second) { | |
106 int tmp = first._indexOrNext; | |
107 first._indexOrNext = second._indexOrNext; | |
108 second._indexOrNext = tmp; | |
109 _list[first._indexOrNext] = first; | |
110 _list[second._indexOrNext] = second; | |
111 } | |
112 | |
113 Timer _parent(_Timer timer) => _list[_parentIndex(timer._indexOrNext)]; | |
114 Timer _leftChild(_Timer timer) => _list[_leftChildIndex(timer._indexOrNext)]; | |
115 Timer _rightChild(_Timer timer) => | |
116 _list[_rightChildIndex(timer._indexOrNext)]; | |
117 | |
118 static int _parentIndex(int index) => (index - 1) ~/ 2; | |
119 static int _leftChildIndex(int index) => 2 * index + 1; | |
120 static int _rightChildIndex(int index) => 2 * index + 2; | |
121 } | |
122 | |
123 class _Timer implements Timer { | |
124 // Disables the timer. | |
125 static const int _NO_TIMER = -1; | |
126 | |
127 // Timers are ordered by wakeup time. | |
128 static _TimerHeap _heap = new _TimerHeap(); | |
129 static _Timer _firstZeroTimer; | |
130 static _Timer _lastZeroTimer; | |
131 static int _idCount = 0; | |
132 | |
133 static RawReceivePort _receivePort; | |
134 static SendPort _sendPort; | |
135 static bool _handlingCallbacks = false; | |
136 | |
137 Function _callback; | |
138 int _milliSeconds; | |
139 int _wakeupTime = 0; | |
140 var _indexOrNext; | |
141 int _id = -1; | |
142 | |
143 static Timer _createTimer(void callback(Timer timer), | |
144 int milliSeconds, | |
145 bool repeating) { | |
146 _Timer timer = new _Timer._internal(); | |
147 timer._callback = callback; | |
148 if (milliSeconds > 0) { | |
149 // Add one because DateTime.now() is assumed to round down | |
150 // to nearest millisecond, not up, so that time + duration is before | |
151 // duration milliseconds from now. Using micosecond timers like | |
152 // Stopwatch allows detecting that the timer fires early. | |
153 timer._wakeupTime = | |
154 new DateTime.now().millisecondsSinceEpoch + 1 + milliSeconds; | |
155 } | |
156 timer._milliSeconds = repeating ? milliSeconds : -1; | |
157 if (timer._addTimerToHeap()) { | |
158 // The new timer is the first in queue. Update event handler. | |
159 _notifyEventHandler(); | |
160 } | |
161 return timer; | |
162 } | |
163 | |
164 factory _Timer(int milliSeconds, void callback(Timer timer)) { | |
165 return _createTimer(callback, milliSeconds, false); | |
166 } | |
167 | |
168 factory _Timer.periodic(int milliSeconds, void callback(Timer timer)) { | |
169 return _createTimer(callback, milliSeconds, true); | |
170 } | |
171 | |
172 _Timer._internal() {} | |
173 | |
174 bool get _isInHeap => _id >= 0; | |
175 | |
176 void _clear() { | |
177 _callback = null; | |
178 } | |
179 | |
180 int _compareTo(_Timer other) { | |
181 int c = _wakeupTime - other._wakeupTime; | |
182 if (c != 0) return c; | |
183 return _id - other._id; | |
184 } | |
185 | |
186 bool get _repeating => _milliSeconds >= 0; | |
187 | |
188 bool get isActive => _callback != null; | |
189 | |
190 // Cancels a set timer. The timer is removed from the timer list and if | |
191 // the given timer is the earliest timer the native timer is reset. | |
192 void cancel() { | |
193 _clear(); | |
194 if (!_isInHeap) return; | |
195 assert(_wakeupTime != 0); | |
196 bool update = (_firstZeroTimer == null) && _heap.isFirst(this); | |
197 _heap.remove(this); | |
198 if (update) { | |
199 _notifyEventHandler(); | |
200 } | |
201 } | |
202 | |
203 void _advanceWakeupTime() { | |
204 assert(_milliSeconds >= 0); | |
205 _wakeupTime += _milliSeconds; | |
206 } | |
207 | |
208 // Adds a timer to the timer list. Timers with the same wakeup time are | |
209 // enqueued in order and notified in FIFO order. | |
210 bool _addTimerToHeap() { | |
211 if (_wakeupTime == 0) { | |
212 if (_firstZeroTimer == null) { | |
213 _lastZeroTimer = this; | |
214 _firstZeroTimer = this; | |
215 return true; | |
216 } else { | |
217 _lastZeroTimer._indexOrNext = this; | |
218 _lastZeroTimer = this; | |
219 return false; | |
220 } | |
221 } else { | |
222 _id = _idCount++; | |
223 _heap.add(this); | |
224 return _firstZeroTimer == null && _heap.isFirst(this); | |
225 } | |
226 } | |
227 | |
228 | |
229 static void _notifyEventHandler() { | |
230 if (_handlingCallbacks) { | |
231 // While we are already handling callbacks we will not notify the event | |
232 // handler. _handleTimeout will call _notifyEventHandler once all pending | |
233 // timers are processed. | |
234 return; | |
235 } | |
236 | |
237 if (_firstZeroTimer == null && _heap.isEmpty) { | |
238 // No pending timers: Close the receive port and let the event handler | |
239 // know. | |
240 if (_receivePort != null) { | |
241 MojoHandleWatcher.timer(_sendPort, _NO_TIMER); | |
242 _shutdownTimerHandler(); | |
243 } | |
244 } else { | |
245 if (_receivePort == null) { | |
246 // Create a receive port and register a message handler for the timer | |
247 // events. | |
248 _createTimerHandler(); | |
249 } | |
250 if (_firstZeroTimer != null) { | |
251 _sendPort.send(null); | |
252 } else { | |
253 MojoHandleWatcher.timer(_sendPort, _heap.first._wakeupTime); | |
254 } | |
255 } | |
256 } | |
257 | |
258 static void _handleTimeout(_) { | |
259 int currentTime = new DateTime.now().millisecondsSinceEpoch; | |
260 // Collect all pending timers. | |
261 var timer = _firstZeroTimer; | |
262 var nextTimer = _lastZeroTimer; | |
263 _firstZeroTimer = null; | |
264 _lastZeroTimer = null; | |
265 while (_heap.isNotEmpty && _heap.first._wakeupTime <= currentTime) { | |
266 var next = _heap.removeFirst(); | |
267 if (timer == null) { | |
268 nextTimer = next; | |
269 timer = next; | |
270 } else { | |
271 nextTimer._indexOrNext = next; | |
272 nextTimer = next; | |
273 } | |
274 } | |
275 | |
276 // Trigger all of the pending timers. New timers added as part of the | |
277 // callbacks will be enqueued now and notified in the next spin at the | |
278 // earliest. | |
279 _handlingCallbacks = true; | |
280 try { | |
281 while (timer != null) { | |
282 var next = timer._indexOrNext; | |
283 timer._indexOrNext = null; | |
284 // One of the timers in the pending_timers list can cancel | |
285 // one of the later timers which will set the callback to | |
286 // null. | |
287 if (timer._callback != null) { | |
288 var callback = timer._callback; | |
289 if (!timer._repeating) { | |
290 // Mark timer as inactive. | |
291 timer._callback = null; | |
292 } | |
293 callback(timer); | |
294 // Re-insert repeating timer if not canceled. | |
295 if (timer._repeating && timer._callback != null) { | |
296 timer._advanceWakeupTime(); | |
297 timer._addTimerToHeap(); | |
298 } | |
299 } | |
300 timer = next; | |
301 } | |
302 } finally { | |
303 _handlingCallbacks = false; | |
304 _notifyEventHandler(); | |
305 } | |
306 } | |
307 | |
308 // Creates a receive port and registers the timer handler on that | |
309 // receive port. | |
310 static void _createTimerHandler() { | |
311 if(_receivePort == null) { | |
312 _receivePort = new RawReceivePort(_handleTimeout); | |
313 _sendPort = _receivePort.sendPort; | |
314 } | |
315 } | |
316 | |
317 static void _shutdownTimerHandler() { | |
318 _receivePort.close(); | |
319 _receivePort = null; | |
320 _sendPort = null; | |
321 } | |
322 } | |
323 | |
324 // Provide a closure which will allocate a Timer object to be able to hook | |
325 // up the Timer interface in dart:isolate with the implementation here. | |
326 _getTimerFactoryClosure() { | |
327 return (int milliSeconds, void callback(Timer timer), bool repeating) { | |
328 if (repeating) { | |
329 return new _Timer.periodic(milliSeconds, callback); | |
330 } | |
331 return new _Timer(milliSeconds, callback); | |
332 }; | |
333 } | |
OLD | NEW |