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

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
« no previous file with comments | « runtime/lib/isolate_patch.dart ('k') | tests/lib/mirrors/invocation_fuzz_test.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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;
134 static int _scheduledWakeupTime;
132 static bool _handlingCallbacks = false; 135 static bool _handlingCallbacks = false;
133 136
134 Function _callback; 137 Function _callback; // Closure to call when timer fires. null if canceled.
135 int _milliSeconds; 138 int _wakeupTime; // Expiration time.
136 int _wakeupTime = 0; 139 int _milliSeconds; // Duration specified at creation.
137 var _indexOrNext; 140 bool _repeating; // Indicates periodic timers.
138 int _id = -1; 141 var _indexOrNext; // Index if part of the TimerHeap, link otherwise.
142 int _id; // Incrementing id to enable sorting of timers with same expiry.
143
144 // Get the next available id. We accept collisions and reordering when the
145 // _idCount overflows and the timers expire at the same millisecond.
146 static int _nextId() {
147 var result = _idCount;
148 _idCount = (_idCount + 1) & _ID_MASK;
149 return result;
150 }
151
152 _Timer._internal(this._callback,
153 this._wakeupTime,
154 this._milliSeconds,
155 this._repeating) : _id = _nextId();
139 156
140 static Timer _createTimer(void callback(Timer timer), 157 static Timer _createTimer(void callback(Timer timer),
141 int milliSeconds, 158 int milliSeconds,
142 bool repeating) { 159 bool repeating) {
143 _Timer timer = new _Timer._internal(); 160 // Negative timeouts are treated as if 0 timeout.
144 timer._callback = callback; 161 if (milliSeconds < 0) {
145 if (milliSeconds > 0) { 162 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 } 163 }
153 timer._milliSeconds = repeating ? milliSeconds : -1; 164 // Add one because DateTime.now() is assumed to round down
165 // to nearest millisecond, not up, so that time + duration is before
166 // duration milliseconds from now. Using microsecond timers like
167 // Stopwatch allows detecting that the timer fires early.
168 int now = new DateTime.now().millisecondsSinceEpoch;
169 int wakeupTime = (milliSeconds == 0) ? now : (now + 1 + milliSeconds);
170
171 _Timer timer = new _Timer._internal(callback,
172 wakeupTime,
173 milliSeconds,
174 repeating);
175
154 if (timer._addTimerToHeap()) { 176 if (timer._addTimerToHeap()) {
155 // The new timer is the first in queue. Update event handler. 177 // The new timer is the first in queue. Update event handler.
156 _notifyEventHandler(); 178 _notifyEventHandler();
157 } 179 }
158 return timer; 180 return timer;
159 } 181 }
160 182
161 factory _Timer(int milliSeconds, void callback(Timer timer)) { 183 factory _Timer(int milliSeconds, void callback(Timer timer)) {
162 return _createTimer(callback, milliSeconds, false); 184 return _createTimer(callback, milliSeconds, false);
163 } 185 }
164 186
165 factory _Timer.periodic(int milliSeconds, void callback(Timer timer)) { 187 factory _Timer.periodic(int milliSeconds, void callback(Timer timer)) {
166 return _createTimer(callback, milliSeconds, true); 188 return _createTimer(callback, milliSeconds, true);
167 } 189 }
168 190
169 _Timer._internal() {} 191 bool get _isInHeap => _indexOrNext is int;
170
171 bool get _isInHeap => _id >= 0;
172 192
173 void _clear() { 193 void _clear() {
174 _callback = null; 194 _callback = null;
175 } 195 }
176 196
177 int _compareTo(_Timer other) { 197 int _compareTo(_Timer other) {
178 int c = _wakeupTime - other._wakeupTime; 198 int c = _wakeupTime - other._wakeupTime;
179 if (c != 0) return c; 199 if (c != 0) return c;
180 return _id - other._id; 200 return _id - other._id;
181 } 201 }
182 202
183 bool get _repeating => _milliSeconds >= 0;
184
185 bool get isActive => _callback != null; 203 bool get isActive => _callback != null;
186 204
187 // Cancels a set timer. The timer is removed from the timer list and if 205 // 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. 206 // the given timer is the earliest timer the event handler is notified.
189 void cancel() { 207 void cancel() {
190 _clear(); 208 _clear();
191 if (!_isInHeap) return; 209 if (!_isInHeap) return;
192 assert(_wakeupTime != 0); 210 // Only heap timers are really removed. Others are just dropped on
211 // notification.
193 bool update = (_firstZeroTimer == null) && _heap.isFirst(this); 212 bool update = (_firstZeroTimer == null) && _heap.isFirst(this);
194 _heap.remove(this); 213 _heap.remove(this);
195 if (update) { 214 if (update) {
196 _notifyEventHandler(); 215 _notifyEventHandler();
197 } 216 }
198 } 217 }
199 218
200 void _advanceWakeupTime() { 219 void _advanceWakeupTime() {
201 assert(_milliSeconds >= 0); 220 // Recalculate the next wakeup time. For repeating timers with a 0 timeout
202 _wakeupTime += _milliSeconds; 221 // the next wakeup time is now.
222 _id = _nextId();
223 if (_milliSeconds > 0) {
224 _wakeupTime += _milliSeconds;
225 } else {
226 _wakeupTime = new DateTime.now().millisecondsSinceEpoch;
227 }
203 } 228 }
204 229
205 // Adds a timer to the timer list. Timers with the same wakeup time are 230 // Adds a timer to the heap or timer list. Timers with the same wakeup time
206 // enqueued in order and notified in FIFO order. 231 // are enqueued in order and notified in FIFO order.
207 bool _addTimerToHeap() { 232 bool _addTimerToHeap() {
208 if (_wakeupTime == 0) { 233 if (_milliSeconds == 0) {
209 if (_firstZeroTimer == null) { 234 if (_firstZeroTimer == null) {
210 _lastZeroTimer = this; 235 _lastZeroTimer = this;
211 _firstZeroTimer = this; 236 _firstZeroTimer = this;
212 return true; 237 return true;
213 } else { 238 } else {
214 _lastZeroTimer._indexOrNext = this; 239 _lastZeroTimer._indexOrNext = this;
215 _lastZeroTimer = this; 240 _lastZeroTimer = this;
216 return false; 241 return false;
217 } 242 }
218 } else { 243 } else {
219 _id = _idCount++;
220 _heap.add(this); 244 _heap.add(this);
221 return _firstZeroTimer == null && _heap.isFirst(this); 245 return _firstZeroTimer == null && _heap.isFirst(this);
222 } 246 }
223 } 247 }
224 248
225 249
226 static void _notifyEventHandler() { 250 static void _notifyEventHandler() {
227 if (_handlingCallbacks) { 251 if (_handlingCallbacks) {
228 // While we are already handling callbacks we will not notify the event 252 // While we are already handling callbacks we will not notify the event
229 // handler. _handleTimeout will call _notifyEventHandler once all pending 253 // handler. _handleTimeout will call _notifyEventHandler once all pending
(...skipping 10 matching lines...) Expand all
240 } 264 }
241 } else { 265 } else {
242 if (_receivePort == null) { 266 if (_receivePort == null) {
243 // Create a receive port and register a message handler for the timer 267 // Create a receive port and register a message handler for the timer
244 // events. 268 // events.
245 _createTimerHandler(); 269 _createTimerHandler();
246 } 270 }
247 if (_firstZeroTimer != null) { 271 if (_firstZeroTimer != null) {
248 _sendPort.send(null); 272 _sendPort.send(null);
249 } else { 273 } else {
250 _EventHandler._sendData(null, 274 var wakeupTime = _heap.first._wakeupTime;
251 _sendPort, 275 if ((_scheduledWakeupTime == null) ||
252 _heap.first._wakeupTime); 276 (wakeupTime != _scheduledWakeupTime)) {
277 _EventHandler._sendData(null, _sendPort, wakeupTime);
278 _scheduledWakeupTime = wakeupTime;
279 }
253 } 280 }
254 } 281 }
255 } 282 }
256 283
257 static void _handleTimeout(_) { 284 static void _handleTimeout(pendingImmediateCallback) {
258 int currentTime = new DateTime.now().millisecondsSinceEpoch; 285 int currentTime = new DateTime.now().millisecondsSinceEpoch;
259 // Collect all pending timers. 286 // Collect all pending timers.
260 var timer = _firstZeroTimer; 287 var head = null;
261 var nextTimer = _lastZeroTimer; 288 var tail = null;
262 _firstZeroTimer = null; 289 // Keep track of the lowest wakeup times for both the list and heap. If
263 _lastZeroTimer = null; 290 // the respective queue is empty move its time beyond the current time.
264 while (_heap.isNotEmpty && _heap.first._wakeupTime <= currentTime) { 291 var heapTime = _heap.isEmpty ?
265 var next = _heap.removeFirst(); 292 (currentTime + 1) : _heap.first._wakeupTime;
266 if (timer == null) { 293 var listTime = (_firstZeroTimer == null) ?
267 nextTimer = next; 294 (currentTime + 1) : _firstZeroTimer._wakeupTime;
268 timer = next; 295
296 while ((heapTime <= currentTime) || (listTime <= currentTime)) {
297 var timer;
298 // Consume the timers in order by removing from heap or list based on
299 // their wakeup time and update the queue's time.
300 assert((heapTime != listTime) ||
301 ((_heap.first != null) && (_firstZeroTimer != null)));
302 if ((heapTime < listTime) ||
303 ((heapTime == listTime) &&
304 (_heap.first._id < _firstZeroTimer._id))) {
305 timer = _heap.removeFirst();
306 heapTime = _heap.isEmpty ? (currentTime + 1) : _heap.first._wakeupTime;
269 } else { 307 } else {
270 nextTimer._indexOrNext = next; 308 timer = _firstZeroTimer;
271 nextTimer = next; 309 assert(timer._milliSeconds == 0);
310 _firstZeroTimer = timer._indexOrNext;
311 if (_firstZeroTimer == null) {
312 _lastZeroTimer = null;
313 listTime = currentTime + 1;
314 } else {
315 // We want to drain all entries from the list as they should have
316 // been pending for 0 ms. To prevent issues with current time moving
317 // we ensure that the listTime does not go beyond current, unless the
318 // list is empty.
319 listTime = _firstZeroTimer._wakeupTime;
320 if (listTime > currentTime) {
321 listTime = currentTime;
322 }
323 }
272 } 324 }
325
326 // Append this timer to the pending timer list.
327 timer._indexOrNext = null;
328 if (head == null) {
329 assert(tail == null);
330 head = timer;
331 tail = timer;
332 } else {
333 tail._indexOrNext = timer;
334 tail = timer;
335 }
336 }
337
338 // No timers queued: Early exit.
339 if (head == null) {
340 return;
341 }
342
343 // If there are no pending timers currently reset the id space before we
344 // have a chance to enqueue new timers.
345 assert(_firstZeroTimer == null);
346 if (_heap.isEmpty) {
347 _idCount = 0;
273 } 348 }
274 349
275 // Trigger all of the pending timers. New timers added as part of the 350 // 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 351 // callbacks will be enqueued now and notified in the next spin at the
277 // earliest. 352 // earliest.
278 _handlingCallbacks = true; 353 _handlingCallbacks = true;
279 try { 354 try {
280 while (timer != null) { 355 while (head != null) {
281 var next = timer._indexOrNext; 356 // Dequeue the first candidate timer.
357 var timer = head;
358 head = timer._indexOrNext;
282 timer._indexOrNext = null; 359 timer._indexOrNext = null;
360
283 // One of the timers in the pending_timers list can cancel 361 // One of the timers in the pending_timers list can cancel
284 // one of the later timers which will set the callback to 362 // one of the later timers which will set the callback to
285 // null. 363 // null.
286 if (timer._callback != null) { 364 if (timer._callback != null) {
287 var callback = timer._callback; 365 var callback = timer._callback;
288 if (!timer._repeating) { 366 if (!timer._repeating) {
289 // Mark timer as inactive. 367 // Mark timer as inactive.
290 timer._callback = null; 368 timer._callback = null;
291 } 369 }
292 callback(timer); 370 callback(timer);
293 // Re-insert repeating timer if not canceled. 371 // Re-insert repeating timer if not canceled.
294 if (timer._repeating && timer._callback != null) { 372 if (timer._repeating && (timer._callback != null)) {
295 timer._advanceWakeupTime(); 373 timer._advanceWakeupTime();
296 timer._addTimerToHeap(); 374 timer._addTimerToHeap();
297 } 375 }
376 // Execute pending micro tasks.
377 pendingImmediateCallback();
298 } 378 }
299 timer = next;
300 } 379 }
301 } finally { 380 } finally {
302 _handlingCallbacks = false; 381 _handlingCallbacks = false;
303 _notifyEventHandler(); 382 _notifyEventHandler();
304 } 383 }
305 } 384 }
306 385
307 // Creates a receive port and registers the timer handler on that 386 // Creates a receive port and registers an empty handler on that port. Just
308 // receive port. 387 // the triggering of the event loop will ensure that timers are executed.
388 static _ignoreMessage(_) => null;
389
309 static void _createTimerHandler() { 390 static void _createTimerHandler() {
310 if(_receivePort == null) { 391 assert(_receivePort == null);
311 _receivePort = new RawReceivePort(_handleTimeout); 392 _receivePort = new RawReceivePort(_ignoreMessage);
312 _sendPort = _receivePort.sendPort; 393 _sendPort = _receivePort.sendPort;
313 } 394 _scheduledWakeupTime = null;
314 } 395 }
315 396
316 static void _shutdownTimerHandler() { 397 static void _shutdownTimerHandler() {
317 _receivePort.close(); 398 _receivePort.close();
318 _receivePort = null; 399 _receivePort = null;
319 _sendPort = null; 400 _sendPort = null;
401 _scheduledWakeupTime = null;
402 }
403
404 // The Timer factory registered with the dart:async library by the embedder.
405 static Timer _factory(int milliSeconds,
406 void callback(Timer timer),
407 bool repeating) {
408 if (repeating) {
409 return new _Timer.periodic(milliSeconds, callback);
410 }
411 return new _Timer(milliSeconds, callback);
320 } 412 }
321 } 413 }
322 414
323 // Provide a closure which will allocate a Timer object to be able to hook 415 // 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. 416 // up the Timer interface in dart:isolate with the implementation here.
325 _getTimerFactoryClosure() { 417 _getTimerFactoryClosure() {
326 return (int milliSeconds, void callback(Timer timer), bool repeating) { 418 runTimerClosure = _Timer._handleTimeout;
327 if (repeating) { 419 return _Timer._factory;
328 return new _Timer.periodic(milliSeconds, callback);
329 }
330 return new _Timer(milliSeconds, callback);
331 };
332 } 420 }
333
334
OLDNEW
« no previous file with comments | « runtime/lib/isolate_patch.dart ('k') | tests/lib/mirrors/invocation_fuzz_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698