Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(149)

Side by Side Diff: sdk/lib/io/timer_impl.dart

Issue 825403003: Fix http://dartbug.com/21834 (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 5 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698