diff --git a/sdk/lib/_internal/lib/isolate_helper.dart b/sdk/lib/_internal/lib/isolate_helper.dart index 47df338e5fa..2a25f549ea6 100644 --- a/sdk/lib/_internal/lib/isolate_helper.dart +++ b/sdk/lib/_internal/lib/isolate_helper.dart @@ -1360,6 +1360,16 @@ class TimerImpl implements Timer { TimerImpl(int milliseconds, void callback()) : _once = true { if (milliseconds == 0 && (!hasTimer() || _globalState.isWorker)) { + + void internalCallback() { + _handle = null; + callback(); + } + + // Setting _handle to something different from null indicates that the + // callback has not been run. Hence, the choice of 1 is arbitrary. + _handle = 1; + // This makes a dependency between the async library and the // event loop of the isolate library. The compiler makes sure // that the event loop is compiled if [Timer] is used. @@ -1367,15 +1377,17 @@ class TimerImpl implements Timer { // loop instead of setTimeout, to make sure the futures get executed in // order. _globalState.topEventLoop.enqueue( - _globalState.currentContext, callback, 'timer'); + _globalState.currentContext, internalCallback, 'timer'); _inEventLoop = true; } else if (hasTimer()) { - _globalState.topEventLoop.activeTimerCount++; + void internalCallback() { - callback(); _handle = null; _globalState.topEventLoop.activeTimerCount--; + callback(); } + + _globalState.topEventLoop.activeTimerCount++; _handle = JS('int', '#.setTimeout(#, #)', globalThis, convertDartClosureToJS(internalCallback, 0), @@ -1416,6 +1428,8 @@ class TimerImpl implements Timer { throw new UnsupportedError("Canceling a timer."); } } + + bool get isActive => _handle != null; } bool hasTimer() => JS('', '#.setTimeout', globalThis) != null; diff --git a/sdk/lib/async/timer.dart b/sdk/lib/async/timer.dart index 3544728ece9..ff7b07ed5bd 100644 --- a/sdk/lib/async/timer.dart +++ b/sdk/lib/async/timer.dart @@ -57,6 +57,16 @@ abstract class Timer { * Cancels the timer. */ void cancel(); + + /** + * Returns whether the timer is still active. + * + * A non-periodic timer is active if the callback has not been executed, + * and the timer has not been canceled. + * + * A periodic timer is active if it has not been canceled. + */ + bool get isActive; } external Timer _createTimer(Duration duration, void callback()); diff --git a/sdk/lib/async/zone.dart b/sdk/lib/async/zone.dart index 5f9fce92436..bea3fc24b1e 100644 --- a/sdk/lib/async/zone.dart +++ b/sdk/lib/async/zone.dart @@ -436,7 +436,6 @@ class _ZoneTimer implements Timer { final _Zone _zone; final _TimerCallback _callback; Timer _timer; - bool _isDone = false; _ZoneTimer(this._zone, Duration duration, this._callback) { _zone.expectCallback(); @@ -444,15 +443,15 @@ class _ZoneTimer implements Timer { } void _run() { - _isDone = true; _zone.executeCallbackGuarded(_callback); } void cancel() { - if (!_isDone) _zone.cancelCallbackExpectation(); - _isDone = true; + if (_timer.isActive) _zone.cancelCallbackExpectation(); _timer.cancel(); } + + bool get isActive => _timer.isActive; } typedef void _PeriodicTimerCallback(Timer timer); @@ -464,7 +463,6 @@ class _PeriodicZoneTimer implements Timer { final _Zone _zone; final _PeriodicTimerCallback _callback; Timer _timer; - bool _isDone = false; _PeriodicZoneTimer(this._zone, Duration duration, this._callback) { _zone.expectCallback(); @@ -477,10 +475,11 @@ class _PeriodicZoneTimer implements Timer { } void cancel() { - if (!_isDone) _zone.cancelCallbackExpectation(); - _isDone = true; + if (_timer.isActive) _zone.cancelCallbackExpectation(); _timer.cancel(); } + + bool get isActive => _timer.isActive; } /** diff --git a/sdk/lib/html/dartium/html_dartium.dart b/sdk/lib/html/dartium/html_dartium.dart index 5c00a9f14ab..13ab373f2c3 100644 --- a/sdk/lib/html/dartium/html_dartium.dart +++ b/sdk/lib/html/dartium/html_dartium.dart @@ -31009,31 +31009,44 @@ final _pureIsolatePrintClosure = (s) { final _forwardingPrintClosure = _Utils.forwardingPrint; -class _Timer implements Timer { - final canceller; + class _Timer implements Timer { + final _canceler; - _Timer(this.canceller); + _Timer(int milliSeconds, void callback(Timer timer), bool repeating) { - void cancel() { canceller(); } + if (repeating) { + int id = window._setInterval(() { + _canceler = null; + callback(this); + }, milliSeconds);) + _canceler = () => window._clearInterval(id); + } else { + int id = window._setTimeout(() { + _canceler = null; + callback(this); + }, milliSeconds); ) + _canceler = window._clearTimeout(id); + } + } + + void cancel() { + if (_canceler != null) { + _canceler(); + } + _canceler = null; + } + + bool get isActive => _canceler != null; } -get _timerFactoryClosure => (int milliSeconds, void callback(Timer timer), bool repeating) { - var maker; - var canceller; - if (repeating) { - maker = window._setInterval; - canceller = window._clearInterval; - } else { - maker = window._setTimeout; - canceller = window._clearTimeout; - } - Timer timer; - final int id = maker(() { callback(timer); }, milliSeconds); - timer = new _Timer(() { canceller(id); }); - return timer; +get _timerFactoryClosure => + (int milliSeconds, void callback(Timer timer), bool repeating) { + return new _Timer(milliseconds, callback, repeating); }; + class _PureIsolateTimer implements Timer { + bool _isDone = false; final ReceivePort _port = new ReceivePort(); SendPort _sendPort; // Effectively final. @@ -31043,6 +31056,7 @@ class _PureIsolateTimer implements Timer { _sendPort = _port.toSendPort(); _port.receive((msg, replyTo) { assert(msg == _TIMER_PING); + _isDone = !repeating; callback(this); if (!repeating) _cancel(); }); @@ -31056,12 +31070,15 @@ class _PureIsolateTimer implements Timer { } void _cancel() { + _isDone = true; _port.close(); } _send(msg) { _sendToHelperIsolate(msg, _sendPort); } + + bool get isActive => !_isDone; } get _pureIsolateTimerFactoryClosure => diff --git a/sdk/lib/io/timer_impl.dart b/sdk/lib/io/timer_impl.dart index 73c7cae1c12..6e56e3e1550 100644 --- a/sdk/lib/io/timer_impl.dart +++ b/sdk/lib/io/timer_impl.dart @@ -50,6 +50,7 @@ class _Timer extends LinkedListEntry<_Timer> implements Timer { bool get _repeating => _milliSeconds >= 0; + bool get isActive => _callback != null; // 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. @@ -143,7 +144,12 @@ class _Timer extends LinkedListEntry<_Timer> implements Timer { // one of the later timers which will set the callback to // null. if (timer._callback != null) { - timer._callback(timer); + var callback = timer._callback; + if (!timer._repeating) { + //Mark timer as inactive. + timer._callback = null; + } + callback(timer); // Re-insert repeating timer if not canceled. if (timer._repeating && timer._callback != null) { timer._advanceWakeupTime(); diff --git a/tests/lib/async/timer_isActive_test.dart b/tests/lib/async/timer_isActive_test.dart new file mode 100644 index 00000000000..e9ecda14976 --- /dev/null +++ b/tests/lib/async/timer_isActive_test.dart @@ -0,0 +1,45 @@ +// 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. + +import 'dart:async'; +import '../../../pkg/unittest/lib/unittest.dart'; + +main() { + + test("timer isActive test", () { + Timer t; + + t = new Timer(const Duration(seconds: 1), + expectAsync0(() => expect(t.isActive, equals(false)))); + expect(t.isActive, equals(true)); + }); + + test("periodic timer cancel test", () { + Timer t; + + int i = 0; + void checkActive(Timer timer) { + expect(t.isActive, equals(true)); + if (i == 2) { + t.cancel(); + expect(t.isActive, equals(false)); + } + i++; + } + + t = new Timer.periodic(new Duration(milliseconds: 1), + expectAsync1(checkActive, count: 3)); + expect(t.isActive, equals(true)); + }); + + test("timer cancel test", () { + Timer timer = new Timer(const Duration(seconds: 1), + () => fail("Should not be reached.")); + Timer.run(expectAsync0(() { + expect(timer.isActive, equals(true)); + timer.cancel(); + expect(timer.isActive, equals(false)); + })); + }); +} diff --git a/tests/lib/lib.status b/tests/lib/lib.status index bedba2b1397..a7dc253a14d 100644 --- a/tests/lib/lib.status +++ b/tests/lib/lib.status @@ -59,6 +59,7 @@ async/stream_periodic4_test: Fail # Timer interface not supported; dartbug.com/7 async/stream_periodic5_test: Fail # Timer interface not supported; dartbug.com/7728. async/run_zoned7_test: Fail # Timer interface not supported: dartbug.com/7728. async/catch_errors22_test: Fail # Timer interface not supported: dartbug.com/7728. +async/timer_isActive_test: Fail # Timer interface not supported: dartbug.com/7728. [ $compiler == dart2js && $browser ] async/timer_not_available_test: Fail, OK # only meant to test when there is no way to diff --git a/tools/dom/src/native_DOMImplementation.dart b/tools/dom/src/native_DOMImplementation.dart index 89f13941082..01a8ecba011 100644 --- a/tools/dom/src/native_DOMImplementation.dart +++ b/tools/dom/src/native_DOMImplementation.dart @@ -198,31 +198,43 @@ final _pureIsolatePrintClosure = (s) { final _forwardingPrintClosure = _Utils.forwardingPrint; -class _Timer implements Timer { - final canceller; + class _Timer implements Timer{ + var _canceler; - _Timer(this.canceller); + _Timer(int milliSeconds, void callback(Timer timer), bool repeating) { - void cancel() { canceller(); } + if (repeating) { + int id = window._setInterval(() { + callback(this); + }, milliSeconds);) + _canceler = () => window._clearInterval(id); + } else { + int id = window._setTimeout(() { + _canceler = null; + callback(this); + }, milliSeconds);) + _canceler = () => window._clearTimeout(id); + } + } + + void cancel() { + if (_canceler != null) { + _canceler(); + } + _canceler = null; + } + + bool get isActive => _canceler != null; } -get _timerFactoryClosure => (int milliSeconds, void callback(Timer timer), bool repeating) { - var maker; - var canceller; - if (repeating) { - maker = window._setInterval; - canceller = window._clearInterval; - } else { - maker = window._setTimeout; - canceller = window._clearTimeout; - } - Timer timer; - final int id = maker(() { callback(timer); }, milliSeconds); - timer = new _Timer(() { canceller(id); }); - return timer; +get _timerFactoryClosure => + (int milliSeconds, void callback(Timer timer), bool repeating) { + return new _Timer(milliseconds, callback, repeating); }; + class _PureIsolateTimer implements Timer { + bool _isActive = true; final ReceivePort _port = new ReceivePort(); SendPort _sendPort; // Effectively final. @@ -232,6 +244,7 @@ class _PureIsolateTimer implements Timer { _sendPort = _port.toSendPort(); _port.receive((msg, replyTo) { assert(msg == _TIMER_PING); + _isActive = repeating; callback(this); if (!repeating) _cancel(); }); @@ -245,12 +258,15 @@ class _PureIsolateTimer implements Timer { } void _cancel() { + _isActive = false; _port.close(); } _send(msg) { _sendToHelperIsolate(msg, _sendPort); } + + bool get isActive => _isActive; } get _pureIsolateTimerFactoryClosure =>