| 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 |