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 |
|
Søren Gjesse
2013/12/12 08:11:52
Please provide a short description of the data str
Anders Johnsen
2013/12/13 13:47:54
Done.
| |
| 7 class _Timer extends LinkedListEntry<_Timer> implements Timer { | 7 class _TimerHeap { |
| 8 List<_Timer> _list; | |
| 9 int _used = 0; | |
| 10 | |
| 11 _TimerHeap([int initSize = 32]) | |
|
Lasse Reichstein Nielsen
2013/12/12 09:57:19
How about starting with 31 (or any other power-of-
Anders Johnsen
2013/12/13 13:47:54
Done.
| |
| 12 : _list = new List<_Timer>(initSize); | |
| 13 | |
| 14 bool get isEmpty => _used == 0; | |
| 15 bool get isNotEmpty => _used > 0; | |
| 16 | |
| 17 _Timer get first => _list[0]; | |
| 18 | |
| 19 bool isFirst(_Timer timer) => timer._index == 0; | |
| 20 | |
| 21 void add(_Timer timer) { | |
| 22 if (_used == _list.length) { | |
| 23 _resize(); | |
| 24 } | |
| 25 timer._index = _used++; | |
| 26 _list[timer._index] = timer; | |
| 27 _bubbleUp(timer); | |
| 28 } | |
| 29 | |
| 30 _Timer removeFirst() { | |
| 31 var f = first; | |
| 32 remove(f); | |
| 33 return f; | |
| 34 } | |
| 35 | |
| 36 void remove(_Timer timer) { | |
| 37 _used--; | |
| 38 if (isEmpty) return; | |
| 39 var last = _list[_used]; | |
|
Søren Gjesse
2013/12/12 08:11:52
Maybe set _list[_used] to null even though it is n
Lasse Reichstein Nielsen
2013/12/12 09:57:19
Absolutely. We don't want to keep old timers alive
Anders Johnsen
2013/12/13 13:47:54
Done.
Anders Johnsen
2013/12/13 13:47:54
Done.
| |
| 40 _swap(timer, last); | |
|
Lasse Reichstein Nielsen
2013/12/12 09:57:19
I'd prefer keeping 'last' in a variable until we h
Anders Johnsen
2013/12/13 13:47:54
Done.
| |
| 41 _bubbleUp(last); | |
|
Lasse Reichstein Nielsen
2013/12/12 09:57:19
If it actually bubbles anywhere by doing bubbleUp,
Anders Johnsen
2013/12/13 13:47:54
Done.
| |
| 42 _bubbleDown(last); | |
| 43 } | |
| 44 | |
| 45 void _resize() { | |
| 46 var newList = new List(_list.length * 2); | |
| 47 for (int i = 0; i < _used; i++) { | |
| 48 newList[i] = _list[i]; | |
| 49 } | |
|
Lasse Reichstein Nielsen
2013/12/12 09:57:19
Consider using newList.setRange(0, _used, _list).
Anders Johnsen
2013/12/13 13:47:54
Done.
| |
| 50 _list = newList; | |
| 51 } | |
| 52 | |
| 53 void _bubbleUp(_Timer timer) { | |
| 54 while (!isFirst(timer)) { | |
| 55 Timer parent = _parent(timer); | |
| 56 if (timer._compareTo(parent) < 0) { | |
| 57 _swap(timer, parent); | |
| 58 } else { | |
| 59 break; | |
| 60 } | |
| 61 } | |
| 62 } | |
| 63 | |
| 64 void _bubbleDown(_Timer timer) { | |
| 65 while (true) { | |
| 66 int leftIndex = _leftChildIndex(timer._index); | |
| 67 int rightIndex = _rightChildIndex(timer._index); | |
| 68 _Timer newest = timer; | |
| 69 if (leftIndex < _used && _list[leftIndex]._compareTo(newest) < 0) { | |
| 70 newest = _list[leftIndex]; | |
| 71 } | |
| 72 if (rightIndex < _used && _list[rightIndex]._compareTo(newest) < 0) { | |
| 73 newest = _list[rightIndex]; | |
| 74 } | |
| 75 if (newest == timer) { | |
| 76 // We are where we should be, break. | |
| 77 break; | |
| 78 } | |
| 79 _swap(newest, timer); | |
| 80 } | |
| 81 } | |
| 82 | |
| 83 void _swap(_Timer first, _Timer second) { | |
| 84 int tmp = first._index; | |
| 85 first._index = second._index; | |
| 86 second._index = tmp; | |
| 87 _list[first._index] = first; | |
| 88 _list[second._index] = second; | |
| 89 } | |
| 90 | |
| 91 String toString() { | |
|
Lasse Reichstein Nielsen
2013/12/12 09:57:19
Remove this. It's for debugging purposes only, I a
Anders Johnsen
2013/12/13 13:47:54
Done.
| |
| 92 return "($_used, ${_list.sublist(0, _used)})"; | |
| 93 } | |
| 94 | |
| 95 Timer _parent(_Timer timer) => _list[_parentIndex(timer._index)]; | |
| 96 Timer _leftChild(_Timer timer) => _list[_leftChildIndex(timer._index)]; | |
| 97 Timer _rightChild(_Timer timer) => _list[_rightChildIndex(timer._index)]; | |
| 98 | |
| 99 static int _parentIndex(int index) => (index - 1) ~/ 2; | |
| 100 static int _leftChildIndex(int index) => 2 * index + 1; | |
| 101 static int _rightChildIndex(int index) => 2 * index + 2; | |
| 102 } | |
| 103 | |
| 104 class _Timer implements Timer { | |
| 8 // Disables the timer. | 105 // Disables the timer. |
| 9 static const int _NO_TIMER = -1; | 106 static const int _NO_TIMER = -1; |
| 10 | 107 |
| 11 // Timers are ordered by wakeup time. | 108 // Timers are ordered by wakeup time. |
| 12 static LinkedList<_Timer> _timers = new LinkedList<_Timer>(); | 109 static _TimerHeap _heap = new _TimerHeap(); |
| 110 static _Timer _firstZeroTimer; | |
| 111 static _Timer _lastZeroTimer; | |
| 112 static int _idCount = 0; | |
| 13 | 113 |
| 14 static RawReceivePort _receivePort; | 114 static RawReceivePort _receivePort; |
| 15 static bool _handling_callbacks = false; | 115 static bool _handling_callbacks = false; |
|
Lasse Reichstein Nielsen
2013/12/12 09:57:19
-> _handlingCallbacks
Anders Johnsen
2013/12/13 13:47:54
Done.
| |
| 16 | 116 |
| 17 Function _callback; | 117 Function _callback; |
| 18 int _milliSeconds; | 118 int _milliSeconds; |
| 19 int _wakeupTime = 0; | 119 int _wakeupTime = 0; |
| 120 _Timer _next; | |
|
Lasse Reichstein Nielsen
2013/12/12 09:57:19
Consider letting this field and _index use the sam
Anders Johnsen
2013/12/13 13:47:54
Done.
| |
| 121 int _index = -1; | |
| 122 int _id = -1; | |
| 123 | |
| 124 String toString() => "($_wakeupTime, $_index, $_id)"; | |
| 20 | 125 |
| 21 static Timer _createTimer(void callback(Timer timer), | 126 static Timer _createTimer(void callback(Timer timer), |
| 22 int milliSeconds, | 127 int milliSeconds, |
| 23 bool repeating) { | 128 bool repeating) { |
| 24 _Timer timer = new _Timer._internal(); | 129 _Timer timer = new _Timer._internal(); |
| 25 timer._callback = callback; | 130 timer._callback = callback; |
| 26 if (milliSeconds > 0) { | 131 if (milliSeconds > 0) { |
| 27 // Add one because DateTime.now() is assumed to round down | 132 // Add one because DateTime.now() is assumed to round down |
| 28 // to nearest millisecond, not up, so that time + duration is before | 133 // to nearest millisecond, not up, so that time + duration is before |
| 29 // duration milliseconds from now. Using micosecond timers like | 134 // duration milliseconds from now. Using micosecond timers like |
| 30 // Stopwatch allows detecting that the timer fires early. | 135 // Stopwatch allows detecting that the timer fires early. |
| 31 timer._wakeupTime = | 136 timer._wakeupTime = |
| 32 new DateTime.now().millisecondsSinceEpoch + 1 + milliSeconds; | 137 new DateTime.now().millisecondsSinceEpoch + 1 + milliSeconds; |
| 33 } | 138 } |
| 34 timer._milliSeconds = repeating ? milliSeconds : -1; | 139 timer._milliSeconds = repeating ? milliSeconds : -1; |
| 35 timer._addTimerToList(); | 140 if (timer._addTimerToHeap()) { |
| 36 if (identical(timer, _timers.first)) { | |
| 37 // The new timer is the first in queue. Update event handler. | 141 // The new timer is the first in queue. Update event handler. |
| 38 timer._notifyEventHandler(); | 142 timer._notifyEventHandler(); |
| 39 } | 143 } |
| 40 return timer; | 144 return timer; |
| 41 } | 145 } |
| 42 | 146 |
| 43 factory _Timer(int milliSeconds, void callback(Timer timer)) { | 147 factory _Timer(int milliSeconds, void callback(Timer timer)) { |
| 44 return _createTimer(callback, milliSeconds, false); | 148 return _createTimer(callback, milliSeconds, false); |
| 45 } | 149 } |
| 46 | 150 |
| 47 factory _Timer.periodic(int milliSeconds, void callback(Timer timer)) { | 151 factory _Timer.periodic(int milliSeconds, void callback(Timer timer)) { |
| 48 return _createTimer(callback, milliSeconds, true); | 152 return _createTimer(callback, milliSeconds, true); |
| 49 } | 153 } |
| 50 | 154 |
| 51 _Timer._internal() {} | 155 _Timer._internal() {} |
| 52 | 156 |
| 53 void _clear() { | 157 void _clear() { |
| 54 _callback = null; | 158 _callback = null; |
| 55 _milliSeconds = 0; | 159 _milliSeconds = 0; |
|
Lasse Reichstein Nielsen
2013/12/12 09:57:19
Why clear milliSeconds?
Is the number used for an
Anders Johnsen
2013/12/13 13:47:54
Done.
| |
| 56 _wakeupTime = 0; | 160 } |
| 161 | |
| 162 int _compareTo(_Timer other) { | |
| 163 int c = _wakeupTime - other._wakeupTime; | |
| 164 if (c != 0) return c; | |
| 165 return _id - other._id; | |
| 57 } | 166 } |
| 58 | 167 |
| 59 bool get _repeating => _milliSeconds >= 0; | 168 bool get _repeating => _milliSeconds >= 0; |
| 60 | 169 |
| 61 bool get isActive => _callback != null; | 170 bool get isActive => _callback != null; |
| 62 | 171 |
| 63 // Cancels a set timer. The timer is removed from the timer list and if | 172 // Cancels a set timer. The timer is removed from the timer list and if |
| 64 // the given timer is the earliest timer the native timer is reset. | 173 // the given timer is the earliest timer the native timer is reset. |
| 65 void cancel() { | 174 void cancel() { |
| 175 if (_index == -1) { | |
|
Lasse Reichstein Nielsen
2013/12/12 09:57:19
Add comment: // Is in the zero-timer queue.
Or add
Anders Johnsen
2013/12/13 13:47:54
Exactly :)
| |
| 176 _clear(); | |
| 177 return; | |
| 178 } | |
| 66 _clear(); | 179 _clear(); |
|
Lasse Reichstein Nielsen
2013/12/12 09:57:19
You clear in both cases, so move the _clear() befo
Anders Johnsen
2013/12/13 13:47:54
Done.
| |
| 67 // Return if already canceled. | 180 // Return if already canceled. |
| 68 if (list == null) return; | 181 bool update = _firstZeroTimer == null; |
| 69 assert(!_timers.isEmpty); | 182 if (_wakeupTime != 0 && _index != -1) { |
|
Lasse Reichstein Nielsen
2013/12/12 09:57:19
How can _index be -1 here? That case is special-ca
Anders Johnsen
2013/12/13 13:47:54
No, this was due to several iterations. I've clean
| |
| 70 _Timer first = _timers.first; | 183 update = update && _heap.isFirst(this); |
| 71 unlink(); | 184 _heap.remove(this); |
| 72 if (identical(first, this)) { | 185 _index = -1; |
| 186 } | |
| 187 if (update) { | |
| 73 _notifyEventHandler(); | 188 _notifyEventHandler(); |
| 74 } | 189 } |
| 75 } | 190 } |
| 76 | 191 |
| 77 void _advanceWakeupTime() { | 192 void _advanceWakeupTime() { |
| 78 assert(_milliSeconds >= 0); | 193 assert(_milliSeconds >= 0); |
| 79 _wakeupTime += _milliSeconds; | 194 _wakeupTime += _milliSeconds; |
| 80 } | 195 } |
| 81 | 196 |
| 82 // Adds a timer to the timer list. Timers with the same wakeup time are | 197 // Adds a timer to the timer list. Timers with the same wakeup time are |
| 83 // enqueued in order and notified in FIFO order. | 198 // enqueued in order and notified in FIFO order. |
| 84 void _addTimerToList() { | 199 bool _addTimerToHeap() { |
| 85 _Timer entry = _timers.isEmpty ? null : _timers.first; | 200 if (_wakeupTime == 0) { |
|
Lasse Reichstein Nielsen
2013/12/12 09:57:19
I would *love* if any timers in the heap that are
Anders Johnsen
2013/12/13 13:47:54
As discussed offline, will look into this later on
| |
| 86 // If timer is last, add to end. | 201 if (_firstZeroTimer == null) { |
| 87 if (entry == null || _timers.last._wakeupTime <= _wakeupTime) { | 202 _lastZeroTimer = _firstZeroTimer = this; |
| 88 _timers.add(this); | 203 return true; |
| 89 return; | 204 } else { |
| 205 _lastZeroTimer = _lastZeroTimer._next = this; | |
| 206 return false; | |
| 207 } | |
| 208 } else { | |
| 209 _id = _idCount++; | |
| 210 _heap.add(this); | |
| 211 return _firstZeroTimer == null && _heap.isFirst(this); | |
| 90 } | 212 } |
| 91 // Otherwise scan through and find the right position. | |
| 92 while (entry != null) { | |
| 93 if (_wakeupTime < entry._wakeupTime) { | |
| 94 entry.insertBefore(this); | |
| 95 return; | |
| 96 } | |
| 97 entry = entry.next; | |
| 98 } | |
| 99 _timers.add(this); | |
| 100 } | 213 } |
| 101 | 214 |
| 102 | 215 |
| 103 void _notifyEventHandler() { | 216 void _notifyEventHandler() { |
| 104 if (_handling_callbacks) { | 217 if (_handling_callbacks) { |
| 105 // While we are already handling callbacks we will not notify the event | 218 // While we are already handling callbacks we will not notify the event |
| 106 // handler. _handleTimeout will call _notifyEventHandler once all pending | 219 // handler. _handleTimeout will call _notifyEventHandler once all pending |
| 107 // timers are processed. | 220 // timers are processed. |
| 108 return; | 221 return; |
| 109 } | 222 } |
| 110 | 223 |
| 111 if (_timers.isEmpty) { | 224 if (_firstZeroTimer == null && _heap.isEmpty) { |
| 112 // No pending timers: Close the receive port and let the event handler | 225 // No pending timers: Close the receive port and let the event handler |
| 113 // know. | 226 // know. |
| 114 if (_receivePort != null) { | 227 if (_receivePort != null) { |
| 115 _EventHandler._sendData(null, _receivePort, _NO_TIMER); | 228 _EventHandler._sendData(null, _receivePort, _NO_TIMER); |
| 116 _shutdownTimerHandler(); | 229 _shutdownTimerHandler(); |
| 117 } | 230 } |
| 118 } else { | 231 } else { |
| 119 if (_receivePort == null) { | 232 if (_receivePort == null) { |
| 120 // Create a receive port and register a message handler for the timer | 233 // Create a receive port and register a message handler for the timer |
| 121 // events. | 234 // events. |
| 122 _createTimerHandler(); | 235 _createTimerHandler(); |
| 123 } | 236 } |
| 124 _EventHandler._sendData(null, | 237 _EventHandler._sendData(null, |
| 125 _receivePort, | 238 _receivePort, |
| 126 _timers.first._wakeupTime); | 239 _firstZeroTimer != null ? |
| 240 0 : _heap.first._wakeupTime); | |
| 127 } | 241 } |
| 128 } | 242 } |
| 129 | 243 |
| 130 | 244 |
| 131 // Creates a receive port and registers the timer handler on that | 245 // Creates a receive port and registers the timer handler on that |
| 132 // receive port. | 246 // receive port. |
| 133 void _createTimerHandler() { | 247 void _createTimerHandler() { |
| 134 | 248 |
| 135 void _handleTimeout() { | 249 void _handleTimeout() { |
|
Lasse Reichstein Nielsen
2013/12/12 09:57:19
Consider making _handleTimeout an instance method
Anders Johnsen
2013/12/13 13:47:54
Done.
| |
| 136 int currentTime = new DateTime.now().millisecondsSinceEpoch; | 250 int currentTime = new DateTime.now().millisecondsSinceEpoch; |
| 137 | 251 |
| 138 // Collect all pending timers. | 252 // Collect all pending timers. |
| 139 var pending_timers = new List(); | 253 var timer = _firstZeroTimer; |
| 140 while (!_timers.isEmpty) { | 254 var nextTimer = _lastZeroTimer; |
| 141 _Timer entry = _timers.first; | 255 _firstZeroTimer = _lastZeroTimer = null; |
| 142 if (entry._wakeupTime <= currentTime) { | 256 while (_heap.isNotEmpty && _heap.first._wakeupTime <= currentTime) { |
| 143 entry.unlink(); | 257 var next = _heap.removeFirst(); |
|
Lasse Reichstein Nielsen
2013/12/12 09:57:19
Consider making removeFirst set the index to -1.
T
Anders Johnsen
2013/12/13 13:47:54
Done.
| |
| 144 pending_timers.add(entry); | 258 next._index = -1; |
| 259 if (timer == null) { | |
| 260 nextTimer = timer = next; | |
| 145 } else { | 261 } else { |
| 146 break; | 262 nextTimer = nextTimer._next = next; |
| 147 } | 263 } |
| 148 } | 264 } |
| 149 | 265 |
| 150 // Trigger all of the pending timers. New timers added as part of the | 266 // Trigger all of the pending timers. New timers added as part of the |
| 151 // callbacks will be enqueued now and notified in the next spin at the | 267 // callbacks will be enqueued now and notified in the next spin at the |
| 152 // earliest. | 268 // earliest. |
| 153 _handling_callbacks = true; | 269 _handling_callbacks = true; |
| 154 try { | 270 try { |
| 155 for (var timer in pending_timers) { | 271 while (timer != null) { |
| 272 var next = timer._next; | |
| 273 timer._next = null; | |
| 156 // One of the timers in the pending_timers list can cancel | 274 // One of the timers in the pending_timers list can cancel |
| 157 // one of the later timers which will set the callback to | 275 // one of the later timers which will set the callback to |
| 158 // null. | 276 // null. |
| 159 if (timer._callback != null) { | 277 if (timer._callback != null) { |
| 160 var callback = timer._callback; | 278 var callback = timer._callback; |
| 161 if (!timer._repeating) { | 279 if (!timer._repeating) { |
| 162 //Mark timer as inactive. | 280 //Mark timer as inactive. |
|
Lasse Reichstein Nielsen
2013/12/12 09:57:19
space between "//" and "M" :)
Anders Johnsen
2013/12/13 13:47:54
Done.
| |
| 163 timer._callback = null; | 281 timer._callback = null; |
| 164 } | 282 } |
| 165 callback(timer); | 283 callback(timer); |
| 166 // Re-insert repeating timer if not canceled. | 284 // Re-insert repeating timer if not canceled. |
| 167 if (timer._repeating && timer._callback != null) { | 285 if (timer._repeating && timer._callback != null) { |
| 168 timer._advanceWakeupTime(); | 286 timer._advanceWakeupTime(); |
| 169 timer._addTimerToList(); | 287 timer._addTimerToHeap(); |
| 170 } | 288 } |
| 171 } | 289 } |
| 290 timer = next; | |
| 172 } | 291 } |
| 173 } finally { | 292 } finally { |
| 174 _handling_callbacks = false; | 293 _handling_callbacks = false; |
| 175 _notifyEventHandler(); | 294 _notifyEventHandler(); |
| 176 } | 295 } |
| 177 } | 296 } |
| 178 | 297 |
| 179 if(_receivePort == null) { | 298 if(_receivePort == null) { |
| 180 _receivePort = new RawReceivePort((_) { _handleTimeout(); }); | 299 _receivePort = new RawReceivePort((_) { _handleTimeout(); }); |
|
Lasse Reichstein Nielsen
2013/12/12 09:57:19
Avoid a wrapper closure by giving _handleTimeout t
Anders Johnsen
2013/12/13 13:47:54
Done.
| |
| 181 } | 300 } |
| 182 } | 301 } |
| 183 | 302 |
| 184 void _shutdownTimerHandler() { | 303 void _shutdownTimerHandler() { |
| 185 _receivePort.close(); | 304 _receivePort.close(); |
| 186 _receivePort = null; | 305 _receivePort = null; |
| 187 } | 306 } |
| 188 } | 307 } |
| 189 | 308 |
| 190 // Provide a closure which will allocate a Timer object to be able to hook | 309 // Provide a closure which will allocate a Timer object to be able to hook |
| 191 // up the Timer interface in dart:isolate with the implementation here. | 310 // up the Timer interface in dart:isolate with the implementation here. |
| 192 _getTimerFactoryClosure() { | 311 _getTimerFactoryClosure() { |
| 193 return (int milliSeconds, void callback(Timer timer), bool repeating) { | 312 return (int milliSeconds, void callback(Timer timer), bool repeating) { |
| 194 if (repeating) { | 313 if (repeating) { |
| 195 return new _Timer.periodic(milliSeconds, callback); | 314 return new _Timer.periodic(milliSeconds, callback); |
| 196 } | 315 } |
| 197 return new _Timer(milliSeconds, callback); | 316 return new _Timer(milliSeconds, callback); |
| 198 }; | 317 }; |
| 199 } | 318 } |
| 200 | 319 |
| 201 | 320 |
| OLD | NEW |