OLD | NEW |
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 // Timer heap implemented as a array-based binary heap[0]. | 5 // Timer heap implemented as a array-based binary heap[0]. |
6 // This allows for O(1) `first`, O(log(n)) `remove`/`removeFirst` and O(log(n)) | 6 // This allows for O(1) `first`, O(log(n)) `remove`/`removeFirst` and O(log(n)) |
7 // `add`. | 7 // `add`. |
8 // | 8 // |
9 // To ensure the timers are ordered by insertion time, the _Timer class has a | 9 // To ensure the timers are ordered by insertion time, the _Timer class has a |
10 // `_id` field set when added to the heap. | 10 // `_id` field set when added to the heap. |
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
108 Timer _rightChild(_Timer timer) => | 108 Timer _rightChild(_Timer timer) => |
109 _list[_rightChildIndex(timer._indexOrNext)]; | 109 _list[_rightChildIndex(timer._indexOrNext)]; |
110 | 110 |
111 static int _parentIndex(int index) => (index - 1) ~/ 2; | 111 static int _parentIndex(int index) => (index - 1) ~/ 2; |
112 static int _leftChildIndex(int index) => 2 * index + 1; | 112 static int _leftChildIndex(int index) => 2 * index + 1; |
113 static int _rightChildIndex(int index) => 2 * index + 2; | 113 static int _rightChildIndex(int index) => 2 * index + 2; |
114 } | 114 } |
115 | 115 |
116 class _Timer implements Timer { | 116 class _Timer implements Timer { |
117 // Cancels the timer in the event handler. | 117 // Cancels the timer in the event handler. |
118 static const int _NO_TIMER = -1; | 118 static const _NO_TIMER = -1; |
119 | 119 |
120 // We distinguish what kind of message arrived based on the value being sent. | 120 // We distinguish what kind of message arrived based on the value being sent. |
121 static const _ZERO_EVENT = 1; | 121 static const _ZERO_EVENT = 1; |
122 static const _TIMEOUT_EVENT = null; | 122 static const _TIMEOUT_EVENT = null; |
123 | 123 |
124 // Timers are ordered by wakeup time. Timers with a timeout value of > 0 do | 124 // Timers are ordered by wakeup time. Timers with a timeout value of > 0 do |
125 // end up on the TimerHeap. Timers with a timeout of 0 are queued in a list. | 125 // end up on the TimerHeap. Timers with a timeout of 0 are queued in a list. |
126 static _TimerHeap _heap = new _TimerHeap(); | 126 static _TimerHeap _heap = new _TimerHeap(); |
127 static _Timer _firstZeroTimer; | 127 static _Timer _firstZeroTimer; |
128 static _Timer _lastZeroTimer; | 128 static _Timer _lastZeroTimer; |
129 | 129 |
130 // We use an id to be able to sort timers with the same expiration time. | 130 // We use an id to be able to sort timers with the same expiration time. |
131 // ids are recycled after ID_MASK enqueues or when the timer queue is empty. | 131 // ids are recycled after ID_MASK enqueues or when the timer queue is empty. |
132 static int _ID_MASK = 0x1fffffff; | 132 static const _ID_MASK = 0x1fffffff; |
133 static int _idCount = 0; | 133 static int _idCount = 0; |
134 | 134 |
135 static RawReceivePort _receivePort; | 135 static RawReceivePort _receivePort; |
136 static SendPort _sendPort; | 136 static SendPort _sendPort; |
137 static int _scheduledWakeupTime; | 137 static int _scheduledWakeupTime; |
138 | 138 |
139 static bool _handlingCallbacks = false; | 139 static bool _handlingCallbacks = false; |
140 | 140 |
141 Function _callback; // Closure to call when timer fires. null if canceled. | 141 Function _callback; // Closure to call when timer fires. null if canceled. |
142 int _wakeupTime; // Expiration time. | 142 int _wakeupTime; // Expiration time. |
143 int _milliSeconds; // Duration specified at creation. | 143 final int _milliSeconds; // Duration specified at creation. |
144 bool _repeating; // Indicates periodic timers. | 144 final bool _repeating; // Indicates periodic timers. |
145 var _indexOrNext; // Index if part of the TimerHeap, link otherwise. | 145 var _indexOrNext; // Index if part of the TimerHeap, link otherwise. |
146 int _id; // Incrementing id to enable sorting of timers with same expiry. | 146 int _id; // Incrementing id to enable sorting of timers with same expiry. |
147 | 147 |
| 148 |
148 // Get the next available id. We accept collisions and reordering when the | 149 // Get the next available id. We accept collisions and reordering when the |
149 // _idCount overflows and the timers expire at the same millisecond. | 150 // _idCount overflows and the timers expire at the same millisecond. |
150 static int _nextId() { | 151 static int _nextId() { |
151 var result = _idCount; | 152 var result = _idCount; |
152 _idCount = (_idCount + 1) & _ID_MASK; | 153 _idCount = (_idCount + 1) & _ID_MASK; |
153 return result; | 154 return result; |
154 } | 155 } |
155 | 156 |
| 157 |
156 _Timer._internal(this._callback, | 158 _Timer._internal(this._callback, |
157 this._wakeupTime, | 159 this._wakeupTime, |
158 this._milliSeconds, | 160 this._milliSeconds, |
159 this._repeating) : _id = _nextId(); | 161 this._repeating) : _id = _nextId(); |
160 | 162 |
| 163 |
161 static Timer _createTimer(void callback(Timer timer), | 164 static Timer _createTimer(void callback(Timer timer), |
162 int milliSeconds, | 165 int milliSeconds, |
163 bool repeating) { | 166 bool repeating) { |
164 // Negative timeouts are treated as if 0 timeout. | 167 // Negative timeouts are treated as if 0 timeout. |
165 if (milliSeconds < 0) { | 168 if (milliSeconds < 0) { |
166 milliSeconds = 0; | 169 milliSeconds = 0; |
167 } | 170 } |
168 // Add one because DateTime.now() is assumed to round down | 171 // Add one because DateTime.now() is assumed to round down |
169 // to nearest millisecond, not up, so that time + duration is before | 172 // to nearest millisecond, not up, so that time + duration is before |
170 // duration milliseconds from now. Using microsecond timers like | 173 // duration milliseconds from now. Using microsecond timers like |
171 // Stopwatch allows detecting that the timer fires early. | 174 // Stopwatch allows detecting that the timer fires early. |
172 int now = new DateTime.now().millisecondsSinceEpoch; | 175 int now = new DateTime.now().millisecondsSinceEpoch; |
173 int wakeupTime = (milliSeconds == 0) ? now : (now + 1 + milliSeconds); | 176 int wakeupTime = (milliSeconds == 0) ? now : (now + 1 + milliSeconds); |
174 | 177 |
175 _Timer timer = new _Timer._internal(callback, | 178 _Timer timer = new _Timer._internal(callback, |
176 wakeupTime, | 179 wakeupTime, |
177 milliSeconds, | 180 milliSeconds, |
178 repeating); | 181 repeating); |
179 // Enqueue this newly created timer in the appropriate structure and | 182 // Enqueue this newly created timer in the appropriate structure and |
180 // notify if necessary. | 183 // notify if necessary. |
181 timer._enqueue(); | 184 timer._enqueue(); |
182 return timer; | 185 return timer; |
183 } | 186 } |
184 | 187 |
| 188 |
185 factory _Timer(int milliSeconds, void callback(Timer timer)) { | 189 factory _Timer(int milliSeconds, void callback(Timer timer)) { |
186 return _createTimer(callback, milliSeconds, false); | 190 return _createTimer(callback, milliSeconds, false); |
187 } | 191 } |
188 | 192 |
| 193 |
189 factory _Timer.periodic(int milliSeconds, void callback(Timer timer)) { | 194 factory _Timer.periodic(int milliSeconds, void callback(Timer timer)) { |
190 return _createTimer(callback, milliSeconds, true); | 195 return _createTimer(callback, milliSeconds, true); |
191 } | 196 } |
192 | 197 |
| 198 |
193 bool get _isInHeap => _indexOrNext is int; | 199 bool get _isInHeap => _indexOrNext is int; |
194 | 200 |
| 201 |
195 int _compareTo(_Timer other) { | 202 int _compareTo(_Timer other) { |
196 int c = _wakeupTime - other._wakeupTime; | 203 int c = _wakeupTime - other._wakeupTime; |
197 if (c != 0) return c; | 204 if (c != 0) return c; |
198 return _id - other._id; | 205 return _id - other._id; |
199 } | 206 } |
200 | 207 |
| 208 |
201 bool get isActive => _callback != null; | 209 bool get isActive => _callback != null; |
202 | 210 |
| 211 |
203 // Cancels a set timer. The timer is removed from the timer heap if it is a | 212 // Cancels a set timer. The timer is removed from the timer heap if it is a |
204 // non-zero timer. Zero timers are kept in the list as they need to consume | 213 // non-zero timer. Zero timers are kept in the list as they need to consume |
205 // the corresponding pending message. | 214 // the corresponding pending message. |
206 void cancel() { | 215 void cancel() { |
207 _callback = null; | 216 _callback = null; |
208 // Only heap timers are really removed. Zero timers need to consume their | 217 // Only heap timers are really removed. Zero timers need to consume their |
209 // corresponding wakeup message so they are left in the queue. | 218 // corresponding wakeup message so they are left in the queue. |
210 if (!_isInHeap) return; | 219 if (!_isInHeap) return; |
211 bool update = _heap.isFirst(this); | 220 bool update = _heap.isFirst(this); |
212 _heap.remove(this); | 221 _heap.remove(this); |
213 if (update) { | 222 if (update) { |
214 _notifyEventHandler(); | 223 _notifyEventHandler(); |
215 } | 224 } |
216 } | 225 } |
217 | 226 |
| 227 |
218 void _advanceWakeupTime() { | 228 void _advanceWakeupTime() { |
219 // Recalculate the next wakeup time. For repeating timers with a 0 timeout | 229 // Recalculate the next wakeup time. For repeating timers with a 0 timeout |
220 // the next wakeup time is now. | 230 // the next wakeup time is now. |
221 _id = _nextId(); | 231 _id = _nextId(); |
222 if (_milliSeconds > 0) { | 232 if (_milliSeconds > 0) { |
223 _wakeupTime += _milliSeconds; | 233 _wakeupTime += _milliSeconds; |
224 } else { | 234 } else { |
225 _wakeupTime = new DateTime.now().millisecondsSinceEpoch; | 235 _wakeupTime = new DateTime.now().millisecondsSinceEpoch; |
226 } | 236 } |
227 } | 237 } |
228 | 238 |
| 239 |
229 // Adds a timer to the heap or timer list. Timers with the same wakeup time | 240 // Adds a timer to the heap or timer list. Timers with the same wakeup time |
230 // are enqueued in order and notified in FIFO order. | 241 // are enqueued in order and notified in FIFO order. |
231 void _enqueue() { | 242 void _enqueue() { |
232 if (_milliSeconds == 0) { | 243 if (_milliSeconds == 0) { |
233 if (_firstZeroTimer == null) { | 244 if (_firstZeroTimer == null) { |
234 _lastZeroTimer = this; | 245 _lastZeroTimer = this; |
235 _firstZeroTimer = this; | 246 _firstZeroTimer = this; |
236 } else { | 247 } else { |
237 _lastZeroTimer._indexOrNext = this; | 248 _lastZeroTimer._indexOrNext = this; |
238 _lastZeroTimer = this; | 249 _lastZeroTimer = this; |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
285 // handler. _handleTimeout will call _notifyEventHandler once all pending | 296 // handler. _handleTimeout will call _notifyEventHandler once all pending |
286 // timers are processed. | 297 // timers are processed. |
287 return; | 298 return; |
288 } | 299 } |
289 | 300 |
290 // If there are no pending timers. Close down the receive port. | 301 // If there are no pending timers. Close down the receive port. |
291 if ((_firstZeroTimer == null) && _heap.isEmpty) { | 302 if ((_firstZeroTimer == null) && _heap.isEmpty) { |
292 // No pending timers: Close the receive port and let the event handler | 303 // No pending timers: Close the receive port and let the event handler |
293 // know. | 304 // know. |
294 if (_sendPort != null) { | 305 if (_sendPort != null) { |
295 VMLibraryHooks.eventHandlerSendData(null, _sendPort, _NO_TIMER); | 306 _cancelWakeup(); |
296 _shutdownTimerHandler(); | 307 _shutdownTimerHandler(); |
297 } | 308 } |
298 return; | 309 return; |
299 } else if (_heap.isEmpty) { | 310 } else if (_heap.isEmpty) { |
300 // Only zero timers are left. Cancel any scheduled wakeups. | 311 // Only zero timers are left. Cancel any scheduled wakeups. |
301 VMLibraryHooks.eventHandlerSendData(null, _sendPort, _NO_TIMER); | 312 _cancelWakeup(); |
302 return; | 313 return; |
303 } | 314 } |
304 | 315 |
305 // Only send a message if the requested wakeup time differs from the | 316 // Only send a message if the requested wakeup time differs from the |
306 // already scheduled wakeup time. | 317 // already scheduled wakeup time. |
307 var wakeupTime = _heap.first._wakeupTime; | 318 var wakeupTime = _heap.first._wakeupTime; |
308 if ((_scheduledWakeupTime == null) || | 319 if ((_scheduledWakeupTime == null) || |
309 (wakeupTime != _scheduledWakeupTime)) { | 320 (wakeupTime != _scheduledWakeupTime)) { |
310 if (_sendPort == null) { | 321 _scheduleWakeup(wakeupTime); |
311 // Create a receive port and register a message handler for the timer | |
312 // events. | |
313 _createTimerHandler(); | |
314 } | |
315 VMLibraryHooks.eventHandlerSendData(null, _sendPort, wakeupTime); | |
316 _scheduledWakeupTime = wakeupTime; | |
317 } | 322 } |
318 } | 323 } |
319 | 324 |
| 325 |
320 static List _queueFromTimeoutEvent() { | 326 static List _queueFromTimeoutEvent() { |
321 var pendingTimers = new List(); | 327 var pendingTimers = new List(); |
322 if (_firstZeroTimer != null) { | 328 if (_firstZeroTimer != null) { |
323 // Collect pending timers from the timer heap that have an expiration | 329 // Collect pending timers from the timer heap that have an expiration |
324 // prior to the next zero timer. | 330 // prior to the next zero timer. |
325 // By definition the first zero timer has been scheduled before the | 331 // By definition the first zero timer has been scheduled before the |
326 // current time, meaning all timers which are "less than" the first zero | 332 // current time, meaning all timers which are "less than" the first zero |
327 // timer are expired. The first zero timer will be dispatched when its | 333 // timer are expired. The first zero timer will be dispatched when its |
328 // corresponding message is delivered. | 334 // corresponding message is delivered. |
329 var timer; | 335 var timer; |
330 while (!_heap.isEmpty && (_heap.first._compareTo(_firstZeroTimer) < 0)) { | 336 while (!_heap.isEmpty && (_heap.first._compareTo(_firstZeroTimer) < 0)) { |
331 timer = _heap.removeFirst(); | 337 timer = _heap.removeFirst(); |
332 pendingTimers.add(timer); | 338 pendingTimers.add(timer); |
333 } | 339 } |
334 } else { | 340 } else { |
335 // Collect pending timers from the timer heap which have expired at this | 341 // Collect pending timers from the timer heap which have expired at this |
336 // time. | 342 // time. |
337 var currentTime = new DateTime.now().millisecondsSinceEpoch; | 343 var currentTime = new DateTime.now().millisecondsSinceEpoch; |
338 var timer; | 344 var timer; |
339 while (!_heap.isEmpty && (_heap.first._wakeupTime <= currentTime)) { | 345 while (!_heap.isEmpty && (_heap.first._wakeupTime <= currentTime)) { |
340 timer = _heap.removeFirst(); | 346 timer = _heap.removeFirst(); |
341 pendingTimers.add(timer); | 347 pendingTimers.add(timer); |
342 } | 348 } |
343 } | 349 } |
344 return pendingTimers; | 350 return pendingTimers; |
345 } | 351 } |
346 | 352 |
| 353 |
347 static void _runTimers(List pendingTimers) { | 354 static void _runTimers(List pendingTimers) { |
348 // If there are no pending timers currently reset the id space before we | 355 // If there are no pending timers currently reset the id space before we |
349 // have a chance to enqueue new timers. | 356 // have a chance to enqueue new timers. |
350 if (_heap.isEmpty && (_firstZeroTimer == null)) { | 357 if (_heap.isEmpty && (_firstZeroTimer == null)) { |
351 _idCount = 0; | 358 _idCount = 0; |
352 } | 359 } |
353 | 360 |
354 // Fast exit if no pending timers. | 361 // Fast exit if no pending timers. |
355 if (pendingTimers.length == 0) { | 362 if (pendingTimers.length == 0) { |
356 return; | 363 return; |
(...skipping 23 matching lines...) Expand all Loading... |
380 if (timer._repeating && (timer._callback != null)) { | 387 if (timer._repeating && (timer._callback != null)) { |
381 timer._advanceWakeupTime(); | 388 timer._advanceWakeupTime(); |
382 timer._enqueue(); | 389 timer._enqueue(); |
383 } | 390 } |
384 // Execute pending micro tasks. | 391 // Execute pending micro tasks. |
385 _runPendingImmediateCallback(); | 392 _runPendingImmediateCallback(); |
386 } | 393 } |
387 } | 394 } |
388 } finally { | 395 } finally { |
389 _handlingCallbacks = false; | 396 _handlingCallbacks = false; |
390 // Notify the event handler or shutdown the port if no more pending | |
391 // timers are present. | |
392 _notifyEventHandler(); | |
393 } | 397 } |
394 } | 398 } |
395 | 399 |
396 // Creates a receive port and registers an empty handler on that port. Just | 400 |
397 // the triggering of the event loop will ensure that timers are executed. | |
398 static void _handleMessage(msg) { | 401 static void _handleMessage(msg) { |
399 var pendingTimers; | 402 var pendingTimers; |
400 if (msg == _ZERO_EVENT) { | 403 if (msg == _ZERO_EVENT) { |
401 pendingTimers = _queueFromZeroEvent(); | 404 pendingTimers = _queueFromZeroEvent(); |
402 assert(pendingTimers.length > 0); | 405 assert(pendingTimers.length > 0); |
403 } else { | 406 } else { |
404 assert(msg == _TIMEOUT_EVENT); | 407 assert(msg == _TIMEOUT_EVENT); |
| 408 _scheduledWakeupTime = null; // Consumed the last scheduled wakeup now. |
405 pendingTimers = _queueFromTimeoutEvent(); | 409 pendingTimers = _queueFromTimeoutEvent(); |
406 } | 410 } |
407 _runTimers(pendingTimers); | 411 _runTimers(pendingTimers); |
| 412 // Notify the event handler or shutdown the port if no more pending |
| 413 // timers are present. |
| 414 _notifyEventHandler(); |
408 } | 415 } |
409 | 416 |
| 417 |
| 418 // Tell the event handler to wake this isolate at a specific time. |
| 419 static void _scheduleWakeup(int wakeupTime) { |
| 420 if (_sendPort == null) { |
| 421 _createTimerHandler(); |
| 422 } |
| 423 VMLibraryHooks.eventHandlerSendData(null, _sendPort, wakeupTime); |
| 424 _scheduledWakeupTime = wakeupTime; |
| 425 } |
| 426 |
| 427 |
| 428 // Cancel pending wakeups in the event handler. |
| 429 static void _cancelWakeup() { |
| 430 assert(_sendPort != null); |
| 431 VMLibraryHooks.eventHandlerSendData(null, _sendPort, _NO_TIMER); |
| 432 _scheduledWakeupTime = null; |
| 433 } |
| 434 |
| 435 |
| 436 // Create a receive port and register a message handler for the timer |
| 437 // events. |
410 static void _createTimerHandler() { | 438 static void _createTimerHandler() { |
411 assert(_receivePort == null); | 439 assert(_receivePort == null); |
412 assert(_sendPort == null); | 440 assert(_sendPort == null); |
413 _receivePort = new RawReceivePort(_handleMessage); | 441 _receivePort = new RawReceivePort(_handleMessage); |
414 _sendPort = _receivePort.sendPort; | 442 _sendPort = _receivePort.sendPort; |
415 _scheduledWakeupTime = null; | 443 _scheduledWakeupTime = null; |
416 } | 444 } |
417 | 445 |
| 446 |
418 static void _shutdownTimerHandler() { | 447 static void _shutdownTimerHandler() { |
419 _receivePort.close(); | 448 _receivePort.close(); |
420 _receivePort = null; | 449 _receivePort = null; |
421 _sendPort = null; | 450 _sendPort = null; |
422 _scheduledWakeupTime = null; | 451 _scheduledWakeupTime = null; |
423 } | 452 } |
424 | 453 |
| 454 |
425 // The Timer factory registered with the dart:async library by the embedder. | 455 // The Timer factory registered with the dart:async library by the embedder. |
426 static Timer _factory(int milliSeconds, | 456 static Timer _factory(int milliSeconds, |
427 void callback(Timer timer), | 457 void callback(Timer timer), |
428 bool repeating) { | 458 bool repeating) { |
429 if (repeating) { | 459 if (repeating) { |
430 return new _Timer.periodic(milliSeconds, callback); | 460 return new _Timer.periodic(milliSeconds, callback); |
431 } | 461 } |
432 return new _Timer(milliSeconds, callback); | 462 return new _Timer(milliSeconds, callback); |
433 } | 463 } |
434 } | 464 } |
435 | 465 |
| 466 |
436 _setupHooks() { | 467 _setupHooks() { |
437 VMLibraryHooks.timerFactory = _Timer._factory; | 468 VMLibraryHooks.timerFactory = _Timer._factory; |
438 } | 469 } |
OLD | NEW |