Chromium Code Reviews| 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 part of dart.io; | 5 part of dart.io; |
| 6 | 6 |
| 7 // Timer heap implemented as a array-based binary heap[0]. | 7 // Timer heap implemented as a array-based binary heap[0]. |
| 8 // This allows for O(1) `first`, O(log(n)) `remove`/`removeFirst` and O(log(n)) | 8 // This allows for O(1) `first`, O(log(n)) `remove`/`removeFirst` and O(log(n)) |
| 9 // `add`. | 9 // `add`. |
| 10 // | 10 // |
| (...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 125 static _Timer _lastZeroTimer; | 125 static _Timer _lastZeroTimer; |
| 126 | 126 |
| 127 // We use an id to be able to sort timers with the same expiration time. | 127 // We use an id to be able to sort timers with the same expiration time. |
| 128 // ids are recycled after ID_MASK enqueues or when the timer queue is empty. | 128 // ids are recycled after ID_MASK enqueues or when the timer queue is empty. |
| 129 static int _ID_MASK = 0x1fffffff; | 129 static int _ID_MASK = 0x1fffffff; |
| 130 static int _idCount = 0; | 130 static int _idCount = 0; |
| 131 | 131 |
| 132 static RawReceivePort _receivePort; | 132 static RawReceivePort _receivePort; |
| 133 static SendPort _sendPort; | 133 static SendPort _sendPort; |
| 134 static int _scheduledWakeupTime; | 134 static int _scheduledWakeupTime; |
| 135 // Keep track whether at least one message is pending in the event loop. This | |
| 136 // way we do not have to notify for every pending _firstZeroTimer. | |
| 137 static var _messagePending = false; | |
| 138 | |
| 135 static bool _handlingCallbacks = false; | 139 static bool _handlingCallbacks = false; |
| 136 | 140 |
| 137 Function _callback; // Closure to call when timer fires. null if canceled. | 141 Function _callback; // Closure to call when timer fires. null if canceled. |
| 138 int _wakeupTime; // Expiration time. | 142 int _wakeupTime; // Expiration time. |
| 139 int _milliSeconds; // Duration specified at creation. | 143 int _milliSeconds; // Duration specified at creation. |
| 140 bool _repeating; // Indicates periodic timers. | 144 bool _repeating; // Indicates periodic timers. |
| 141 var _indexOrNext; // Index if part of the TimerHeap, link otherwise. | 145 var _indexOrNext; // Index if part of the TimerHeap, link otherwise. |
| 142 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. |
| 143 | 147 |
| 144 // Get the next available id. We accept collisions and reordering when the | 148 // Get the next available id. We accept collisions and reordering when the |
| (...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 248 | 252 |
| 249 | 253 |
| 250 static void _notifyEventHandler() { | 254 static void _notifyEventHandler() { |
| 251 if (_handlingCallbacks) { | 255 if (_handlingCallbacks) { |
| 252 // While we are already handling callbacks we will not notify the event | 256 // While we are already handling callbacks we will not notify the event |
| 253 // handler. _handleTimeout will call _notifyEventHandler once all pending | 257 // handler. _handleTimeout will call _notifyEventHandler once all pending |
| 254 // timers are processed. | 258 // timers are processed. |
| 255 return; | 259 return; |
| 256 } | 260 } |
| 257 | 261 |
| 258 if (_firstZeroTimer == null && _heap.isEmpty) { | 262 if ((_firstZeroTimer == null) && _heap.isEmpty) { |
| 259 // No pending timers: Close the receive port and let the event handler | 263 // No pending timers: Close the receive port and let the event handler |
| 260 // know. | 264 // know. |
| 261 if (_receivePort != null) { | 265 if (_receivePort != null) { |
| 262 _EventHandler._sendData(null, _sendPort, _NO_TIMER); | 266 _EventHandler._sendData(null, _sendPort, _NO_TIMER); |
| 263 _shutdownTimerHandler(); | 267 _shutdownTimerHandler(); |
| 264 } | 268 } |
| 265 } else { | 269 } else { |
| 266 if (_receivePort == null) { | 270 if (_receivePort == null) { |
| 267 // Create a receive port and register a message handler for the timer | 271 // Create a receive port and register a message handler for the timer |
| 268 // events. | 272 // events. |
| 269 _createTimerHandler(); | 273 _createTimerHandler(); |
| 270 } | 274 } |
| 271 if (_firstZeroTimer != null) { | 275 if (_firstZeroTimer != null) { |
| 272 _sendPort.send(null); | 276 if (!_messagePending) { |
| 277 _sendPort.send(null); | |
| 278 _messagePending = true; // Reset when the port receives a message. | |
| 279 } | |
| 273 } else { | 280 } else { |
| 274 var wakeupTime = _heap.first._wakeupTime; | 281 var wakeupTime = _heap.first._wakeupTime; |
| 275 if ((_scheduledWakeupTime == null) || | 282 if ((_scheduledWakeupTime == null) || |
| 276 (wakeupTime != _scheduledWakeupTime)) { | 283 (wakeupTime != _scheduledWakeupTime)) { |
| 277 _EventHandler._sendData(null, _sendPort, wakeupTime); | 284 _EventHandler._sendData(null, _sendPort, wakeupTime); |
| 278 _scheduledWakeupTime = wakeupTime; | 285 _scheduledWakeupTime = wakeupTime; |
| 279 } | 286 } |
| 280 } | 287 } |
| 281 } | 288 } |
| 282 } | 289 } |
| 283 | 290 |
| 284 static void _handleTimeout(pendingImmediateCallback) { | 291 static void _handleTimeout(pendingImmediateCallback) { |
| 285 int currentTime = new DateTime.now().millisecondsSinceEpoch; | 292 // Fast exit if no timers have been scheduled. |
| 293 if (_heap.isEmpty && (_firstZeroTimer == null)) { | |
|
Søren Gjesse
2015/01/20 15:36:27
maybe assert(_receivePort == null)
Ivan Posva
2015/01/20 16:01:17
Done.
| |
| 294 return; | |
| 295 } | |
| 296 | |
| 286 // Collect all pending timers. | 297 // Collect all pending timers. |
| 287 var head = null; | 298 var head = null; |
| 288 var tail = null; | 299 var tail = null; |
| 289 // Keep track of the lowest wakeup times for both the list and heap. If | 300 if (_heap.isEmpty) { |
| 290 // the respective queue is empty move its time beyond the current time. | 301 // Only immediate timers are scheduled. Take over the whole list as is. |
| 291 var heapTime = _heap.isEmpty ? | 302 assert(_firstZeroTimer != null); |
| 292 (currentTime + 1) : _heap.first._wakeupTime; | 303 assert(_lastZeroTimer != null); |
| 293 var listTime = (_firstZeroTimer == null) ? | 304 head = _firstZeroTimer; |
| 294 (currentTime + 1) : _firstZeroTimer._wakeupTime; | 305 tail = _lastZeroTimer; |
| 306 _firstZeroTimer = null; | |
| 307 _lastZeroTimer = null; | |
| 308 } else { | |
| 309 assert(!_heap.isEmpty); | |
|
zra
2015/01/20 15:23:34
Why is this assert needed?
Ivan Posva
2015/01/20 16:01:17
It is more of a documentation and making sure the
| |
| 310 // Keep track of the lowest wakeup times for both the list and heap. If | |
| 311 // the respective queue is empty move its time beyond the current time. | |
| 312 var currentTime = new DateTime.now().millisecondsSinceEpoch; | |
| 313 var heapTime = _heap.first._wakeupTime; | |
| 314 var listTime = (_firstZeroTimer == null) ? | |
| 315 (currentTime + 1) : _firstZeroTimer._wakeupTime; | |
|
zra
2015/01/20 15:23:34
Indentation
Ivan Posva
2015/01/20 16:01:17
Done.
| |
| 295 | 316 |
| 296 while ((heapTime <= currentTime) || (listTime <= currentTime)) { | 317 while ((heapTime <= currentTime) || (listTime <= currentTime)) { |
| 297 var timer; | 318 var timer; |
| 298 // Consume the timers in order by removing from heap or list based on | 319 // Consume the timers in order by removing from heap or list based on |
| 299 // their wakeup time and update the queue's time. | 320 // their wakeup time and update the queue's time. |
| 300 assert((heapTime != listTime) || | 321 assert((heapTime != listTime) || |
| 301 ((_heap.first != null) && (_firstZeroTimer != null))); | 322 ((_heap.first != null) && (_firstZeroTimer != null))); |
| 302 if ((heapTime < listTime) || | 323 if ((heapTime < listTime) || |
| 303 ((heapTime == listTime) && | 324 ((heapTime == listTime) && |
| 304 (_heap.first._id < _firstZeroTimer._id))) { | 325 (_heap.first._id < _firstZeroTimer._id))) { |
| 305 timer = _heap.removeFirst(); | 326 timer = _heap.removeFirst(); |
| 306 heapTime = _heap.isEmpty ? (currentTime + 1) : _heap.first._wakeupTime; | 327 heapTime = _heap.isEmpty ? (currentTime + 1) : _heap.first._wakeupTime ; |
|
zra
2015/01/20 15:23:34
Long line
Ivan Posva
2015/01/20 16:01:17
Done.
| |
| 307 } else { | |
| 308 timer = _firstZeroTimer; | |
| 309 assert(timer._milliSeconds == 0); | |
| 310 _firstZeroTimer = timer._indexOrNext; | |
| 311 if (_firstZeroTimer == null) { | |
| 312 _lastZeroTimer = null; | |
| 313 listTime = currentTime + 1; | |
| 314 } else { | 328 } else { |
| 315 // We want to drain all entries from the list as they should have | 329 timer = _firstZeroTimer; |
| 316 // been pending for 0 ms. To prevent issues with current time moving | 330 assert(timer._milliSeconds == 0); |
| 317 // we ensure that the listTime does not go beyond current, unless the | 331 _firstZeroTimer = timer._indexOrNext; |
| 318 // list is empty. | 332 if (_firstZeroTimer == null) { |
| 319 listTime = _firstZeroTimer._wakeupTime; | 333 _lastZeroTimer = null; |
| 320 if (listTime > currentTime) { | 334 listTime = currentTime + 1; |
| 321 listTime = currentTime; | 335 } else { |
| 336 // We want to drain all entries from the list as they should have | |
| 337 // been pending for 0 ms. To prevent issues with current time moving | |
| 338 // we ensure that the listTime does not go beyond current, unless | |
| 339 // the list is empty. | |
| 340 listTime = _firstZeroTimer._wakeupTime; | |
| 341 if (listTime > currentTime) { | |
| 342 listTime = currentTime; | |
| 343 } | |
| 322 } | 344 } |
| 323 } | 345 } |
| 324 } | |
| 325 | 346 |
| 326 // Append this timer to the pending timer list. | 347 // Append this timer to the pending timer list. |
| 327 timer._indexOrNext = null; | 348 timer._indexOrNext = null; |
| 328 if (head == null) { | 349 if (head == null) { |
| 329 assert(tail == null); | 350 assert(tail == null); |
| 330 head = timer; | 351 head = timer; |
| 331 tail = timer; | 352 tail = timer; |
| 332 } else { | 353 } else { |
| 333 tail._indexOrNext = timer; | 354 tail._indexOrNext = timer; |
| 334 tail = timer; | 355 tail = timer; |
| 356 } | |
| 335 } | 357 } |
| 336 } | 358 } |
| 337 | 359 |
| 338 // No timers queued: Early exit. | 360 // No timers queued: Early exit. |
| 339 if (head == null) { | 361 if (head == null) { |
| 340 return; | 362 return; |
| 341 } | 363 } |
| 342 | 364 |
| 343 // If there are no pending timers currently reset the id space before we | 365 // If there are no pending timers currently reset the id space before we |
| 344 // have a chance to enqueue new timers. | 366 // have a chance to enqueue new timers. |
| (...skipping 17 matching lines...) Expand all Loading... | |
| 362 // one of the later timers which will set the callback to | 384 // one of the later timers which will set the callback to |
| 363 // null. | 385 // null. |
| 364 if (timer._callback != null) { | 386 if (timer._callback != null) { |
| 365 var callback = timer._callback; | 387 var callback = timer._callback; |
| 366 if (!timer._repeating) { | 388 if (!timer._repeating) { |
| 367 // Mark timer as inactive. | 389 // Mark timer as inactive. |
| 368 timer._callback = null; | 390 timer._callback = null; |
| 369 } | 391 } |
| 370 callback(timer); | 392 callback(timer); |
| 371 // Re-insert repeating timer if not canceled. | 393 // Re-insert repeating timer if not canceled. |
| 372 if (timer._repeating && (timer._callback != null)) { | 394 if (timer._repeating && (timer._callback != null)) { |
|
Søren Gjesse
2015/01/20 15:36:26
I know this is not part of the change, but reading
Ivan Posva
2015/01/20 16:01:17
I understand what you mean, but this is as if you
| |
| 373 timer._advanceWakeupTime(); | 395 timer._advanceWakeupTime(); |
| 374 timer._addTimerToHeap(); | 396 timer._addTimerToHeap(); |
| 375 } | 397 } |
| 376 // Execute pending micro tasks. | 398 // Execute pending micro tasks. |
| 377 pendingImmediateCallback(); | 399 pendingImmediateCallback(); |
| 378 } | 400 } |
| 379 } | 401 } |
| 380 } finally { | 402 } finally { |
| 381 _handlingCallbacks = false; | 403 _handlingCallbacks = false; |
| 382 _notifyEventHandler(); | 404 _notifyEventHandler(); |
| 383 } | 405 } |
| 384 } | 406 } |
| 385 | 407 |
| 386 // Creates a receive port and registers an empty handler on that port. Just | 408 // Creates a receive port and registers an empty handler on that port. Just |
| 387 // the triggering of the event loop will ensure that timers are executed. | 409 // the triggering of the event loop will ensure that timers are executed. |
| 388 static _ignoreMessage(_) => null; | 410 static _ignoreMessage(_) { |
| 411 _messagePending = false; | |
| 412 } | |
| 389 | 413 |
| 390 static void _createTimerHandler() { | 414 static void _createTimerHandler() { |
| 391 assert(_receivePort == null); | 415 assert(_receivePort == null); |
| 392 _receivePort = new RawReceivePort(_ignoreMessage); | 416 _receivePort = new RawReceivePort(_ignoreMessage); |
| 393 _sendPort = _receivePort.sendPort; | 417 _sendPort = _receivePort.sendPort; |
| 394 _scheduledWakeupTime = null; | 418 _scheduledWakeupTime = null; |
| 419 _messagePending = false; | |
| 395 } | 420 } |
| 396 | 421 |
| 397 static void _shutdownTimerHandler() { | 422 static void _shutdownTimerHandler() { |
| 398 _receivePort.close(); | 423 _receivePort.close(); |
| 399 _receivePort = null; | 424 _receivePort = null; |
| 400 _sendPort = null; | 425 _sendPort = null; |
| 401 _scheduledWakeupTime = null; | 426 _scheduledWakeupTime = null; |
| 427 _messagePending = false; | |
| 402 } | 428 } |
| 403 | 429 |
| 404 // The Timer factory registered with the dart:async library by the embedder. | 430 // The Timer factory registered with the dart:async library by the embedder. |
| 405 static Timer _factory(int milliSeconds, | 431 static Timer _factory(int milliSeconds, |
| 406 void callback(Timer timer), | 432 void callback(Timer timer), |
| 407 bool repeating) { | 433 bool repeating) { |
| 408 if (repeating) { | 434 if (repeating) { |
| 409 return new _Timer.periodic(milliSeconds, callback); | 435 return new _Timer.periodic(milliSeconds, callback); |
| 410 } | 436 } |
| 411 return new _Timer(milliSeconds, callback); | 437 return new _Timer(milliSeconds, callback); |
| 412 } | 438 } |
| 413 } | 439 } |
| OLD | NEW |