1aaa58f803
R=sgjesse@google.com BUG= TEST= Review URL: http://codereview.chromium.org//8992007 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@2577 260f80e4-7a28-3924-810f-c04153c831b5
195 lines
5.1 KiB
Dart
195 lines
5.1 KiB
Dart
// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
class _Timer implements Timer {
|
|
|
|
/*
|
|
* Set jitter to wake up timer events that would happen in _TIMER_JITTER ms.
|
|
*/
|
|
static final int _TIMER_JITTER = 0;
|
|
|
|
/*
|
|
* Disables the timer.
|
|
*/
|
|
static final int _NO_TIMER = -1;
|
|
|
|
static Timer _createTimer(void callback(Timer timer),
|
|
int milliSeconds,
|
|
bool repeating) {
|
|
EventHandler._start();
|
|
if (_timers === null) {
|
|
_timers = new DoubleLinkedQueue<_Timer>();
|
|
}
|
|
Timer timer = new _Timer._internal();
|
|
timer._callback = callback;
|
|
timer._milliSeconds = milliSeconds;
|
|
timer._wakeupTime = (new Date.now()).value + milliSeconds;
|
|
timer._repeating = repeating;
|
|
timer._addTimerToList();
|
|
timer._notifyEventHandler();
|
|
return timer;
|
|
}
|
|
|
|
factory _Timer(void callback(Timer timer), int milliSeconds) {
|
|
return _createTimer(callback, milliSeconds, false);
|
|
}
|
|
|
|
factory _Timer.repeating(void callback(Timer timer), int milliSeconds) {
|
|
return _createTimer(callback, milliSeconds, true);
|
|
}
|
|
|
|
_Timer._internal() {}
|
|
|
|
void _clear() {
|
|
_callback = null;
|
|
_milliSeconds = 0;
|
|
_wakeupTime = 0;
|
|
_repeating = false;
|
|
}
|
|
|
|
/*
|
|
* Cancels a set timer. The timer is removed from the timer list and if
|
|
* the given timer is the earliest timer the native timer is reset.
|
|
*/
|
|
void cancel() {
|
|
_clear();
|
|
DoubleLinkedQueueEntry<_Timer> entry = _timers.firstEntry();
|
|
DoubleLinkedQueueEntry<_Timer> first = _timers.firstEntry();
|
|
|
|
while (entry !== null) {
|
|
if (entry.element === this) {
|
|
entry.remove();
|
|
if (first.element == this) {
|
|
entry = _timers.firstEntry();
|
|
_notifyEventHandler();
|
|
}
|
|
return;
|
|
}
|
|
entry = entry.nextEntry();
|
|
}
|
|
}
|
|
|
|
void _advanceWakeupTime() {
|
|
_wakeupTime += _milliSeconds;
|
|
}
|
|
|
|
/*
|
|
* Adds a timer to the timer list and resets the native timer if it is the
|
|
* earliest timer in the list. Timers with the same wakeup time are enqueued
|
|
* in order and notified in FIFO order.
|
|
*/
|
|
void _addTimerToList() {
|
|
if (_callback !== null) {
|
|
|
|
DoubleLinkedQueueEntry<_Timer> entry = _timers.firstEntry();
|
|
while (entry !== null) {
|
|
if (_wakeupTime < entry.element._wakeupTime) {
|
|
entry.prepend(this);
|
|
return;
|
|
}
|
|
entry = entry.nextEntry();
|
|
}
|
|
_timers.addLast(this);
|
|
}
|
|
}
|
|
|
|
|
|
void _notifyEventHandler() {
|
|
if (_handling_callbacks) {
|
|
// While we are already handling callbacks we will not notify the event
|
|
// handler. _handleTimeout will call _notifyEventHandler once all pending
|
|
// timers are processed.
|
|
return;
|
|
}
|
|
|
|
if (_timers.firstEntry() === null) {
|
|
// No pending timers: Close the receive port and let the event handler
|
|
// know.
|
|
if (_receivePort !== null) {
|
|
EventHandler._sendData(-1, _receivePort, _NO_TIMER);
|
|
_shutdownTimerHandler();
|
|
}
|
|
} else {
|
|
if (_receivePort === null) {
|
|
// Create a receive port and register a message handler for the timer
|
|
// events.
|
|
_createTimerHandler();
|
|
}
|
|
EventHandler._sendData(-1,
|
|
_receivePort,
|
|
_timers.firstEntry().element._wakeupTime);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Creates a receive port and registers the timer handler on that receive
|
|
* port.
|
|
*/
|
|
void _createTimerHandler() {
|
|
|
|
void _handleTimeout() {
|
|
int currentTime = (new Date.now()).value + _TIMER_JITTER;
|
|
|
|
// Collect all pending timers.
|
|
DoubleLinkedQueueEntry<_Timer> entry = _timers.firstEntry();
|
|
var pending_timers = new List();
|
|
while (entry !== null) {
|
|
_Timer timer = entry.element;
|
|
if (timer._wakeupTime <= currentTime) {
|
|
entry.remove();
|
|
pending_timers.addLast(timer);
|
|
entry = _timers.firstEntry();
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Trigger all of the pending timers. New timers added as part of the
|
|
// callbacks will be enqueued now and notified in the next spin at the
|
|
// earliest.
|
|
_handling_callbacks = true;
|
|
try {
|
|
for (var timer in pending_timers) {
|
|
timer._callback(timer);
|
|
if (timer._repeating) {
|
|
timer._advanceWakeupTime();
|
|
timer._addTimerToList();
|
|
}
|
|
}
|
|
} finally {
|
|
_handling_callbacks = false;
|
|
}
|
|
_notifyEventHandler();
|
|
}
|
|
|
|
if(_receivePort === null) {
|
|
_receivePort = new ReceivePort();
|
|
_receivePort.receive((var message, ignored) {
|
|
_handleTimeout();
|
|
});
|
|
}
|
|
}
|
|
|
|
void _shutdownTimerHandler() {
|
|
_receivePort.close();
|
|
_receivePort = null;
|
|
}
|
|
|
|
|
|
/*
|
|
* Timers are ordered by wakeup time.
|
|
*/
|
|
static DoubleLinkedQueue<_Timer> _timers;
|
|
|
|
static ReceivePort _receivePort;
|
|
static bool _handling_callbacks = false;
|
|
|
|
var _callback;
|
|
int _milliSeconds;
|
|
int _wakeupTime;
|
|
bool _repeating;
|
|
}
|
|
|