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

Side by Side Diff: runtime/lib/timer_impl.dart

Issue 971193002: - Make sure we schedule another wakeup when handling a timeout (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 5 years, 9 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 | « no previous file | tests/lib/async/timer_regress22626_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 // Timer heap implemented as a array-based binary heap[0]. 5 // Timer heap implemented as a array-based binary heap[0].
6 // This allows for O(1) `first`, O(log(n)) `remove`/`removeFirst` and O(log(n)) 6 // This allows for O(1) `first`, O(log(n)) `remove`/`removeFirst` and O(log(n))
7 // `add`. 7 // `add`.
8 // 8 //
9 // To ensure the timers are ordered by insertion time, the _Timer class has a 9 // To ensure the timers are ordered by insertion time, the _Timer class has a
10 // `_id` field set when added to the heap. 10 // `_id` field set when added to the heap.
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after
108 Timer _rightChild(_Timer timer) => 108 Timer _rightChild(_Timer timer) =>
109 _list[_rightChildIndex(timer._indexOrNext)]; 109 _list[_rightChildIndex(timer._indexOrNext)];
110 110
111 static int _parentIndex(int index) => (index - 1) ~/ 2; 111 static int _parentIndex(int index) => (index - 1) ~/ 2;
112 static int _leftChildIndex(int index) => 2 * index + 1; 112 static int _leftChildIndex(int index) => 2 * index + 1;
113 static int _rightChildIndex(int index) => 2 * index + 2; 113 static int _rightChildIndex(int index) => 2 * index + 2;
114 } 114 }
115 115
116 class _Timer implements Timer { 116 class _Timer implements Timer {
117 // Cancels the timer in the event handler. 117 // Cancels the timer in the event handler.
118 static const int _NO_TIMER = -1; 118 static const _NO_TIMER = -1;
119 119
120 // We distinguish what kind of message arrived based on the value being sent. 120 // We distinguish what kind of message arrived based on the value being sent.
121 static const _ZERO_EVENT = 1; 121 static const _ZERO_EVENT = 1;
122 static const _TIMEOUT_EVENT = null; 122 static const _TIMEOUT_EVENT = null;
123 123
124 // Timers are ordered by wakeup time. Timers with a timeout value of > 0 do 124 // Timers are ordered by wakeup time. Timers with a timeout value of > 0 do
125 // end up on the TimerHeap. Timers with a timeout of 0 are queued in a list. 125 // end up on the TimerHeap. Timers with a timeout of 0 are queued in a list.
126 static _TimerHeap _heap = new _TimerHeap(); 126 static _TimerHeap _heap = new _TimerHeap();
127 static _Timer _firstZeroTimer; 127 static _Timer _firstZeroTimer;
128 static _Timer _lastZeroTimer; 128 static _Timer _lastZeroTimer;
129 129
130 // We use an id to be able to sort timers with the same expiration time. 130 // We use an id to be able to sort timers with the same expiration time.
131 // ids are recycled after ID_MASK enqueues or when the timer queue is empty. 131 // ids are recycled after ID_MASK enqueues or when the timer queue is empty.
132 static int _ID_MASK = 0x1fffffff; 132 static const _ID_MASK = 0x1fffffff;
133 static int _idCount = 0; 133 static int _idCount = 0;
134 134
135 static RawReceivePort _receivePort; 135 static RawReceivePort _receivePort;
136 static SendPort _sendPort; 136 static SendPort _sendPort;
137 static int _scheduledWakeupTime; 137 static int _scheduledWakeupTime;
138 138
139 static bool _handlingCallbacks = false; 139 static bool _handlingCallbacks = false;
140 140
141 Function _callback; // Closure to call when timer fires. null if canceled. 141 Function _callback; // Closure to call when timer fires. null if canceled.
142 int _wakeupTime; // Expiration time. 142 int _wakeupTime; // Expiration time.
143 int _milliSeconds; // Duration specified at creation. 143 final int _milliSeconds; // Duration specified at creation.
144 bool _repeating; // Indicates periodic timers. 144 final bool _repeating; // Indicates periodic timers.
145 var _indexOrNext; // Index if part of the TimerHeap, link otherwise. 145 var _indexOrNext; // Index if part of the TimerHeap, link otherwise.
146 int _id; // Incrementing id to enable sorting of timers with same expiry. 146 int _id; // Incrementing id to enable sorting of timers with same expiry.
147 147
148
148 // Get the next available id. We accept collisions and reordering when the 149 // Get the next available id. We accept collisions and reordering when the
149 // _idCount overflows and the timers expire at the same millisecond. 150 // _idCount overflows and the timers expire at the same millisecond.
150 static int _nextId() { 151 static int _nextId() {
151 var result = _idCount; 152 var result = _idCount;
152 _idCount = (_idCount + 1) & _ID_MASK; 153 _idCount = (_idCount + 1) & _ID_MASK;
153 return result; 154 return result;
154 } 155 }
155 156
157
156 _Timer._internal(this._callback, 158 _Timer._internal(this._callback,
157 this._wakeupTime, 159 this._wakeupTime,
158 this._milliSeconds, 160 this._milliSeconds,
159 this._repeating) : _id = _nextId(); 161 this._repeating) : _id = _nextId();
160 162
163
161 static Timer _createTimer(void callback(Timer timer), 164 static Timer _createTimer(void callback(Timer timer),
162 int milliSeconds, 165 int milliSeconds,
163 bool repeating) { 166 bool repeating) {
164 // Negative timeouts are treated as if 0 timeout. 167 // Negative timeouts are treated as if 0 timeout.
165 if (milliSeconds < 0) { 168 if (milliSeconds < 0) {
166 milliSeconds = 0; 169 milliSeconds = 0;
167 } 170 }
168 // Add one because DateTime.now() is assumed to round down 171 // Add one because DateTime.now() is assumed to round down
169 // to nearest millisecond, not up, so that time + duration is before 172 // to nearest millisecond, not up, so that time + duration is before
170 // duration milliseconds from now. Using microsecond timers like 173 // duration milliseconds from now. Using microsecond timers like
171 // Stopwatch allows detecting that the timer fires early. 174 // Stopwatch allows detecting that the timer fires early.
172 int now = new DateTime.now().millisecondsSinceEpoch; 175 int now = new DateTime.now().millisecondsSinceEpoch;
173 int wakeupTime = (milliSeconds == 0) ? now : (now + 1 + milliSeconds); 176 int wakeupTime = (milliSeconds == 0) ? now : (now + 1 + milliSeconds);
174 177
175 _Timer timer = new _Timer._internal(callback, 178 _Timer timer = new _Timer._internal(callback,
176 wakeupTime, 179 wakeupTime,
177 milliSeconds, 180 milliSeconds,
178 repeating); 181 repeating);
179 // Enqueue this newly created timer in the appropriate structure and 182 // Enqueue this newly created timer in the appropriate structure and
180 // notify if necessary. 183 // notify if necessary.
181 timer._enqueue(); 184 timer._enqueue();
182 return timer; 185 return timer;
183 } 186 }
184 187
188
185 factory _Timer(int milliSeconds, void callback(Timer timer)) { 189 factory _Timer(int milliSeconds, void callback(Timer timer)) {
186 return _createTimer(callback, milliSeconds, false); 190 return _createTimer(callback, milliSeconds, false);
187 } 191 }
188 192
193
189 factory _Timer.periodic(int milliSeconds, void callback(Timer timer)) { 194 factory _Timer.periodic(int milliSeconds, void callback(Timer timer)) {
190 return _createTimer(callback, milliSeconds, true); 195 return _createTimer(callback, milliSeconds, true);
191 } 196 }
192 197
198
193 bool get _isInHeap => _indexOrNext is int; 199 bool get _isInHeap => _indexOrNext is int;
194 200
201
195 int _compareTo(_Timer other) { 202 int _compareTo(_Timer other) {
196 int c = _wakeupTime - other._wakeupTime; 203 int c = _wakeupTime - other._wakeupTime;
197 if (c != 0) return c; 204 if (c != 0) return c;
198 return _id - other._id; 205 return _id - other._id;
199 } 206 }
200 207
208
201 bool get isActive => _callback != null; 209 bool get isActive => _callback != null;
202 210
211
203 // Cancels a set timer. The timer is removed from the timer heap if it is a 212 // Cancels a set timer. The timer is removed from the timer heap if it is a
204 // non-zero timer. Zero timers are kept in the list as they need to consume 213 // non-zero timer. Zero timers are kept in the list as they need to consume
205 // the corresponding pending message. 214 // the corresponding pending message.
206 void cancel() { 215 void cancel() {
207 _callback = null; 216 _callback = null;
208 // Only heap timers are really removed. Zero timers need to consume their 217 // Only heap timers are really removed. Zero timers need to consume their
209 // corresponding wakeup message so they are left in the queue. 218 // corresponding wakeup message so they are left in the queue.
210 if (!_isInHeap) return; 219 if (!_isInHeap) return;
211 bool update = _heap.isFirst(this); 220 bool update = _heap.isFirst(this);
212 _heap.remove(this); 221 _heap.remove(this);
213 if (update) { 222 if (update) {
214 _notifyEventHandler(); 223 _notifyEventHandler();
215 } 224 }
216 } 225 }
217 226
227
218 void _advanceWakeupTime() { 228 void _advanceWakeupTime() {
219 // Recalculate the next wakeup time. For repeating timers with a 0 timeout 229 // Recalculate the next wakeup time. For repeating timers with a 0 timeout
220 // the next wakeup time is now. 230 // the next wakeup time is now.
221 _id = _nextId(); 231 _id = _nextId();
222 if (_milliSeconds > 0) { 232 if (_milliSeconds > 0) {
223 _wakeupTime += _milliSeconds; 233 _wakeupTime += _milliSeconds;
224 } else { 234 } else {
225 _wakeupTime = new DateTime.now().millisecondsSinceEpoch; 235 _wakeupTime = new DateTime.now().millisecondsSinceEpoch;
226 } 236 }
227 } 237 }
228 238
239
229 // Adds a timer to the heap or timer list. Timers with the same wakeup time 240 // Adds a timer to the heap or timer list. Timers with the same wakeup time
230 // are enqueued in order and notified in FIFO order. 241 // are enqueued in order and notified in FIFO order.
231 void _enqueue() { 242 void _enqueue() {
232 if (_milliSeconds == 0) { 243 if (_milliSeconds == 0) {
233 if (_firstZeroTimer == null) { 244 if (_firstZeroTimer == null) {
234 _lastZeroTimer = this; 245 _lastZeroTimer = this;
235 _firstZeroTimer = this; 246 _firstZeroTimer = this;
236 } else { 247 } else {
237 _lastZeroTimer._indexOrNext = this; 248 _lastZeroTimer._indexOrNext = this;
238 _lastZeroTimer = this; 249 _lastZeroTimer = this;
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
285 // handler. _handleTimeout will call _notifyEventHandler once all pending 296 // handler. _handleTimeout will call _notifyEventHandler once all pending
286 // timers are processed. 297 // timers are processed.
287 return; 298 return;
288 } 299 }
289 300
290 // If there are no pending timers. Close down the receive port. 301 // If there are no pending timers. Close down the receive port.
291 if ((_firstZeroTimer == null) && _heap.isEmpty) { 302 if ((_firstZeroTimer == null) && _heap.isEmpty) {
292 // No pending timers: Close the receive port and let the event handler 303 // No pending timers: Close the receive port and let the event handler
293 // know. 304 // know.
294 if (_sendPort != null) { 305 if (_sendPort != null) {
295 VMLibraryHooks.eventHandlerSendData(null, _sendPort, _NO_TIMER); 306 _cancelWakeup();
296 _shutdownTimerHandler(); 307 _shutdownTimerHandler();
297 } 308 }
298 return; 309 return;
299 } else if (_heap.isEmpty) { 310 } else if (_heap.isEmpty) {
300 // Only zero timers are left. Cancel any scheduled wakeups. 311 // Only zero timers are left. Cancel any scheduled wakeups.
301 VMLibraryHooks.eventHandlerSendData(null, _sendPort, _NO_TIMER); 312 _cancelWakeup();
302 return; 313 return;
303 } 314 }
304 315
305 // Only send a message if the requested wakeup time differs from the 316 // Only send a message if the requested wakeup time differs from the
306 // already scheduled wakeup time. 317 // already scheduled wakeup time.
307 var wakeupTime = _heap.first._wakeupTime; 318 var wakeupTime = _heap.first._wakeupTime;
308 if ((_scheduledWakeupTime == null) || 319 if ((_scheduledWakeupTime == null) ||
309 (wakeupTime != _scheduledWakeupTime)) { 320 (wakeupTime != _scheduledWakeupTime)) {
310 if (_sendPort == null) { 321 _scheduleWakeup(wakeupTime);
311 // Create a receive port and register a message handler for the timer
312 // events.
313 _createTimerHandler();
314 }
315 VMLibraryHooks.eventHandlerSendData(null, _sendPort, wakeupTime);
316 _scheduledWakeupTime = wakeupTime;
317 } 322 }
318 } 323 }
319 324
325
320 static List _queueFromTimeoutEvent() { 326 static List _queueFromTimeoutEvent() {
321 var pendingTimers = new List(); 327 var pendingTimers = new List();
322 if (_firstZeroTimer != null) { 328 if (_firstZeroTimer != null) {
323 // Collect pending timers from the timer heap that have an expiration 329 // Collect pending timers from the timer heap that have an expiration
324 // prior to the next zero timer. 330 // prior to the next zero timer.
325 // By definition the first zero timer has been scheduled before the 331 // By definition the first zero timer has been scheduled before the
326 // current time, meaning all timers which are "less than" the first zero 332 // current time, meaning all timers which are "less than" the first zero
327 // timer are expired. The first zero timer will be dispatched when its 333 // timer are expired. The first zero timer will be dispatched when its
328 // corresponding message is delivered. 334 // corresponding message is delivered.
329 var timer; 335 var timer;
330 while (!_heap.isEmpty && (_heap.first._compareTo(_firstZeroTimer) < 0)) { 336 while (!_heap.isEmpty && (_heap.first._compareTo(_firstZeroTimer) < 0)) {
331 timer = _heap.removeFirst(); 337 timer = _heap.removeFirst();
332 pendingTimers.add(timer); 338 pendingTimers.add(timer);
333 } 339 }
334 } else { 340 } else {
335 // Collect pending timers from the timer heap which have expired at this 341 // Collect pending timers from the timer heap which have expired at this
336 // time. 342 // time.
337 var currentTime = new DateTime.now().millisecondsSinceEpoch; 343 var currentTime = new DateTime.now().millisecondsSinceEpoch;
338 var timer; 344 var timer;
339 while (!_heap.isEmpty && (_heap.first._wakeupTime <= currentTime)) { 345 while (!_heap.isEmpty && (_heap.first._wakeupTime <= currentTime)) {
340 timer = _heap.removeFirst(); 346 timer = _heap.removeFirst();
341 pendingTimers.add(timer); 347 pendingTimers.add(timer);
342 } 348 }
343 } 349 }
344 return pendingTimers; 350 return pendingTimers;
345 } 351 }
346 352
353
347 static void _runTimers(List pendingTimers) { 354 static void _runTimers(List pendingTimers) {
348 // If there are no pending timers currently reset the id space before we 355 // If there are no pending timers currently reset the id space before we
349 // have a chance to enqueue new timers. 356 // have a chance to enqueue new timers.
350 if (_heap.isEmpty && (_firstZeroTimer == null)) { 357 if (_heap.isEmpty && (_firstZeroTimer == null)) {
351 _idCount = 0; 358 _idCount = 0;
352 } 359 }
353 360
354 // Fast exit if no pending timers. 361 // Fast exit if no pending timers.
355 if (pendingTimers.length == 0) { 362 if (pendingTimers.length == 0) {
356 return; 363 return;
(...skipping 23 matching lines...) Expand all
380 if (timer._repeating && (timer._callback != null)) { 387 if (timer._repeating && (timer._callback != null)) {
381 timer._advanceWakeupTime(); 388 timer._advanceWakeupTime();
382 timer._enqueue(); 389 timer._enqueue();
383 } 390 }
384 // Execute pending micro tasks. 391 // Execute pending micro tasks.
385 _runPendingImmediateCallback(); 392 _runPendingImmediateCallback();
386 } 393 }
387 } 394 }
388 } finally { 395 } finally {
389 _handlingCallbacks = false; 396 _handlingCallbacks = false;
390 // Notify the event handler or shutdown the port if no more pending
391 // timers are present.
392 _notifyEventHandler();
393 } 397 }
394 } 398 }
395 399
396 // Creates a receive port and registers an empty handler on that port. Just 400
397 // the triggering of the event loop will ensure that timers are executed.
398 static void _handleMessage(msg) { 401 static void _handleMessage(msg) {
399 var pendingTimers; 402 var pendingTimers;
400 if (msg == _ZERO_EVENT) { 403 if (msg == _ZERO_EVENT) {
401 pendingTimers = _queueFromZeroEvent(); 404 pendingTimers = _queueFromZeroEvent();
402 assert(pendingTimers.length > 0); 405 assert(pendingTimers.length > 0);
403 } else { 406 } else {
404 assert(msg == _TIMEOUT_EVENT); 407 assert(msg == _TIMEOUT_EVENT);
408 _scheduledWakeupTime = null; // Consumed the last scheduled wakeup now.
405 pendingTimers = _queueFromTimeoutEvent(); 409 pendingTimers = _queueFromTimeoutEvent();
406 } 410 }
407 _runTimers(pendingTimers); 411 _runTimers(pendingTimers);
412 // Notify the event handler or shutdown the port if no more pending
413 // timers are present.
414 _notifyEventHandler();
408 } 415 }
409 416
417
418 // Tell the event handler to wake this isolate at a specific time.
419 static void _scheduleWakeup(int wakeupTime) {
420 if (_sendPort == null) {
421 _createTimerHandler();
422 }
423 VMLibraryHooks.eventHandlerSendData(null, _sendPort, wakeupTime);
424 _scheduledWakeupTime = wakeupTime;
425 }
426
427
428 // Cancel pending wakeups in the event handler.
429 static void _cancelWakeup() {
430 assert(_sendPort != null);
431 VMLibraryHooks.eventHandlerSendData(null, _sendPort, _NO_TIMER);
432 _scheduledWakeupTime = null;
433 }
434
435
436 // Create a receive port and register a message handler for the timer
437 // events.
410 static void _createTimerHandler() { 438 static void _createTimerHandler() {
411 assert(_receivePort == null); 439 assert(_receivePort == null);
412 assert(_sendPort == null); 440 assert(_sendPort == null);
413 _receivePort = new RawReceivePort(_handleMessage); 441 _receivePort = new RawReceivePort(_handleMessage);
414 _sendPort = _receivePort.sendPort; 442 _sendPort = _receivePort.sendPort;
415 _scheduledWakeupTime = null; 443 _scheduledWakeupTime = null;
416 } 444 }
417 445
446
418 static void _shutdownTimerHandler() { 447 static void _shutdownTimerHandler() {
419 _receivePort.close(); 448 _receivePort.close();
420 _receivePort = null; 449 _receivePort = null;
421 _sendPort = null; 450 _sendPort = null;
422 _scheduledWakeupTime = null; 451 _scheduledWakeupTime = null;
423 } 452 }
424 453
454
425 // The Timer factory registered with the dart:async library by the embedder. 455 // The Timer factory registered with the dart:async library by the embedder.
426 static Timer _factory(int milliSeconds, 456 static Timer _factory(int milliSeconds,
427 void callback(Timer timer), 457 void callback(Timer timer),
428 bool repeating) { 458 bool repeating) {
429 if (repeating) { 459 if (repeating) {
430 return new _Timer.periodic(milliSeconds, callback); 460 return new _Timer.periodic(milliSeconds, callback);
431 } 461 }
432 return new _Timer(milliSeconds, callback); 462 return new _Timer(milliSeconds, callback);
433 } 463 }
434 } 464 }
435 465
466
436 _setupHooks() { 467 _setupHooks() {
437 VMLibraryHooks.timerFactory = _Timer._factory; 468 VMLibraryHooks.timerFactory = _Timer._factory;
438 } 469 }
OLDNEW
« no previous file with comments | « no previous file | tests/lib/async/timer_regress22626_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698