blob: 5a7981ec6ff777436562273656a56803b0be8756 [file] [log] [blame]
// Copyright (c) 2013, 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.
part of dart.io;
class _Timer extends LinkedListEntry<_Timer> implements Timer {
// Disables the timer.
static const int _NO_TIMER = -1;
// Timers are ordered by wakeup time.
static LinkedList<_Timer> _timers = new LinkedList<_Timer>();
static ReceivePort _receivePort;
static bool _handling_callbacks = false;
Function _callback;
int _milliSeconds;
int _wakeupTime;
static Timer _createTimer(void callback(Timer timer),
int milliSeconds,
bool repeating) {
_EventHandler._start();
_Timer timer = new _Timer._internal();
timer._callback = callback;
timer._wakeupTime =
new DateTime.now().millisecondsSinceEpoch + milliSeconds;
timer._milliSeconds = repeating ? milliSeconds : -1;
timer._addTimerToList();
timer._notifyEventHandler();
return timer;
}
factory _Timer(int milliSeconds, void callback(Timer timer)) {
return _createTimer(callback, milliSeconds, false);
}
factory _Timer.periodic(int milliSeconds, void callback(Timer timer)) {
return _createTimer(callback, milliSeconds, true);
}
_Timer._internal() {}
void _clear() {
_callback = null;
_milliSeconds = 0;
_wakeupTime = 0;
}
bool get _repeating => _milliSeconds >= 0;
// 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();
// Return if already canceled.
if (list == null) return;
assert(!_timers.isEmpty);
_Timer first = _timers.first;
unlink();
if (identical(first, this)) {
_notifyEventHandler();
}
}
void _advanceWakeupTime() {
assert(_milliSeconds >= 0);
_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() {
_Timer entry = _timers.isEmpty ? null : _timers.first;
while (entry != null) {
if (_wakeupTime < entry._wakeupTime) {
entry.insertBefore(this);
return;
}
entry = entry.next;
}
_timers.add(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.isEmpty) {
// No pending timers: Close the receive port and let the event handler
// know.
if (_receivePort != null) {
_EventHandler._sendData(null, _receivePort, _NO_TIMER);
_shutdownTimerHandler();
}
} else {
if (_receivePort == null) {
// Create a receive port and register a message handler for the timer
// events.
_createTimerHandler();
}
_EventHandler._sendData(null,
_receivePort,
_timers.first._wakeupTime);
}
}
// Creates a receive port and registers the timer handler on that
// receive port.
void _createTimerHandler() {
void _handleTimeout() {
int currentTime = new DateTime.now().millisecondsSinceEpoch;
// Collect all pending timers.
var pending_timers = new List();
while (!_timers.isEmpty) {
_Timer entry = _timers.first;
if (entry._wakeupTime <= currentTime) {
entry.unlink();
pending_timers.add(entry);
} 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) {
// One of the timers in the pending_timers list can cancel
// one of the later timers which will set the callback to
// null.
if (timer._callback != null) {
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;
}
}
// Provide a closure which will allocate a Timer object to be able to hook
// up the Timer interface in dart:isolate with the implementation here.
_getTimerFactoryClosure() {
return (int milliSeconds, void callback(Timer timer), bool repeating) {
if (repeating) {
return new _Timer.periodic(milliSeconds, callback);
}
return new _Timer(milliSeconds, callback);
};
}