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