| 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.io; | |
| 6 | |
| 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)) | |
| 9 // `add`. | |
| 10 // | |
| 11 // To ensure the timers are ordered by insertion time, the _Timer class has a | |
| 12 // `_id` field set when added to the heap. | |
| 13 // | |
| 14 // [0] http://en.wikipedia.org/wiki/Binary_heap | |
| 15 class _TimerHeap { | |
| 16 List<_Timer> _list; | |
| 17 int _used = 0; | |
| 18 | |
| 19 _TimerHeap([int initSize = 7]) | |
| 20 : _list = new List<_Timer>(initSize); | |
| 21 | |
| 22 bool get isEmpty => _used == 0; | |
| 23 | |
| 24 _Timer get first => _list[0]; | |
| 25 | |
| 26 bool isFirst(_Timer timer) => timer._indexOrNext == 0; | |
| 27 | |
| 28 void add(_Timer timer) { | |
| 29 if (_used == _list.length) { | |
| 30 _resize(); | |
| 31 } | |
| 32 timer._indexOrNext = _used++; | |
| 33 _list[timer._indexOrNext] = timer; | |
| 34 _bubbleUp(timer); | |
| 35 } | |
| 36 | |
| 37 _Timer removeFirst() { | |
| 38 var f = first; | |
| 39 remove(f); | |
| 40 return f; | |
| 41 } | |
| 42 | |
| 43 void remove(_Timer timer) { | |
| 44 _used--; | |
| 45 if (isEmpty) { | |
| 46 _list[0] = null; | |
| 47 timer._indexOrNext = null; | |
| 48 return; | |
| 49 } | |
| 50 var last = _list[_used]; | |
| 51 if (!identical(last, timer)) { | |
| 52 last._indexOrNext = timer._indexOrNext; | |
| 53 _list[last._indexOrNext] = last; | |
| 54 if (last._compareTo(timer) < 0) { | |
| 55 _bubbleUp(last); | |
| 56 } else { | |
| 57 _bubbleDown(last); | |
| 58 } | |
| 59 } | |
| 60 _list[_used] = null; | |
| 61 timer._indexOrNext = null; | |
| 62 } | |
| 63 | |
| 64 void _resize() { | |
| 65 var newList = new List(_list.length * 2 + 1); | |
| 66 newList.setRange(0, _used, _list); | |
| 67 _list = newList; | |
| 68 } | |
| 69 | |
| 70 void _bubbleUp(_Timer timer) { | |
| 71 while (!isFirst(timer)) { | |
| 72 Timer parent = _parent(timer); | |
| 73 if (timer._compareTo(parent) < 0) { | |
| 74 _swap(timer, parent); | |
| 75 } else { | |
| 76 break; | |
| 77 } | |
| 78 } | |
| 79 } | |
| 80 | |
| 81 void _bubbleDown(_Timer timer) { | |
| 82 while (true) { | |
| 83 int leftIndex = _leftChildIndex(timer._indexOrNext); | |
| 84 int rightIndex = _rightChildIndex(timer._indexOrNext); | |
| 85 _Timer newest = timer; | |
| 86 if (leftIndex < _used && _list[leftIndex]._compareTo(newest) < 0) { | |
| 87 newest = _list[leftIndex]; | |
| 88 } | |
| 89 if (rightIndex < _used && _list[rightIndex]._compareTo(newest) < 0) { | |
| 90 newest = _list[rightIndex]; | |
| 91 } | |
| 92 if (identical(newest, timer)) { | |
| 93 // We are where we should be, break. | |
| 94 break; | |
| 95 } | |
| 96 _swap(newest, timer); | |
| 97 } | |
| 98 } | |
| 99 | |
| 100 void _swap(_Timer first, _Timer second) { | |
| 101 int tmp = first._indexOrNext; | |
| 102 first._indexOrNext = second._indexOrNext; | |
| 103 second._indexOrNext = tmp; | |
| 104 _list[first._indexOrNext] = first; | |
| 105 _list[second._indexOrNext] = second; | |
| 106 } | |
| 107 | |
| 108 Timer _parent(_Timer timer) => _list[_parentIndex(timer._indexOrNext)]; | |
| 109 Timer _leftChild(_Timer timer) => _list[_leftChildIndex(timer._indexOrNext)]; | |
| 110 Timer _rightChild(_Timer timer) => | |
| 111 _list[_rightChildIndex(timer._indexOrNext)]; | |
| 112 | |
| 113 static int _parentIndex(int index) => (index - 1) ~/ 2; | |
| 114 static int _leftChildIndex(int index) => 2 * index + 1; | |
| 115 static int _rightChildIndex(int index) => 2 * index + 2; | |
| 116 } | |
| 117 | |
| 118 class _Timer implements Timer { | |
| 119 // Cancels the timer in the event handler. | |
| 120 static const int _NO_TIMER = -1; | |
| 121 | |
| 122 // Timers are ordered by wakeup time. | |
| 123 static _TimerHeap _heap = new _TimerHeap(); | |
| 124 static _Timer _firstZeroTimer; | |
| 125 static _Timer _lastZeroTimer; | |
| 126 | |
| 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. | |
| 129 static int _ID_MASK = 0x1fffffff; | |
| 130 static int _idCount = 0; | |
| 131 | |
| 132 static RawReceivePort _receivePort; | |
| 133 static SendPort _sendPort; | |
| 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 | |
| 139 static bool _handlingCallbacks = false; | |
| 140 | |
| 141 Function _callback; // Closure to call when timer fires. null if canceled. | |
| 142 int _wakeupTime; // Expiration time. | |
| 143 int _milliSeconds; // Duration specified at creation. | |
| 144 bool _repeating; // Indicates periodic timers. | |
| 145 var _indexOrNext; // Index if part of the TimerHeap, link otherwise. | |
| 146 int _id; // Incrementing id to enable sorting of timers with same expiry. | |
| 147 | |
| 148 // Get the next available id. We accept collisions and reordering when the | |
| 149 // _idCount overflows and the timers expire at the same millisecond. | |
| 150 static int _nextId() { | |
| 151 var result = _idCount; | |
| 152 _idCount = (_idCount + 1) & _ID_MASK; | |
| 153 return result; | |
| 154 } | |
| 155 | |
| 156 _Timer._internal(this._callback, | |
| 157 this._wakeupTime, | |
| 158 this._milliSeconds, | |
| 159 this._repeating) : _id = _nextId(); | |
| 160 | |
| 161 static Timer _createTimer(void callback(Timer timer), | |
| 162 int milliSeconds, | |
| 163 bool repeating) { | |
| 164 // Negative timeouts are treated as if 0 timeout. | |
| 165 if (milliSeconds < 0) { | |
| 166 milliSeconds = 0; | |
| 167 } | |
| 168 // Add one because DateTime.now() is assumed to round down | |
| 169 // to nearest millisecond, not up, so that time + duration is before | |
| 170 // duration milliseconds from now. Using microsecond timers like | |
| 171 // Stopwatch allows detecting that the timer fires early. | |
| 172 int now = new DateTime.now().millisecondsSinceEpoch; | |
| 173 int wakeupTime = (milliSeconds == 0) ? now : (now + 1 + milliSeconds); | |
| 174 | |
| 175 _Timer timer = new _Timer._internal(callback, | |
| 176 wakeupTime, | |
| 177 milliSeconds, | |
| 178 repeating); | |
| 179 | |
| 180 if (timer._addTimerToHeap()) { | |
| 181 // The new timer is the first in queue. Update event handler. | |
| 182 _notifyEventHandler(); | |
| 183 } | |
| 184 return timer; | |
| 185 } | |
| 186 | |
| 187 factory _Timer(int milliSeconds, void callback(Timer timer)) { | |
| 188 return _createTimer(callback, milliSeconds, false); | |
| 189 } | |
| 190 | |
| 191 factory _Timer.periodic(int milliSeconds, void callback(Timer timer)) { | |
| 192 return _createTimer(callback, milliSeconds, true); | |
| 193 } | |
| 194 | |
| 195 bool get _isInHeap => _indexOrNext is int; | |
| 196 | |
| 197 void _clear() { | |
| 198 _callback = null; | |
| 199 } | |
| 200 | |
| 201 int _compareTo(_Timer other) { | |
| 202 int c = _wakeupTime - other._wakeupTime; | |
| 203 if (c != 0) return c; | |
| 204 return _id - other._id; | |
| 205 } | |
| 206 | |
| 207 bool get isActive => _callback != null; | |
| 208 | |
| 209 // Cancels a set timer. The timer is removed from the timer list and if | |
| 210 // the given timer is the earliest timer the event handler is notified. | |
| 211 void cancel() { | |
| 212 _clear(); | |
| 213 if (!_isInHeap) return; | |
| 214 // Only heap timers are really removed. Others are just dropped on | |
| 215 // notification. | |
| 216 bool update = (_firstZeroTimer == null) && _heap.isFirst(this); | |
| 217 _heap.remove(this); | |
| 218 if (update) { | |
| 219 _notifyEventHandler(); | |
| 220 } | |
| 221 } | |
| 222 | |
| 223 void _advanceWakeupTime() { | |
| 224 // Recalculate the next wakeup time. For repeating timers with a 0 timeout | |
| 225 // the next wakeup time is now. | |
| 226 _id = _nextId(); | |
| 227 if (_milliSeconds > 0) { | |
| 228 _wakeupTime += _milliSeconds; | |
| 229 } else { | |
| 230 _wakeupTime = new DateTime.now().millisecondsSinceEpoch; | |
| 231 } | |
| 232 } | |
| 233 | |
| 234 // Adds a timer to the heap or timer list. Timers with the same wakeup time | |
| 235 // are enqueued in order and notified in FIFO order. | |
| 236 bool _addTimerToHeap() { | |
| 237 if (_milliSeconds == 0) { | |
| 238 if (_firstZeroTimer == null) { | |
| 239 _lastZeroTimer = this; | |
| 240 _firstZeroTimer = this; | |
| 241 return true; | |
| 242 } else { | |
| 243 _lastZeroTimer._indexOrNext = this; | |
| 244 _lastZeroTimer = this; | |
| 245 return false; | |
| 246 } | |
| 247 } else { | |
| 248 _heap.add(this); | |
| 249 return _firstZeroTimer == null && _heap.isFirst(this); | |
| 250 } | |
| 251 } | |
| 252 | |
| 253 | |
| 254 static void _notifyEventHandler() { | |
| 255 if (_handlingCallbacks) { | |
| 256 // While we are already handling callbacks we will not notify the event | |
| 257 // handler. _handleTimeout will call _notifyEventHandler once all pending | |
| 258 // timers are processed. | |
| 259 return; | |
| 260 } | |
| 261 | |
| 262 if ((_firstZeroTimer == null) && _heap.isEmpty) { | |
| 263 // No pending timers: Close the receive port and let the event handler | |
| 264 // know. | |
| 265 if (_receivePort != null) { | |
| 266 _EventHandler._sendData(null, _sendPort, _NO_TIMER); | |
| 267 _shutdownTimerHandler(); | |
| 268 } | |
| 269 } else { | |
| 270 if (_receivePort == null) { | |
| 271 // Create a receive port and register a message handler for the timer | |
| 272 // events. | |
| 273 _createTimerHandler(); | |
| 274 } | |
| 275 if (_firstZeroTimer != null) { | |
| 276 if (!_messagePending) { | |
| 277 _sendPort.send(null); | |
| 278 _messagePending = true; // Reset when the port receives a message. | |
| 279 } | |
| 280 } else { | |
| 281 var wakeupTime = _heap.first._wakeupTime; | |
| 282 if ((_scheduledWakeupTime == null) || | |
| 283 (wakeupTime != _scheduledWakeupTime)) { | |
| 284 _EventHandler._sendData(null, _sendPort, wakeupTime); | |
| 285 _scheduledWakeupTime = wakeupTime; | |
| 286 } | |
| 287 } | |
| 288 } | |
| 289 } | |
| 290 | |
| 291 static void _handleTimeout(pendingImmediateCallback) { | |
| 292 // Fast exit if no timers have been scheduled. | |
| 293 if (_heap.isEmpty && (_firstZeroTimer == null)) { | |
| 294 assert(_receivePort == null); | |
| 295 return; | |
| 296 } | |
| 297 | |
| 298 // Collect all pending timers. | |
| 299 var head = null; | |
| 300 var tail = null; | |
| 301 if (_heap.isEmpty) { | |
| 302 // Only immediate timers are scheduled. Take over the whole list as is. | |
| 303 assert(_firstZeroTimer != null); | |
| 304 assert(_lastZeroTimer != null); | |
| 305 head = _firstZeroTimer; | |
| 306 tail = _lastZeroTimer; | |
| 307 _firstZeroTimer = null; | |
| 308 _lastZeroTimer = null; | |
| 309 } else { | |
| 310 assert(!_heap.isEmpty); | |
| 311 // Keep track of the lowest wakeup times for both the list and heap. If | |
| 312 // the respective queue is empty move its time beyond the current time. | |
| 313 var currentTime = new DateTime.now().millisecondsSinceEpoch; | |
| 314 var heapTime = _heap.first._wakeupTime; | |
| 315 var listTime = (_firstZeroTimer == null) ? | |
| 316 (currentTime + 1) : _firstZeroTimer._wakeupTime; | |
| 317 | |
| 318 while ((heapTime <= currentTime) || (listTime <= currentTime)) { | |
| 319 var timer; | |
| 320 // Consume the timers in order by removing from heap or list based on | |
| 321 // their wakeup time and update the queue's time. | |
| 322 assert((heapTime != listTime) || | |
| 323 ((_heap.first != null) && (_firstZeroTimer != null))); | |
| 324 if ((heapTime < listTime) || | |
| 325 ((heapTime == listTime) && | |
| 326 (_heap.first._id < _firstZeroTimer._id))) { | |
| 327 timer = _heap.removeFirst(); | |
| 328 heapTime = _heap.isEmpty ? | |
| 329 (currentTime + 1) : _heap.first._wakeupTime; | |
| 330 } else { | |
| 331 timer = _firstZeroTimer; | |
| 332 assert(timer._milliSeconds == 0); | |
| 333 _firstZeroTimer = timer._indexOrNext; | |
| 334 if (_firstZeroTimer == null) { | |
| 335 _lastZeroTimer = null; | |
| 336 listTime = currentTime + 1; | |
| 337 } else { | |
| 338 // We want to drain all entries from the list as they should have | |
| 339 // been pending for 0 ms. To prevent issues with current time moving | |
| 340 // we ensure that the listTime does not go beyond current, unless | |
| 341 // the list is empty. | |
| 342 listTime = _firstZeroTimer._wakeupTime; | |
| 343 if (listTime > currentTime) { | |
| 344 listTime = currentTime; | |
| 345 } | |
| 346 } | |
| 347 } | |
| 348 | |
| 349 // Append this timer to the pending timer list. | |
| 350 timer._indexOrNext = null; | |
| 351 if (head == null) { | |
| 352 assert(tail == null); | |
| 353 head = timer; | |
| 354 tail = timer; | |
| 355 } else { | |
| 356 tail._indexOrNext = timer; | |
| 357 tail = timer; | |
| 358 } | |
| 359 } | |
| 360 } | |
| 361 | |
| 362 // No timers queued: Early exit. | |
| 363 if (head == null) { | |
| 364 return; | |
| 365 } | |
| 366 | |
| 367 // If there are no pending timers currently reset the id space before we | |
| 368 // have a chance to enqueue new timers. | |
| 369 assert(_firstZeroTimer == null); | |
| 370 if (_heap.isEmpty) { | |
| 371 _idCount = 0; | |
| 372 } | |
| 373 | |
| 374 // Trigger all of the pending timers. New timers added as part of the | |
| 375 // callbacks will be enqueued now and notified in the next spin at the | |
| 376 // earliest. | |
| 377 _handlingCallbacks = true; | |
| 378 try { | |
| 379 while (head != null) { | |
| 380 // Dequeue the first candidate timer. | |
| 381 var timer = head; | |
| 382 head = timer._indexOrNext; | |
| 383 timer._indexOrNext = null; | |
| 384 | |
| 385 // One of the timers in the pending_timers list can cancel | |
| 386 // one of the later timers which will set the callback to | |
| 387 // null. | |
| 388 if (timer._callback != null) { | |
| 389 var callback = timer._callback; | |
| 390 if (!timer._repeating) { | |
| 391 // Mark timer as inactive. | |
| 392 timer._callback = null; | |
| 393 } | |
| 394 callback(timer); | |
| 395 // Re-insert repeating timer if not canceled. | |
| 396 if (timer._repeating && (timer._callback != null)) { | |
| 397 timer._advanceWakeupTime(); | |
| 398 timer._addTimerToHeap(); | |
| 399 } | |
| 400 // Execute pending micro tasks. | |
| 401 pendingImmediateCallback(); | |
| 402 } | |
| 403 } | |
| 404 } finally { | |
| 405 _handlingCallbacks = false; | |
| 406 _notifyEventHandler(); | |
| 407 } | |
| 408 } | |
| 409 | |
| 410 // Creates a receive port and registers an empty handler on that port. Just | |
| 411 // the triggering of the event loop will ensure that timers are executed. | |
| 412 static _ignoreMessage(_) { | |
| 413 _messagePending = false; | |
| 414 } | |
| 415 | |
| 416 static void _createTimerHandler() { | |
| 417 assert(_receivePort == null); | |
| 418 _receivePort = new RawReceivePort(_ignoreMessage); | |
| 419 _sendPort = _receivePort.sendPort; | |
| 420 _scheduledWakeupTime = null; | |
| 421 _messagePending = false; | |
| 422 } | |
| 423 | |
| 424 static void _shutdownTimerHandler() { | |
| 425 _receivePort.close(); | |
| 426 _receivePort = null; | |
| 427 _sendPort = null; | |
| 428 _scheduledWakeupTime = null; | |
| 429 _messagePending = false; | |
| 430 } | |
| 431 | |
| 432 // The Timer factory registered with the dart:async library by the embedder. | |
| 433 static Timer _factory(int milliSeconds, | |
| 434 void callback(Timer timer), | |
| 435 bool repeating) { | |
| 436 if (repeating) { | |
| 437 return new _Timer.periodic(milliSeconds, callback); | |
| 438 } | |
| 439 return new _Timer(milliSeconds, callback); | |
| 440 } | |
| 441 } | |
| OLD | NEW |