mirror of
https://github.com/dart-lang/sdk
synced 2024-11-02 14:27:18 +00:00
Add tasks to zones.
R=lrn@google.com, misko@google.com Review URL: https://codereview.chromium.org/1848933002 .
This commit is contained in:
parent
2a2c3c9b33
commit
85cccde717
4 changed files with 1320 additions and 77 deletions
|
@ -4,6 +4,84 @@
|
|||
|
||||
part of dart.async;
|
||||
|
||||
abstract class _TimerTask implements Timer {
|
||||
final Zone _zone;
|
||||
final Timer _nativeTimer;
|
||||
|
||||
_TimerTask(this._nativeTimer, this._zone);
|
||||
|
||||
void cancel() {
|
||||
_nativeTimer.cancel();
|
||||
}
|
||||
|
||||
bool get isActive => _nativeTimer.isActive;
|
||||
}
|
||||
|
||||
class _SingleShotTimerTask extends _TimerTask {
|
||||
// TODO(floitsch): the generic argument should be 'void'.
|
||||
final ZoneCallback<dynamic> _callback;
|
||||
|
||||
_SingleShotTimerTask(Timer timer, this._callback, Zone zone)
|
||||
: super(timer, zone);
|
||||
}
|
||||
|
||||
class _PeriodicTimerTask extends _TimerTask {
|
||||
// TODO(floitsch): the first generic argument should be 'void'.
|
||||
final ZoneUnaryCallback<dynamic, Timer> _callback;
|
||||
|
||||
_PeriodicTimerTask(Timer timer, this._callback, Zone zone)
|
||||
: super(timer, zone);
|
||||
}
|
||||
|
||||
/**
|
||||
* A task specification for a single-shot timer.
|
||||
*
|
||||
* *Experimental*. Might disappear without notice.
|
||||
*/
|
||||
class SingleShotTimerTaskSpecification implements TaskSpecification {
|
||||
static const String specificationName = "dart.async.timer";
|
||||
|
||||
/** The duration after which the timer should invoke the [callback]. */
|
||||
final Duration duration;
|
||||
|
||||
/** The callback that should be run when the timer triggers. */
|
||||
// TODO(floitsch): the generic argument should be void.
|
||||
final ZoneCallback<dynamic> callback;
|
||||
|
||||
SingleShotTimerTaskSpecification(this.duration, void this.callback());
|
||||
|
||||
@override
|
||||
String get name => specificationName;
|
||||
|
||||
@override
|
||||
bool get isOneShot => true;
|
||||
}
|
||||
|
||||
/**
|
||||
* A task specification for a periodic timer.
|
||||
*
|
||||
* *Experimental*. Might disappear without notice.
|
||||
*/
|
||||
class PeriodicTimerTaskSpecification implements TaskSpecification {
|
||||
static const String specificationName = "dart.async.periodic-timer";
|
||||
|
||||
/** The interval at which the periodic timer should invoke the [callback]. */
|
||||
final Duration duration;
|
||||
|
||||
/** The callback that should be run when the timer triggers. */
|
||||
// TODO(floitsch): the first generic argument should be void.
|
||||
final ZoneUnaryCallback<dynamic, Timer> callback;
|
||||
|
||||
PeriodicTimerTaskSpecification(
|
||||
this.duration, void this.callback(Timer timer));
|
||||
|
||||
@override
|
||||
String get name => specificationName;
|
||||
|
||||
@override
|
||||
bool get isOneShot => false;
|
||||
}
|
||||
|
||||
/**
|
||||
* A count-down timer that can be configured to fire once or repeatedly.
|
||||
*
|
||||
|
@ -47,10 +125,15 @@ abstract class Timer {
|
|||
if (Zone.current == Zone.ROOT) {
|
||||
// No need to bind the callback. We know that the root's timer will
|
||||
// be invoked in the root zone.
|
||||
return Zone.current.createTimer(duration, callback);
|
||||
return Timer._createTimer(duration, callback);
|
||||
}
|
||||
return Zone.current.createTimer(
|
||||
duration, Zone.current.bindCallback(callback, runGuarded: true));
|
||||
return Zone.current.createTimer(duration, callback);
|
||||
}
|
||||
|
||||
factory Timer._task(Zone zone, Duration duration, void callback()) {
|
||||
SingleShotTimerTaskSpecification specification =
|
||||
new SingleShotTimerTaskSpecification(duration, callback);
|
||||
return zone.createTask(_createSingleShotTimerTask, specification);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -70,17 +153,65 @@ abstract class Timer {
|
|||
* scheduled for - even if the actual callback was delayed.
|
||||
*/
|
||||
factory Timer.periodic(Duration duration,
|
||||
void callback(Timer timer)) {
|
||||
void callback(Timer timer)) {
|
||||
if (Zone.current == Zone.ROOT) {
|
||||
// No need to bind the callback. We know that the root's timer will
|
||||
// be invoked in the root zone.
|
||||
return Zone.current.createPeriodicTimer(duration, callback);
|
||||
return Timer._createPeriodicTimer(duration, callback);
|
||||
}
|
||||
return Zone.current.createPeriodicTimer(duration, callback);
|
||||
}
|
||||
|
||||
factory Timer._periodicTask(Zone zone, Duration duration,
|
||||
void callback(Timer timer)) {
|
||||
PeriodicTimerTaskSpecification specification =
|
||||
new PeriodicTimerTaskSpecification(duration, callback);
|
||||
return zone.createTask(_createPeriodicTimerTask, specification);
|
||||
}
|
||||
|
||||
static Timer _createSingleShotTimerTask(
|
||||
SingleShotTimerTaskSpecification specification, Zone zone) {
|
||||
ZoneCallback registeredCallback = identical(_ROOT_ZONE, zone)
|
||||
? specification.callback
|
||||
: zone.registerCallback(specification.callback);
|
||||
|
||||
_TimerTask timerTask;
|
||||
|
||||
Timer nativeTimer = Timer._createTimer(specification.duration, () {
|
||||
timerTask._zone.runTask(_runSingleShotCallback, timerTask, null);
|
||||
});
|
||||
|
||||
timerTask = new _SingleShotTimerTask(nativeTimer, registeredCallback, zone);
|
||||
return timerTask;
|
||||
}
|
||||
|
||||
static void _runSingleShotCallback(_SingleShotTimerTask timerTask, Object _) {
|
||||
timerTask._callback();
|
||||
}
|
||||
|
||||
static Timer _createPeriodicTimerTask(
|
||||
PeriodicTimerTaskSpecification specification, Zone zone) {
|
||||
// TODO(floitsch): the return type should be 'void', and the type
|
||||
// should be inferred.
|
||||
var boundCallback = Zone.current.bindUnaryCallback/*<dynamic, Timer>*/(
|
||||
callback, runGuarded: true);
|
||||
return Zone.current.createPeriodicTimer(duration, boundCallback);
|
||||
ZoneUnaryCallback<dynamic, Timer> registeredCallback =
|
||||
identical(_ROOT_ZONE, zone)
|
||||
? specification.callback
|
||||
: zone.registerUnaryCallback/*<dynamic, Timer>*/(
|
||||
specification.callback);
|
||||
|
||||
_TimerTask timerTask;
|
||||
|
||||
Timer nativeTimer =
|
||||
Timer._createPeriodicTimer(specification.duration, (Timer _) {
|
||||
timerTask._zone.runTask(_runPeriodicCallback, timerTask, null);
|
||||
});
|
||||
|
||||
timerTask = new _PeriodicTimerTask(nativeTimer, registeredCallback, zone);
|
||||
return timerTask;
|
||||
}
|
||||
|
||||
static void _runPeriodicCallback(_PeriodicTimerTask timerTask, Object _) {
|
||||
timerTask._callback(timerTask);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -8,6 +8,13 @@ typedef R ZoneCallback<R>();
|
|||
typedef R ZoneUnaryCallback<R, T>(T arg);
|
||||
typedef R ZoneBinaryCallback<R, T1, T2>(T1 arg1, T2 arg2);
|
||||
|
||||
/// *Experimental*. Might disappear without warning.
|
||||
typedef T TaskCreate<T, S extends TaskSpecification>(
|
||||
S specification, Zone zone);
|
||||
/// *Experimental*. Might disappear without warning.
|
||||
typedef void TaskRun<T, A>(T task, A arg);
|
||||
|
||||
|
||||
// TODO(floitsch): we are abusing generic typedefs as typedefs for generic
|
||||
// functions.
|
||||
/*ABUSE*/
|
||||
|
@ -33,19 +40,31 @@ typedef ZoneBinaryCallback<R, T1, T2> RegisterBinaryCallbackHandler<R, T1, T2>(
|
|||
Zone self, ZoneDelegate parent, Zone zone, R f(T1 arg1, T2 arg2));
|
||||
typedef AsyncError ErrorCallbackHandler(Zone self, ZoneDelegate parent,
|
||||
Zone zone, Object error, StackTrace stackTrace);
|
||||
/// *Experimental*. Might disappear without warning.
|
||||
/*ABUSE*/
|
||||
typedef T CreateTaskHandler<T, S extends TaskSpecification>(
|
||||
Zone self, ZoneDelegate parent, Zone zone,
|
||||
TaskCreate<T, S> create, S taskSpecification);
|
||||
/// *Experimental*. Might disappear without warning.
|
||||
/*ABUSE*/
|
||||
typedef void RunTaskHandler<T, A>(Zone self, ZoneDelegate parent, Zone zone,
|
||||
TaskRun<T, A> run, T task, A arg);
|
||||
typedef void ScheduleMicrotaskHandler(
|
||||
Zone self, ZoneDelegate parent, Zone zone, void f());
|
||||
typedef Timer CreateTimerHandler(
|
||||
Zone self, ZoneDelegate parent, Zone zone, Duration duration, void f());
|
||||
typedef Timer CreatePeriodicTimerHandler(
|
||||
Zone self, ZoneDelegate parent, Zone zone,
|
||||
Duration period, void f(Timer timer));
|
||||
typedef void PrintHandler(
|
||||
Zone self, ZoneDelegate parent, Zone zone, String line);
|
||||
typedef Zone ForkHandler(Zone self, ZoneDelegate parent, Zone zone,
|
||||
ZoneSpecification specification,
|
||||
Map zoneValues);
|
||||
|
||||
// The following typedef declarations are used by functionality which
|
||||
// will be removed and replaced by tasksif the task experiment is successful.
|
||||
typedef Timer CreateTimerHandler(
|
||||
Zone self, ZoneDelegate parent, Zone zone, Duration duration, void f());
|
||||
typedef Timer CreatePeriodicTimerHandler(
|
||||
Zone self, ZoneDelegate parent, Zone zone,
|
||||
Duration period, void f(Timer timer));
|
||||
|
||||
/** Pair of error and stack trace. Returned by [Zone.errorCallback]. */
|
||||
class AsyncError implements Error {
|
||||
final Object error;
|
||||
|
@ -56,10 +75,41 @@ class AsyncError implements Error {
|
|||
String toString() => '$error';
|
||||
}
|
||||
|
||||
/**
|
||||
* A task specification contains the necessary information to create a task.
|
||||
*
|
||||
* See [Zone.createTask] for how a specification is used to create a task.
|
||||
*
|
||||
* Task specifications should be public and it should be possible to create
|
||||
* new instances as a user. That is, custom zones should be able to replace
|
||||
* an existing specification with a modified one.
|
||||
*
|
||||
* *Experimental*. This class might disappear without warning.
|
||||
*/
|
||||
abstract class TaskSpecification {
|
||||
/**
|
||||
* Description of the task.
|
||||
*
|
||||
* This string is unused by the root-zone, but might be used for debugging,
|
||||
* and testing. As such, it should be relatively unique in its category.
|
||||
*
|
||||
* As a general guideline we recommend: "package-name.library.action".
|
||||
*/
|
||||
String get name;
|
||||
|
||||
/**
|
||||
* Whether the scheduled task triggers at most once.
|
||||
*
|
||||
* If the task is not a one-shot task, it may need to be canceled to prevent
|
||||
* further iterations of the task.
|
||||
*/
|
||||
bool get isOneShot;
|
||||
}
|
||||
|
||||
class _ZoneFunction<T extends Function> {
|
||||
final _Zone zone;
|
||||
final T function;
|
||||
|
||||
const _ZoneFunction(this.zone, this.function);
|
||||
}
|
||||
|
||||
|
@ -85,6 +135,9 @@ class _ZoneFunction<T extends Function> {
|
|||
abstract class ZoneSpecification {
|
||||
/**
|
||||
* Creates a specification with the provided handlers.
|
||||
*
|
||||
* The task-related parameters ([createTask] and [runTask]) are experimental
|
||||
* and might be removed without warning.
|
||||
*/
|
||||
const factory ZoneSpecification({
|
||||
HandleUncaughtErrorHandler handleUncaughtError,
|
||||
|
@ -96,7 +149,11 @@ abstract class ZoneSpecification {
|
|||
RegisterBinaryCallbackHandler registerBinaryCallback,
|
||||
ErrorCallbackHandler errorCallback,
|
||||
ScheduleMicrotaskHandler scheduleMicrotask,
|
||||
CreateTaskHandler createTask,
|
||||
RunTaskHandler runTask,
|
||||
// TODO(floitsch): mark as deprecated once tasks are non-experimental.
|
||||
CreateTimerHandler createTimer,
|
||||
// TODO(floitsch): mark as deprecated once tasks are non-experimental.
|
||||
CreatePeriodicTimerHandler createPeriodicTimer,
|
||||
PrintHandler print,
|
||||
ForkHandler fork
|
||||
|
@ -105,6 +162,9 @@ abstract class ZoneSpecification {
|
|||
/**
|
||||
* Creates a specification from [other] with the provided handlers overriding
|
||||
* the ones in [other].
|
||||
*
|
||||
* The task-related parameters ([createTask] and [runTask]) are experimental
|
||||
* and might be removed without warning.
|
||||
*/
|
||||
factory ZoneSpecification.from(ZoneSpecification other, {
|
||||
HandleUncaughtErrorHandler handleUncaughtError: null,
|
||||
|
@ -116,7 +176,11 @@ abstract class ZoneSpecification {
|
|||
RegisterBinaryCallbackHandler registerBinaryCallback: null,
|
||||
ErrorCallbackHandler errorCallback: null,
|
||||
ScheduleMicrotaskHandler scheduleMicrotask: null,
|
||||
CreateTaskHandler createTask: null,
|
||||
RunTaskHandler runTask: null,
|
||||
// TODO(floitsch): mark as deprecated once tasks are non-experimental.
|
||||
CreateTimerHandler createTimer: null,
|
||||
// TODO(floitsch): mark as deprecated once tasks are non-experimental.
|
||||
CreatePeriodicTimerHandler createPeriodicTimer: null,
|
||||
PrintHandler print: null,
|
||||
ForkHandler fork: null
|
||||
|
@ -132,11 +196,14 @@ abstract class ZoneSpecification {
|
|||
registerBinaryCallback: registerBinaryCallback ??
|
||||
other.registerBinaryCallback,
|
||||
errorCallback: errorCallback ?? other.errorCallback,
|
||||
|
||||
createTask: createTask ?? other.createTask,
|
||||
runTask: runTask ?? other.runTask,
|
||||
print : print ?? other.print,
|
||||
fork: fork ?? other.fork,
|
||||
scheduleMicrotask: scheduleMicrotask ?? other.scheduleMicrotask,
|
||||
createTimer : createTimer ?? other.createTimer,
|
||||
createPeriodicTimer: createPeriodicTimer ?? other.createPeriodicTimer,
|
||||
print : print ?? other.print,
|
||||
fork: fork ?? other.fork);
|
||||
createPeriodicTimer: createPeriodicTimer ?? other.createPeriodicTimer);
|
||||
}
|
||||
|
||||
HandleUncaughtErrorHandler get handleUncaughtError;
|
||||
|
@ -148,10 +215,17 @@ abstract class ZoneSpecification {
|
|||
RegisterBinaryCallbackHandler get registerBinaryCallback;
|
||||
ErrorCallbackHandler get errorCallback;
|
||||
ScheduleMicrotaskHandler get scheduleMicrotask;
|
||||
CreateTimerHandler get createTimer;
|
||||
CreatePeriodicTimerHandler get createPeriodicTimer;
|
||||
/// *Experimental*. Might disappear without warning.
|
||||
CreateTaskHandler get createTask;
|
||||
/// *Experimental*. Might disappear without warning.
|
||||
RunTaskHandler get runTask;
|
||||
PrintHandler get print;
|
||||
ForkHandler get fork;
|
||||
|
||||
// TODO(floitsch): deprecate once tasks are non-experimental.
|
||||
CreateTimerHandler get createTimer;
|
||||
// TODO(floitsch): deprecate once tasks are non-experimental.
|
||||
CreatePeriodicTimerHandler get createPeriodicTimer;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -172,10 +246,14 @@ class _ZoneSpecification implements ZoneSpecification {
|
|||
this.registerBinaryCallback: null,
|
||||
this.errorCallback: null,
|
||||
this.scheduleMicrotask: null,
|
||||
this.createTimer: null,
|
||||
this.createPeriodicTimer: null,
|
||||
this.createTask: null,
|
||||
this.runTask: null,
|
||||
this.print: null,
|
||||
this.fork: null
|
||||
this.fork: null,
|
||||
// TODO(floitsch): deprecate once tasks are non-experimental.
|
||||
this.createTimer: null,
|
||||
// TODO(floitsch): deprecate once tasks are non-experimental.
|
||||
this.createPeriodicTimer: null
|
||||
});
|
||||
|
||||
final HandleUncaughtErrorHandler handleUncaughtError;
|
||||
|
@ -187,10 +265,15 @@ class _ZoneSpecification implements ZoneSpecification {
|
|||
final RegisterBinaryCallbackHandler registerBinaryCallback;
|
||||
final ErrorCallbackHandler errorCallback;
|
||||
final ScheduleMicrotaskHandler scheduleMicrotask;
|
||||
final CreateTimerHandler createTimer;
|
||||
final CreatePeriodicTimerHandler createPeriodicTimer;
|
||||
final CreateTaskHandler createTask;
|
||||
final RunTaskHandler runTask;
|
||||
final PrintHandler print;
|
||||
final ForkHandler fork;
|
||||
|
||||
// TODO(floitsch): deprecate once tasks are non-experimental.
|
||||
final CreateTimerHandler createTimer;
|
||||
// TODO(floitsch): deprecate once tasks are non-experimental.
|
||||
final CreatePeriodicTimerHandler createPeriodicTimer;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -217,10 +300,23 @@ abstract class ZoneDelegate {
|
|||
Zone zone, /*=R*/ f(/*=T1*/ arg1, /*=T2*/ arg2));
|
||||
AsyncError errorCallback(Zone zone, Object error, StackTrace stackTrace);
|
||||
void scheduleMicrotask(Zone zone, void f());
|
||||
Timer createTimer(Zone zone, Duration duration, void f());
|
||||
Timer createPeriodicTimer(Zone zone, Duration period, void f(Timer timer));
|
||||
|
||||
/// *Experimental*. Might disappear without notice.
|
||||
Object/*=T*/ createTask/*<T, S extends TaskSpecification>*/(
|
||||
Zone zone, TaskCreate/*<T, S>*/ create,
|
||||
TaskSpecification/*=S*/ specification);
|
||||
/// *Experimental*. Might disappear without notice.
|
||||
void runTask/*<T, A>*/(
|
||||
Zone zone, TaskRun/*<T, A>*/ run, Object/*=T*/ task,
|
||||
Object/*=A*/ argument);
|
||||
|
||||
void print(Zone zone, String line);
|
||||
Zone fork(Zone zone, ZoneSpecification specification, Map zoneValues);
|
||||
|
||||
// TODO(floitsch): deprecate once tasks are non-experimental.
|
||||
Timer createTimer(Zone zone, Duration duration, void f());
|
||||
// TODO(floitsch): deprecate once tasks are non-experimental.
|
||||
Timer createPeriodicTimer(Zone zone, Duration period, void f(Timer timer));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -410,14 +506,101 @@ abstract class Zone {
|
|||
*/
|
||||
void scheduleMicrotask(void f());
|
||||
|
||||
/**
|
||||
* Creates a task in the current zone.
|
||||
*
|
||||
* A task represents an asynchronous operation or process that reports back
|
||||
* through the event loop.
|
||||
*
|
||||
* This function allows the zone to intercept the initialization of the
|
||||
* task while the [runTask] function is invoked when the task reports back.
|
||||
*
|
||||
* By default, in the root zone, the [create] function is invoked with the
|
||||
* [specification] as argument. It returns a task object which is used for all
|
||||
* future interactions between the zone and the task. The object is
|
||||
* a unique instance representing the task. It is generally returned to
|
||||
* whoever initiated the task.
|
||||
* For example, the HTML library uses the returned [StreamSubscription] as
|
||||
* task object when users register an event listener.
|
||||
*
|
||||
* Tasks are created when the program starts an operation that reports back
|
||||
* through the event loop. For example, a timer or an HTTP request both
|
||||
* return through the event loop and are therefore tasks.
|
||||
*
|
||||
* If the [create] function is not invoked (because a custom zone has
|
||||
* replaced or intercepted it), then the operation is *not* started. This
|
||||
* means that a custom zone can intercept tasks, like HTTP requests.
|
||||
*
|
||||
* A task goes through the following steps:
|
||||
* - a user invokes a library function that should eventually return through
|
||||
* the event loop.
|
||||
* - the library function creates a [TaskSpecification] that contains the
|
||||
* necessary information to start the operation, and invokes
|
||||
* `Zone.current.createTask` with the specification and a [create] closure.
|
||||
* The closure, when invoked, uses the specification to start the operation
|
||||
* (usually by interacting with the underlying system, or as a native
|
||||
* extension), and returns a task object that identifies the running task.
|
||||
* - custom zones handle the request and (unless completely intercepted and
|
||||
* aborted), end up calling the root zone's [createTask] which runs the
|
||||
* provided `create` closure, which may have been replaced at this point.
|
||||
* - later, the asynchronous operation returns through the event loop.
|
||||
* It invokes [Zone.runTask] on the zone in which the task should run
|
||||
* (and which was originally passed to the `create` function by
|
||||
* `createTask`). The [runTask] function receives the
|
||||
* task object, a `run` function and an argument. As before, custom zones
|
||||
* may intercept this call. Eventually (unless aborted), the `run` function
|
||||
* is invoked. This last step may happen multiple times for tasks that are
|
||||
* not oneshot tasks (see [ZoneSpecification.isOneShot]).
|
||||
*
|
||||
* Custom zones may replace the [specification] with a different one, thus
|
||||
* modifying the task parameters. An operation that wishes to be an
|
||||
* interceptable task must publicly specify the types that intercepting code
|
||||
* sees:
|
||||
* - The specification type (extending [TaskSpecification]) which holds the
|
||||
* information available when intercepting the `createTask` call.
|
||||
* - The task object type, returned by `createTask` and [create]. This object
|
||||
* may simply be typed as [Object].
|
||||
* - The argument type, if [runTask] takes a meaningful argument.
|
||||
*
|
||||
* *Experimental*. Might disappear without notice.
|
||||
*/
|
||||
Object/*=T*/ createTask/*<T, S extends TaskSpecification>*/(
|
||||
/*=T*/ create(TaskSpecification/*=S*/ specification, Zone zone),
|
||||
TaskSpecification/*=S*/ specification);
|
||||
|
||||
/**
|
||||
* Runs a task callback.
|
||||
*
|
||||
* This function is invoked, when an operation, started through [createTask],
|
||||
* generates an event.
|
||||
*
|
||||
* Generally, tasks schedule Dart code in the global event loop when the
|
||||
* [createTask] function is invoked. Since the
|
||||
* event loop does not expect any return value from the code it runs, the
|
||||
* [runTask] function is a void function.
|
||||
*
|
||||
* The [task] object must be the same as the one created with [createTask].
|
||||
*
|
||||
* It is good practice that task operations provide a meaningful [argument],
|
||||
* so that custom zones can interact with it. They might want to log or
|
||||
* replace the argument before calling the [run] function.
|
||||
*
|
||||
* *Experimental*. Might disappear without notice.
|
||||
*/
|
||||
void runTask/*<T, A>*/(
|
||||
/*=T*/ run(/*=T*/ task, /*=A*/ argument), Object/*=T*/ task,
|
||||
Object/*=A*/ argument);
|
||||
|
||||
/**
|
||||
* Creates a Timer where the callback is executed in this zone.
|
||||
*/
|
||||
// TODO(floitsch): deprecate once tasks are non-experimental.
|
||||
Timer createTimer(Duration duration, void callback());
|
||||
|
||||
/**
|
||||
* Creates a periodic Timer where the callback is executed in this zone.
|
||||
*/
|
||||
// TODO(floitsch): deprecate once tasks are non-experimental.
|
||||
Timer createPeriodicTimer(Duration period, void callback(Timer timer));
|
||||
|
||||
/**
|
||||
|
@ -523,7 +706,7 @@ class _ZoneDelegate implements ZoneDelegate {
|
|||
// TODO(floitsch): make this a generic method call on '<R>' once it's
|
||||
// supported. Remove the unnecessary cast.
|
||||
return handler(implZone, _parentDelegate(implZone), zone, f)
|
||||
as Object/*=ZoneCallback<R>*/;
|
||||
as dynamic/*=ZoneCallback<R>*/;
|
||||
}
|
||||
|
||||
ZoneUnaryCallback/*<R, T>*/ registerUnaryCallback/*<R, T>*/(
|
||||
|
@ -534,7 +717,7 @@ class _ZoneDelegate implements ZoneDelegate {
|
|||
// TODO(floitsch): make this a generic method call on '<R, T>' once it's
|
||||
// supported. Remove the unnecessary cast.
|
||||
return handler(implZone, _parentDelegate(implZone), zone, f)
|
||||
as Object/*=ZoneUnaryCallback<R, T>*/;
|
||||
as dynamic/*=ZoneUnaryCallback<R, T>*/;
|
||||
}
|
||||
|
||||
ZoneBinaryCallback/*<R, T1, T2>*/ registerBinaryCallback/*<R, T1, T2>*/(
|
||||
|
@ -545,7 +728,7 @@ class _ZoneDelegate implements ZoneDelegate {
|
|||
// TODO(floitsch): make this a generic method call on '<R, T1, T2>' once
|
||||
// it's supported. Remove the unnecessary cast.
|
||||
return handler(implZone, _parentDelegate(implZone), zone, f)
|
||||
as Object/*=ZoneBinaryCallback<R, T1, T2>*/;
|
||||
as dynamic/*=ZoneBinaryCallback<R, T1, T2>*/;
|
||||
}
|
||||
|
||||
AsyncError errorCallback(Zone zone, Object error, StackTrace stackTrace) {
|
||||
|
@ -564,18 +747,25 @@ class _ZoneDelegate implements ZoneDelegate {
|
|||
handler(implZone, _parentDelegate(implZone), zone, f);
|
||||
}
|
||||
|
||||
Timer createTimer(Zone zone, Duration duration, void f()) {
|
||||
var implementation = _delegationTarget._createTimer;
|
||||
Object/*=T*/ createTask/*<T, S extends TaskSpecification>*/(
|
||||
Zone zone, TaskCreate/*<T, S>*/ create, TaskSpecification/*=S*/ specification) {
|
||||
var implementation = _delegationTarget._createTask;
|
||||
_Zone implZone = implementation.zone;
|
||||
CreateTimerHandler handler = implementation.function;
|
||||
return handler(implZone, _parentDelegate(implZone), zone, duration, f);
|
||||
// TODO(floitsch): make the handler call a generic method call on '<T, S>'
|
||||
// once it's supported. Remove the unnecessary cast.
|
||||
var handler =
|
||||
implementation.function as CreateTaskHandler/*<T, S>*/;
|
||||
return handler(
|
||||
implZone, _parentDelegate(implZone), zone, create, specification);
|
||||
}
|
||||
|
||||
Timer createPeriodicTimer(Zone zone, Duration period, void f(Timer timer)) {
|
||||
var implementation = _delegationTarget._createPeriodicTimer;
|
||||
void runTask/*<T, A>*/(Zone zone, TaskRun run, Object /*=T*/ task,
|
||||
Object /*=A*/ argument) {
|
||||
var implementation = _delegationTarget._runTask;
|
||||
_Zone implZone = implementation.zone;
|
||||
CreatePeriodicTimerHandler handler = implementation.function;
|
||||
return handler(implZone, _parentDelegate(implZone), zone, period, f);
|
||||
RunTaskHandler handler = implementation.function;
|
||||
// TODO(floitsch): make this a generic call on '<T, A>'.
|
||||
handler(implZone, _parentDelegate(implZone), zone, run, task, argument);
|
||||
}
|
||||
|
||||
void print(Zone zone, String line) {
|
||||
|
@ -593,6 +783,22 @@ class _ZoneDelegate implements ZoneDelegate {
|
|||
return handler(
|
||||
implZone, _parentDelegate(implZone), zone, specification, zoneValues);
|
||||
}
|
||||
|
||||
// TODO(floitsch): deprecate once tasks are non-experimental.
|
||||
Timer createTimer(Zone zone, Duration duration, void f()) {
|
||||
var implementation = _delegationTarget._createTimer;
|
||||
_Zone implZone = implementation.zone;
|
||||
CreateTimerHandler handler = implementation.function;
|
||||
return handler(implZone, _parentDelegate(implZone), zone, duration, f);
|
||||
}
|
||||
|
||||
// TODO(floitsch): deprecate once tasks are non-experimental.
|
||||
Timer createPeriodicTimer(Zone zone, Duration period, void f(Timer timer)) {
|
||||
var implementation = _delegationTarget._createPeriodicTimer;
|
||||
_Zone implZone = implementation.zone;
|
||||
CreatePeriodicTimerHandler handler = implementation.function;
|
||||
return handler(implZone, _parentDelegate(implZone), zone, period, f);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -610,11 +816,17 @@ abstract class _Zone implements Zone {
|
|||
_ZoneFunction<RegisterBinaryCallbackHandler> get _registerBinaryCallback;
|
||||
_ZoneFunction<ErrorCallbackHandler> get _errorCallback;
|
||||
_ZoneFunction<ScheduleMicrotaskHandler> get _scheduleMicrotask;
|
||||
_ZoneFunction<CreateTimerHandler> get _createTimer;
|
||||
_ZoneFunction<CreatePeriodicTimerHandler> get _createPeriodicTimer;
|
||||
_ZoneFunction<CreateTaskHandler> get _createTask;
|
||||
_ZoneFunction<RunTaskHandler> get _runTask;
|
||||
_ZoneFunction<PrintHandler> get _print;
|
||||
_ZoneFunction<ForkHandler> get _fork;
|
||||
_ZoneFunction<HandleUncaughtErrorHandler> get _handleUncaughtError;
|
||||
|
||||
// TODO(floitsch): deprecate once tasks are non-experimental.
|
||||
_ZoneFunction<CreateTimerHandler> get _createTimer;
|
||||
// TODO(floitsch): deprecate once tasks are non-experimental.
|
||||
_ZoneFunction<CreatePeriodicTimerHandler> get _createPeriodicTimer;
|
||||
|
||||
_Zone get parent;
|
||||
ZoneDelegate get _delegate;
|
||||
Map get _map;
|
||||
|
@ -636,12 +848,17 @@ class _CustomZone extends _Zone {
|
|||
_ZoneFunction<RegisterBinaryCallbackHandler> _registerBinaryCallback;
|
||||
_ZoneFunction<ErrorCallbackHandler> _errorCallback;
|
||||
_ZoneFunction<ScheduleMicrotaskHandler> _scheduleMicrotask;
|
||||
_ZoneFunction<CreateTimerHandler> _createTimer;
|
||||
_ZoneFunction<CreatePeriodicTimerHandler> _createPeriodicTimer;
|
||||
_ZoneFunction<CreateTaskHandler> _createTask;
|
||||
_ZoneFunction<RunTaskHandler> _runTask;
|
||||
_ZoneFunction<PrintHandler> _print;
|
||||
_ZoneFunction<ForkHandler> _fork;
|
||||
_ZoneFunction<HandleUncaughtErrorHandler> _handleUncaughtError;
|
||||
|
||||
// TODO(floitsch): deprecate once tasks are non-experimental.
|
||||
_ZoneFunction<CreateTimerHandler> _createTimer;
|
||||
// TODO(floitsch): deprecate once tasks are non-experimental.
|
||||
_ZoneFunction<CreatePeriodicTimerHandler> _createPeriodicTimer;
|
||||
|
||||
// A cached delegate to this zone.
|
||||
ZoneDelegate _delegateCache;
|
||||
|
||||
|
@ -692,13 +909,14 @@ class _CustomZone extends _Zone {
|
|||
? new _ZoneFunction<ScheduleMicrotaskHandler>(
|
||||
this, specification.scheduleMicrotask)
|
||||
: parent._scheduleMicrotask;
|
||||
_createTimer = (specification.createTimer != null)
|
||||
? new _ZoneFunction<CreateTimerHandler>(this, specification.createTimer)
|
||||
: parent._createTimer;
|
||||
_createPeriodicTimer = (specification.createPeriodicTimer != null)
|
||||
? new _ZoneFunction<CreatePeriodicTimerHandler>(
|
||||
this, specification.createPeriodicTimer)
|
||||
: parent._createPeriodicTimer;
|
||||
_createTask = (specification.createTask != null)
|
||||
? new _ZoneFunction<CreateTaskHandler>(
|
||||
this, specification.createTask)
|
||||
: parent._createTask;
|
||||
_runTask = (specification.runTask != null)
|
||||
? new _ZoneFunction<RunTaskHandler>(
|
||||
this, specification.runTask)
|
||||
: parent._runTask;
|
||||
_print = (specification.print != null)
|
||||
? new _ZoneFunction<PrintHandler>(this, specification.print)
|
||||
: parent._print;
|
||||
|
@ -709,6 +927,16 @@ class _CustomZone extends _Zone {
|
|||
? new _ZoneFunction<HandleUncaughtErrorHandler>(
|
||||
this, specification.handleUncaughtError)
|
||||
: parent._handleUncaughtError;
|
||||
|
||||
// Deprecated fields, once tasks are non-experimental.
|
||||
_createTimer = (specification.createTimer != null)
|
||||
? new _ZoneFunction<CreateTimerHandler>(
|
||||
this, specification.createTimer)
|
||||
: parent._createTimer;
|
||||
_createPeriodicTimer = (specification.createPeriodicTimer != null)
|
||||
? new _ZoneFunction<CreatePeriodicTimerHandler>(
|
||||
this, specification.createPeriodicTimer)
|
||||
: parent._createPeriodicTimer;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -859,7 +1087,7 @@ class _CustomZone extends _Zone {
|
|||
// TODO(floitsch): make this a generic method call on '<R>' once it's
|
||||
// supported. Remove the unnecessary cast.
|
||||
return handler(implementation.zone, parentDelegate, this, callback)
|
||||
as Object/*=ZoneCallback<R>*/;
|
||||
as dynamic/*=ZoneCallback<R>*/;
|
||||
}
|
||||
|
||||
ZoneUnaryCallback/*<R, T>*/ registerUnaryCallback/*<R, T>*/(
|
||||
|
@ -871,7 +1099,7 @@ class _CustomZone extends _Zone {
|
|||
// TODO(floitsch): make this a generic method call on '<R, T>' once it's
|
||||
// supported. Remove the unnecessary cast.
|
||||
return handler(implementation.zone, parentDelegate, this, callback)
|
||||
as Object/*=ZoneUnaryCallback<R, T>*/;
|
||||
as dynamic/*=ZoneUnaryCallback<R, T>*/;
|
||||
}
|
||||
|
||||
ZoneBinaryCallback/*<R, T1, T2>*/ registerBinaryCallback/*<R, T1, T2>*/(
|
||||
|
@ -883,7 +1111,7 @@ class _CustomZone extends _Zone {
|
|||
// TODO(floitsch): make this a generic method call on '<R, T1, T2>' once
|
||||
// it's supported. Remove the unnecessary cast.
|
||||
return handler(implementation.zone, parentDelegate, this, callback)
|
||||
as Object/*=ZoneBinaryCallback<R, T1, T2>*/;
|
||||
as dynamic/*=ZoneBinaryCallback<R, T1, T2>*/;
|
||||
}
|
||||
|
||||
AsyncError errorCallback(Object error, StackTrace stackTrace) {
|
||||
|
@ -902,24 +1130,29 @@ class _CustomZone extends _Zone {
|
|||
assert(implementation != null);
|
||||
ZoneDelegate parentDelegate = _parentDelegate(implementation.zone);
|
||||
ScheduleMicrotaskHandler handler = implementation.function;
|
||||
return handler(implementation.zone, parentDelegate, this, f);
|
||||
handler(implementation.zone, parentDelegate, this, f);
|
||||
}
|
||||
|
||||
Timer createTimer(Duration duration, void f()) {
|
||||
var implementation = this._createTimer;
|
||||
assert(implementation != null);
|
||||
Object/*=T*/ createTask/*<T, S extends TaskSpecification>*/(
|
||||
TaskCreate/*<T, S>*/ create, TaskSpecification/*=S*/ specification) {
|
||||
var implementation = this._createTask;
|
||||
ZoneDelegate parentDelegate = _parentDelegate(implementation.zone);
|
||||
CreateTimerHandler handler = implementation.function;
|
||||
return handler(implementation.zone, parentDelegate, this, duration, f);
|
||||
}
|
||||
|
||||
Timer createPeriodicTimer(Duration duration, void f(Timer timer)) {
|
||||
var implementation = this._createPeriodicTimer;
|
||||
assert(implementation != null);
|
||||
ZoneDelegate parentDelegate = _parentDelegate(implementation.zone);
|
||||
CreatePeriodicTimerHandler handler = implementation.function;
|
||||
// TODO(floitsch): make the handler call a generic method call on '<T, S>'
|
||||
// once it's supported. Remove the unnecessary cast.
|
||||
var handler =
|
||||
implementation.function as CreateTaskHandler/*<T, S>*/;
|
||||
return handler(
|
||||
implementation.zone, parentDelegate, this, duration, f);
|
||||
implementation.zone, parentDelegate, this, create, specification);
|
||||
}
|
||||
|
||||
void runTask/*<T, A>*/(
|
||||
TaskRun/*<T, A>*/ run, Object/*=T*/ task, Object/*=A*/ arg1) {
|
||||
var implementation = this._runTask;
|
||||
ZoneDelegate parentDelegate = _parentDelegate(implementation.zone);
|
||||
RunTaskHandler handler = implementation.function;
|
||||
// TODO(floitsch): make this a generic method call on '<T, A>' once it's
|
||||
// supported.
|
||||
handler(implementation.zone, parentDelegate, this, run, task, arg1);
|
||||
}
|
||||
|
||||
void print(String line) {
|
||||
|
@ -929,6 +1162,25 @@ class _CustomZone extends _Zone {
|
|||
PrintHandler handler = implementation.function;
|
||||
return handler(implementation.zone, parentDelegate, this, line);
|
||||
}
|
||||
|
||||
// TODO(floitsch): deprecate once tasks are non-experimental.
|
||||
Timer createTimer(Duration duration, void f()) {
|
||||
var implementation = this._createTimer;
|
||||
assert(implementation != null);
|
||||
ZoneDelegate parentDelegate = _parentDelegate(implementation.zone);
|
||||
CreateTimerHandler handler = implementation.function;
|
||||
return handler(implementation.zone, parentDelegate, this, duration, f);
|
||||
}
|
||||
|
||||
// TODO(floitsch): deprecate once tasks are non-experimental.
|
||||
Timer createPeriodicTimer(Duration duration, void f(Timer timer)) {
|
||||
var implementation = this._createPeriodicTimer;
|
||||
assert(implementation != null);
|
||||
ZoneDelegate parentDelegate = _parentDelegate(implementation.zone);
|
||||
CreatePeriodicTimerHandler handler = implementation.function;
|
||||
return handler(
|
||||
implementation.zone, parentDelegate, this, duration, f);
|
||||
}
|
||||
}
|
||||
|
||||
/*=R*/ _rootHandleUncaughtError/*<R>*/(
|
||||
|
@ -1006,22 +1258,39 @@ void _rootScheduleMicrotask(Zone self, ZoneDelegate parent, Zone zone, f()) {
|
|||
_scheduleAsyncCallback(f);
|
||||
}
|
||||
|
||||
Object/*=T*/ _rootCreateTask/*<T, S extends TaskSpecification>*/(
|
||||
Zone self, ZoneDelegate parent, Zone zone,
|
||||
TaskCreate/*<T, S>*/ create, TaskSpecification/*=S*/ specification) {
|
||||
return create(specification, zone);
|
||||
}
|
||||
|
||||
void _rootRunTask/*<T, A>*/(
|
||||
Zone self, ZoneDelegate parent, Zone zone, TaskRun run/*<T, A>*/,
|
||||
Object/*=T*/ task, Object/*=A*/ arg) {
|
||||
if (Zone._current == zone) {
|
||||
run(task, arg);
|
||||
return;
|
||||
}
|
||||
|
||||
Zone old = Zone._enter(zone);
|
||||
try {
|
||||
run(task, arg);
|
||||
} catch (e, s) {
|
||||
zone.handleUncaughtError/*<dynamic>*/(e, s);
|
||||
} finally {
|
||||
Zone._leave(old);
|
||||
}
|
||||
}
|
||||
|
||||
Timer _rootCreateTimer(Zone self, ZoneDelegate parent, Zone zone,
|
||||
Duration duration, void callback()) {
|
||||
if (!identical(_ROOT_ZONE, zone)) {
|
||||
callback = zone.bindCallback(callback);
|
||||
}
|
||||
return Timer._createTimer(duration, callback);
|
||||
return new Timer._task(zone, duration, callback);
|
||||
}
|
||||
|
||||
Timer _rootCreatePeriodicTimer(
|
||||
Zone self, ZoneDelegate parent, Zone zone,
|
||||
Duration duration, void callback(Timer timer)) {
|
||||
if (!identical(_ROOT_ZONE, zone)) {
|
||||
// TODO(floitsch): the return type should be 'void'.
|
||||
callback = zone.bindUnaryCallback/*<dynamic, Timer>*/(callback);
|
||||
}
|
||||
return Timer._createPeriodicTimer(duration, callback);
|
||||
return new Timer._periodicTask(zone, duration, callback);
|
||||
}
|
||||
|
||||
void _rootPrint(Zone self, ZoneDelegate parent, Zone zone, String line) {
|
||||
|
@ -1082,10 +1351,10 @@ class _RootZone extends _Zone {
|
|||
_ZoneFunction<ScheduleMicrotaskHandler> get _scheduleMicrotask =>
|
||||
const _ZoneFunction<ScheduleMicrotaskHandler>(
|
||||
_ROOT_ZONE, _rootScheduleMicrotask);
|
||||
_ZoneFunction<CreateTimerHandler> get _createTimer =>
|
||||
const _ZoneFunction<CreateTimerHandler>(_ROOT_ZONE, _rootCreateTimer);
|
||||
_ZoneFunction<CreatePeriodicTimerHandler> get _createPeriodicTimer =>
|
||||
const _ZoneFunction<CreatePeriodicTimerHandler>(_ROOT_ZONE, _rootCreatePeriodicTimer);
|
||||
_ZoneFunction<CreateTaskHandler> get _createTask =>
|
||||
const _ZoneFunction<CreateTaskHandler>(_ROOT_ZONE, _rootCreateTask);
|
||||
_ZoneFunction<RunTaskHandler> get _runTask =>
|
||||
const _ZoneFunction<RunTaskHandler>(_ROOT_ZONE, _rootRunTask);
|
||||
_ZoneFunction<PrintHandler> get _print =>
|
||||
const _ZoneFunction<PrintHandler>(_ROOT_ZONE, _rootPrint);
|
||||
_ZoneFunction<ForkHandler> get _fork =>
|
||||
|
@ -1094,6 +1363,14 @@ class _RootZone extends _Zone {
|
|||
const _ZoneFunction<HandleUncaughtErrorHandler>(
|
||||
_ROOT_ZONE, _rootHandleUncaughtError);
|
||||
|
||||
// TODO(floitsch): deprecate once tasks are non-experimental.
|
||||
_ZoneFunction<CreateTimerHandler> get _createTimer =>
|
||||
const _ZoneFunction<CreateTimerHandler>(_ROOT_ZONE, _rootCreateTimer);
|
||||
// TODO(floitsch): deprecate once tasks are non-experimental.
|
||||
_ZoneFunction<CreatePeriodicTimerHandler> get _createPeriodicTimer =>
|
||||
const _ZoneFunction<CreatePeriodicTimerHandler>(
|
||||
_ROOT_ZONE, _rootCreatePeriodicTimer);
|
||||
|
||||
// The parent zone.
|
||||
_Zone get parent => null;
|
||||
|
||||
|
@ -1225,6 +1502,16 @@ class _RootZone extends _Zone {
|
|||
_rootScheduleMicrotask(null, null, this, f);
|
||||
}
|
||||
|
||||
Object/*=T*/ createTask/*<T, S extends TaskSpecification>*/(
|
||||
TaskCreate/*<T, S>*/ create, TaskSpecification/*=S*/ specification) {
|
||||
return _rootCreateTask/*<T, S>*/(null, null, this, create, specification);
|
||||
}
|
||||
|
||||
void runTask/*<T, A>*/(
|
||||
TaskRun/*<T, A>*/ run, Object/*=T*/ task, Object/*=A*/ arg) {
|
||||
_rootRunTask/*<T, A>*/(null, null, this, run, task, arg);
|
||||
}
|
||||
|
||||
Timer createTimer(Duration duration, void f()) {
|
||||
return Timer._createTimer(duration, f);
|
||||
}
|
||||
|
|
310
tests/lib/async/zone_task_test.dart
Normal file
310
tests/lib/async/zone_task_test.dart
Normal file
|
@ -0,0 +1,310 @@
|
|||
// Copyright (c) 2016, 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.
|
||||
|
||||
// Tests basic functionality of tasks in zones.
|
||||
|
||||
import 'package:expect/expect.dart';
|
||||
import 'package:async_helper/async_helper.dart';
|
||||
import 'dart:async';
|
||||
|
||||
List log = [];
|
||||
|
||||
class MySpecification extends TaskSpecification {
|
||||
final Function callback;
|
||||
final bool isOneShot;
|
||||
final int value;
|
||||
|
||||
MySpecification(void this.callback(), this.isOneShot, this.value);
|
||||
|
||||
String get name => "test.specification-name";
|
||||
}
|
||||
|
||||
class MyTask {
|
||||
final Zone zone;
|
||||
final Function callback;
|
||||
final int id;
|
||||
int invocationCount = 0;
|
||||
bool shouldStop = false;
|
||||
|
||||
MyTask(this.zone, void this.callback(), this.id);
|
||||
}
|
||||
|
||||
void runMyTask(MyTask task, int value) {
|
||||
log.add("running "
|
||||
"zone: ${Zone.current['name']} "
|
||||
"task-id: ${task.id} "
|
||||
"invocation-count: ${task.invocationCount} "
|
||||
"value: $value");
|
||||
task.callback();
|
||||
task.invocationCount++;
|
||||
}
|
||||
|
||||
MyTask createMyTask(MySpecification spec, Zone zone) {
|
||||
var task = new MyTask(zone, spec.callback, spec.value);
|
||||
log.add("creating task: ${spec.value} oneshot?: ${spec.isOneShot}");
|
||||
if (spec.isOneShot) {
|
||||
Timer.run(() {
|
||||
zone.runTask(runMyTask, task, task.id);
|
||||
});
|
||||
} else {
|
||||
new Timer.periodic(const Duration(milliseconds: 10), (Timer timer) {
|
||||
zone.runTask(runMyTask, task, task.id);
|
||||
if (task.shouldStop) {
|
||||
timer.cancel();
|
||||
}
|
||||
});
|
||||
}
|
||||
return task;
|
||||
}
|
||||
|
||||
MyTask startTask(f, bool oneShot, int value) {
|
||||
var spec = new MySpecification(f, oneShot, value);
|
||||
return Zone.current.createTask(createMyTask, spec);
|
||||
}
|
||||
|
||||
/// Makes sure things are working in a simple setting.
|
||||
/// No interceptions, changes, ...
|
||||
Future testCustomTask() {
|
||||
var testCompleter = new Completer();
|
||||
asyncStart();
|
||||
|
||||
Object createTaskHandler(Zone self, ZoneDelegate parent, Zone zone,
|
||||
TaskCreate create, TaskSpecification specification) {
|
||||
if (specification is MySpecification) {
|
||||
log.add("create enter "
|
||||
"zone: ${self['name']} "
|
||||
"spec-value: ${specification.value} "
|
||||
"spec-oneshot?: ${specification.isOneShot}");
|
||||
MyTask result = parent.createTask(zone, create, specification);
|
||||
log.add("create leave");
|
||||
return result;
|
||||
}
|
||||
return parent.createTask(zone, create, specification);
|
||||
}
|
||||
|
||||
void runTaskHandler(Zone self, ZoneDelegate parent, Zone zone, TaskRun run,
|
||||
Object task, Object arg) {
|
||||
if (task is MyTask) {
|
||||
log.add("run enter "
|
||||
"zone: ${self['name']} "
|
||||
"task-id: ${task.id} "
|
||||
"invocation-count: ${task.invocationCount} "
|
||||
"arg: $arg");
|
||||
parent.runTask(zone, run, task, arg);
|
||||
log.add("run leave invocation-count: ${task.invocationCount}");
|
||||
return;
|
||||
}
|
||||
parent.runTask(zone, run, task, arg);
|
||||
}
|
||||
|
||||
runZoned(() async {
|
||||
var completer0 = new Completer();
|
||||
startTask(() {
|
||||
completer0.complete("done");
|
||||
}, true, 0);
|
||||
await completer0.future;
|
||||
|
||||
Expect.listEquals([
|
||||
'create enter zone: custom zone spec-value: 0 spec-oneshot?: true',
|
||||
'creating task: 0 oneshot?: true',
|
||||
'create leave',
|
||||
'run enter zone: custom zone task-id: 0 invocation-count: 0 arg: 0',
|
||||
'running zone: custom zone task-id: 0 invocation-count: 0 value: 0',
|
||||
'run leave invocation-count: 1'
|
||||
], log);
|
||||
log.clear();
|
||||
|
||||
var completer1 = new Completer();
|
||||
MyTask task1;
|
||||
task1 = startTask(() {
|
||||
if (task1.invocationCount == 1) {
|
||||
task1.shouldStop = true;
|
||||
completer1.complete("done");
|
||||
}
|
||||
}, false, 1);
|
||||
await completer1.future;
|
||||
|
||||
Expect.listEquals([
|
||||
'create enter zone: custom zone spec-value: 1 spec-oneshot?: false',
|
||||
'creating task: 1 oneshot?: false',
|
||||
'create leave',
|
||||
'run enter zone: custom zone task-id: 1 invocation-count: 0 arg: 1',
|
||||
'running zone: custom zone task-id: 1 invocation-count: 0 value: 1',
|
||||
'run leave invocation-count: 1',
|
||||
'run enter zone: custom zone task-id: 1 invocation-count: 1 arg: 1',
|
||||
'running zone: custom zone task-id: 1 invocation-count: 1 value: 1',
|
||||
'run leave invocation-count: 2',
|
||||
], log);
|
||||
log.clear();
|
||||
|
||||
testCompleter.complete("done");
|
||||
asyncEnd();
|
||||
},
|
||||
zoneValues: {'name': 'custom zone'},
|
||||
zoneSpecification: new ZoneSpecification(
|
||||
createTask: createTaskHandler,
|
||||
runTask: runTaskHandler));
|
||||
|
||||
return testCompleter.future;
|
||||
}
|
||||
|
||||
/// More complicated zone, that intercepts...
|
||||
Future testCustomTask2() {
|
||||
var testCompleter = new Completer();
|
||||
asyncStart();
|
||||
|
||||
Object createTaskHandler(Zone self, ZoneDelegate parent, Zone zone,
|
||||
TaskCreate create, TaskSpecification specification) {
|
||||
if (specification is MySpecification) {
|
||||
log.add("create enter "
|
||||
"zone: ${self['name']} "
|
||||
"spec-value: ${specification.value} "
|
||||
"spec-oneshot?: ${specification.isOneShot}");
|
||||
var replacement = new MySpecification(specification.callback,
|
||||
specification.isOneShot, specification.value + 1);
|
||||
MyTask result = parent.createTask(zone, create, replacement);
|
||||
log.add("create leave");
|
||||
return result;
|
||||
}
|
||||
return parent.createTask(zone, create, specification);
|
||||
}
|
||||
|
||||
void runTaskHandler(Zone self, ZoneDelegate parent, Zone zone, TaskRun run,
|
||||
Object task, Object arg) {
|
||||
if (task is MyTask) {
|
||||
log.add("run enter "
|
||||
"zone: ${self['name']} "
|
||||
"task-id: ${task.id} "
|
||||
"invocation-count: ${task.invocationCount} "
|
||||
"arg: $arg");
|
||||
int value = arg;
|
||||
parent.runTask(zone, run, task, value + 101);
|
||||
log.add("run leave invocation-count: ${task.invocationCount}");
|
||||
return;
|
||||
}
|
||||
parent.runTask(zone, run, task, arg);
|
||||
}
|
||||
|
||||
runZoned(() async {
|
||||
var completer0 = new Completer();
|
||||
startTask(() {
|
||||
completer0.complete("done");
|
||||
}, true, 0);
|
||||
await completer0.future;
|
||||
|
||||
Expect.listEquals([
|
||||
'create enter zone: outer-zone spec-value: 0 spec-oneshot?: true',
|
||||
'creating task: 1 oneshot?: true',
|
||||
'create leave',
|
||||
'run enter zone: outer-zone task-id: 1 invocation-count: 0 arg: 1',
|
||||
'running zone: outer-zone task-id: 1 invocation-count: 0 value: 102',
|
||||
'run leave invocation-count: 1'
|
||||
], log);
|
||||
log.clear();
|
||||
|
||||
var completer1 = new Completer();
|
||||
MyTask task1;
|
||||
task1 = startTask(() {
|
||||
if (task1.invocationCount == 1) {
|
||||
task1.shouldStop = true;
|
||||
completer1.complete("done");
|
||||
}
|
||||
}, false, 1);
|
||||
await completer1.future;
|
||||
|
||||
Expect.listEquals([
|
||||
'create enter zone: outer-zone spec-value: 1 spec-oneshot?: false',
|
||||
'creating task: 2 oneshot?: false',
|
||||
'create leave',
|
||||
'run enter zone: outer-zone task-id: 2 invocation-count: 0 arg: 2',
|
||||
'running zone: outer-zone task-id: 2 invocation-count: 0 value: 103',
|
||||
'run leave invocation-count: 1',
|
||||
'run enter zone: outer-zone task-id: 2 invocation-count: 1 arg: 2',
|
||||
'running zone: outer-zone task-id: 2 invocation-count: 1 value: 103',
|
||||
'run leave invocation-count: 2',
|
||||
], log);
|
||||
log.clear();
|
||||
|
||||
var nestedCompleter = new Completer();
|
||||
|
||||
runZoned(() async {
|
||||
var completer0 = new Completer();
|
||||
startTask(() {
|
||||
completer0.complete("done");
|
||||
}, true, 0);
|
||||
await completer0.future;
|
||||
|
||||
Expect.listEquals([
|
||||
'create enter zone: inner-zone spec-value: 0 spec-oneshot?: true',
|
||||
'create enter zone: outer-zone spec-value: 1 spec-oneshot?: true',
|
||||
'creating task: 2 oneshot?: true',
|
||||
'create leave',
|
||||
'create leave',
|
||||
'run enter zone: inner-zone task-id: 2 invocation-count: 0 arg: 2',
|
||||
'run enter zone: outer-zone task-id: 2 invocation-count: 0 arg: 103',
|
||||
'running zone: inner-zone task-id: 2 invocation-count: 0 value: 204',
|
||||
'run leave invocation-count: 1',
|
||||
'run leave invocation-count: 1'
|
||||
], log);
|
||||
log.clear();
|
||||
|
||||
var completer1 = new Completer();
|
||||
MyTask task1;
|
||||
task1 = startTask(() {
|
||||
if (task1.invocationCount == 1) {
|
||||
task1.shouldStop = true;
|
||||
completer1.complete("done");
|
||||
}
|
||||
}, false, 1);
|
||||
await completer1.future;
|
||||
|
||||
Expect.listEquals([
|
||||
'create enter zone: inner-zone spec-value: 1 spec-oneshot?: false',
|
||||
'create enter zone: outer-zone spec-value: 2 spec-oneshot?: false',
|
||||
'creating task: 3 oneshot?: false',
|
||||
'create leave',
|
||||
'create leave',
|
||||
'run enter zone: inner-zone task-id: 3 invocation-count: 0 arg: 3',
|
||||
'run enter zone: outer-zone task-id: 3 invocation-count: 0 arg: 104',
|
||||
'running zone: inner-zone task-id: 3 invocation-count: 0 value: 205',
|
||||
'run leave invocation-count: 1',
|
||||
'run leave invocation-count: 1',
|
||||
'run enter zone: inner-zone task-id: 3 invocation-count: 1 arg: 3',
|
||||
'run enter zone: outer-zone task-id: 3 invocation-count: 1 arg: 104',
|
||||
'running zone: inner-zone task-id: 3 invocation-count: 1 value: 205',
|
||||
'run leave invocation-count: 2',
|
||||
'run leave invocation-count: 2',
|
||||
], log);
|
||||
log.clear();
|
||||
|
||||
nestedCompleter.complete("done");
|
||||
},
|
||||
zoneValues: {'name': 'inner-zone'},
|
||||
zoneSpecification: new ZoneSpecification(
|
||||
createTask: createTaskHandler,
|
||||
runTask: runTaskHandler));
|
||||
|
||||
await nestedCompleter.future;
|
||||
testCompleter.complete("done");
|
||||
asyncEnd();
|
||||
},
|
||||
zoneValues: {'name': 'outer-zone'},
|
||||
zoneSpecification: new ZoneSpecification(
|
||||
createTask: createTaskHandler,
|
||||
runTask: runTaskHandler));
|
||||
|
||||
return testCompleter.future;
|
||||
}
|
||||
|
||||
runTests() async {
|
||||
await testCustomTask();
|
||||
await testCustomTask2();
|
||||
}
|
||||
|
||||
main() {
|
||||
asyncStart();
|
||||
runTests().then((_) {
|
||||
asyncEnd();
|
||||
});
|
||||
}
|
515
tests/lib/async/zone_timer_task_test.dart
Normal file
515
tests/lib/async/zone_timer_task_test.dart
Normal file
|
@ -0,0 +1,515 @@
|
|||
// Copyright (c) 2016, 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.
|
||||
|
||||
// Tests timer tasks.
|
||||
|
||||
import 'package:expect/expect.dart';
|
||||
import 'package:async_helper/async_helper.dart';
|
||||
import 'dart:async';
|
||||
import 'dart:collection';
|
||||
|
||||
class MyTimerSpecification implements SingleShotTimerTaskSpecification {
|
||||
final Function callback;
|
||||
final Duration duration;
|
||||
|
||||
MyTimerSpecification(this.callback, this.duration);
|
||||
|
||||
bool get isOneShot => true;
|
||||
String get name => "test.timer-override";
|
||||
}
|
||||
|
||||
class MyPeriodicTimerSpecification implements PeriodicTimerTaskSpecification {
|
||||
final Function callback;
|
||||
final Duration duration;
|
||||
|
||||
MyPeriodicTimerSpecification(this.callback, this.duration);
|
||||
|
||||
bool get isOneShot => true;
|
||||
String get name => "test.periodic-timer-override";
|
||||
}
|
||||
|
||||
/// Makes sure things are working in a simple setting.
|
||||
/// No interceptions, changes, ...
|
||||
Future testTimerTask() {
|
||||
List log = [];
|
||||
|
||||
var testCompleter = new Completer();
|
||||
asyncStart();
|
||||
|
||||
int taskIdCounter = 0;
|
||||
|
||||
Object createTaskHandler(Zone self, ZoneDelegate parent, Zone zone,
|
||||
TaskCreate create, TaskSpecification specification) {
|
||||
var taskMap = self['taskMap'];
|
||||
var taskIdMap = self['taskIdMap'];
|
||||
if (specification is SingleShotTimerTaskSpecification) {
|
||||
log.add("create enter "
|
||||
"zone: ${self['name']} "
|
||||
"spec-duration: ${specification.duration} "
|
||||
"spec-oneshot?: ${specification.isOneShot}");
|
||||
var result = parent.createTask(zone, create, specification);
|
||||
taskMap[result] = specification;
|
||||
taskIdMap[specification] = taskIdCounter++;
|
||||
log.add("create leave");
|
||||
return result;
|
||||
} else if (specification is PeriodicTimerTaskSpecification) {
|
||||
log.add("create enter "
|
||||
"zone: ${self['name']} "
|
||||
"spec-duration: ${specification.duration} "
|
||||
"spec-oneshot?: ${specification.isOneShot}");
|
||||
var result = parent.createTask(zone, create, specification);
|
||||
taskMap[result] = specification;
|
||||
taskIdMap[specification] = taskIdCounter++;
|
||||
log.add("create leave");
|
||||
return result;
|
||||
}
|
||||
return parent.createTask(zone, create, specification);
|
||||
}
|
||||
|
||||
void runTaskHandler(Zone self, ZoneDelegate parent, Zone zone, TaskRun run,
|
||||
Object task, Object arg) {
|
||||
var taskMap = self['taskMap'];
|
||||
var taskIdMap = self['taskIdMap'];
|
||||
if (taskMap.containsKey(task)) {
|
||||
var spec = taskMap[task];
|
||||
log.add("run enter "
|
||||
"zone: ${self['name']} "
|
||||
"task-id: ${taskIdMap[spec]} "
|
||||
"arg: $arg");
|
||||
parent.runTask(zone, run, task, arg);
|
||||
log.add("run leave");
|
||||
return;
|
||||
}
|
||||
parent.runTask(zone, run, task, arg);
|
||||
}
|
||||
|
||||
runZoned(() async {
|
||||
var completer0 = new Completer();
|
||||
Timer.run(() {
|
||||
completer0.complete("done");
|
||||
});
|
||||
await completer0.future;
|
||||
|
||||
Expect.listEquals([
|
||||
'create enter zone: custom zone spec-duration: 0:00:00.000000 '
|
||||
'spec-oneshot?: true',
|
||||
'create leave',
|
||||
'run enter zone: custom zone task-id: 0 arg: null',
|
||||
'run leave'
|
||||
], log);
|
||||
log.clear();
|
||||
|
||||
var completer1 = new Completer();
|
||||
var counter1 = 0;
|
||||
new Timer.periodic(const Duration(milliseconds: 5), (Timer timer) {
|
||||
if (counter1++ > 1) {
|
||||
timer.cancel();
|
||||
completer1.complete("done");
|
||||
}
|
||||
});
|
||||
await completer1.future;
|
||||
|
||||
Expect.listEquals([
|
||||
'create enter zone: custom zone spec-duration: 0:00:00.005000 '
|
||||
'spec-oneshot?: false',
|
||||
'create leave',
|
||||
'run enter zone: custom zone task-id: 1 arg: null',
|
||||
'run leave',
|
||||
'run enter zone: custom zone task-id: 1 arg: null',
|
||||
'run leave',
|
||||
'run enter zone: custom zone task-id: 1 arg: null',
|
||||
'run leave'
|
||||
], log);
|
||||
log.clear();
|
||||
|
||||
testCompleter.complete("done");
|
||||
asyncEnd();
|
||||
},
|
||||
zoneValues: {'name': 'custom zone', 'taskMap': {}, 'taskIdMap': {}},
|
||||
zoneSpecification: new ZoneSpecification(
|
||||
createTask: createTaskHandler,
|
||||
runTask: runTaskHandler));
|
||||
|
||||
return testCompleter.future;
|
||||
}
|
||||
|
||||
/// More complicated zone, that intercepts...
|
||||
Future testTimerTask2() {
|
||||
List log = [];
|
||||
|
||||
var testCompleter = new Completer();
|
||||
asyncStart();
|
||||
|
||||
int taskIdCounter = 0;
|
||||
|
||||
Object createTaskHandler(Zone self, ZoneDelegate parent, Zone zone,
|
||||
TaskCreate create, TaskSpecification specification) {
|
||||
var taskMap = self['taskMap'];
|
||||
var taskIdMap = self['taskIdMap'];
|
||||
if (specification is SingleShotTimerTaskSpecification) {
|
||||
log.add("create enter "
|
||||
"zone: ${self['name']} "
|
||||
"spec-duration: ${specification.duration} "
|
||||
"spec-oneshot?: ${specification.isOneShot}");
|
||||
var mySpec = new MyTimerSpecification(specification.callback,
|
||||
specification.duration + const Duration(milliseconds: 2));
|
||||
var result = parent.createTask(zone, create, mySpec);
|
||||
taskMap[result] = specification;
|
||||
taskIdMap[specification] = taskIdCounter++;
|
||||
log.add("create leave");
|
||||
return result;
|
||||
} else if (specification is PeriodicTimerTaskSpecification) {
|
||||
log.add("create enter "
|
||||
"zone: ${self['name']} "
|
||||
"spec-duration: ${specification.duration} "
|
||||
"spec-oneshot?: ${specification.isOneShot}");
|
||||
var mySpec = new MyPeriodicTimerSpecification(specification.callback,
|
||||
specification.duration + const Duration(milliseconds: 2));
|
||||
var result = parent.createTask(zone, create, specification);
|
||||
taskMap[result] = specification;
|
||||
taskIdMap[specification] = taskIdCounter++;
|
||||
log.add("create leave");
|
||||
return result;
|
||||
}
|
||||
return parent.createTask(zone, create, specification);
|
||||
}
|
||||
|
||||
void runTaskHandler(Zone self, ZoneDelegate parent, Zone zone, TaskRun run,
|
||||
Object task, Object arg) {
|
||||
var taskMap = self['taskMap'];
|
||||
var taskIdMap = self['taskIdMap'];
|
||||
if (taskMap.containsKey(task)) {
|
||||
var spec = taskMap[task];
|
||||
log.add("run enter "
|
||||
"zone: ${self['name']} "
|
||||
"task-id: ${taskIdMap[spec]} "
|
||||
"arg: $arg");
|
||||
parent.runTask(zone, run, task, arg);
|
||||
log.add("run leave");
|
||||
return;
|
||||
}
|
||||
parent.runTask(zone, run, task, arg);
|
||||
}
|
||||
|
||||
runZoned(() async {
|
||||
var completer0 = new Completer();
|
||||
Timer.run(() {
|
||||
completer0.complete("done");
|
||||
});
|
||||
await completer0.future;
|
||||
|
||||
// No visible change (except for the zone name) in the log, compared to the
|
||||
// simple invocations.
|
||||
Expect.listEquals([
|
||||
'create enter zone: outer-zone spec-duration: 0:00:00.000000 '
|
||||
'spec-oneshot?: true',
|
||||
'create leave',
|
||||
'run enter zone: outer-zone task-id: 0 arg: null',
|
||||
'run leave'
|
||||
], log);
|
||||
log.clear();
|
||||
|
||||
var completer1 = new Completer();
|
||||
var counter1 = 0;
|
||||
new Timer.periodic(const Duration(milliseconds: 5), (Timer timer) {
|
||||
if (counter1++ > 1) {
|
||||
timer.cancel();
|
||||
completer1.complete("done");
|
||||
}
|
||||
});
|
||||
await completer1.future;
|
||||
|
||||
// No visible change (except for the zone nome) in the log, compared to the
|
||||
// simple invocations.
|
||||
Expect.listEquals([
|
||||
'create enter zone: outer-zone spec-duration: 0:00:00.005000 '
|
||||
'spec-oneshot?: false',
|
||||
'create leave',
|
||||
'run enter zone: outer-zone task-id: 1 arg: null',
|
||||
'run leave',
|
||||
'run enter zone: outer-zone task-id: 1 arg: null',
|
||||
'run leave',
|
||||
'run enter zone: outer-zone task-id: 1 arg: null',
|
||||
'run leave'
|
||||
], log);
|
||||
log.clear();
|
||||
|
||||
var nestedCompleter = new Completer();
|
||||
|
||||
runZoned(() async {
|
||||
var completer0 = new Completer();
|
||||
Timer.run(() {
|
||||
completer0.complete("done");
|
||||
});
|
||||
await completer0.future;
|
||||
|
||||
// The outer zone sees the duration change of the inner zone.
|
||||
Expect.listEquals([
|
||||
'create enter zone: inner-zone spec-duration: 0:00:00.000000 '
|
||||
'spec-oneshot?: true',
|
||||
'create enter zone: outer-zone spec-duration: 0:00:00.002000 '
|
||||
'spec-oneshot?: true',
|
||||
'create leave',
|
||||
'create leave',
|
||||
'run enter zone: inner-zone task-id: 3 arg: null',
|
||||
'run enter zone: outer-zone task-id: 2 arg: null',
|
||||
'run leave',
|
||||
'run leave'
|
||||
], log);
|
||||
log.clear();
|
||||
|
||||
var completer1 = new Completer();
|
||||
var counter1 = 0;
|
||||
new Timer.periodic(const Duration(milliseconds: 5), (Timer timer) {
|
||||
if (counter1++ > 1) {
|
||||
timer.cancel();
|
||||
completer1.complete("done");
|
||||
}
|
||||
});
|
||||
await completer1.future;
|
||||
|
||||
// The outer zone sees the duration change of the inner zone.
|
||||
Expect.listEquals([
|
||||
'create enter zone: inner-zone spec-duration: 0:00:00.005000 '
|
||||
'spec-oneshot?: false',
|
||||
'create enter zone: outer-zone spec-duration: 0:00:00.005000 '
|
||||
'spec-oneshot?: false',
|
||||
'create leave',
|
||||
'create leave',
|
||||
'run enter zone: inner-zone task-id: 5 arg: null',
|
||||
'run enter zone: outer-zone task-id: 4 arg: null',
|
||||
'run leave',
|
||||
'run leave',
|
||||
'run enter zone: inner-zone task-id: 5 arg: null',
|
||||
'run enter zone: outer-zone task-id: 4 arg: null',
|
||||
'run leave',
|
||||
'run leave',
|
||||
'run enter zone: inner-zone task-id: 5 arg: null',
|
||||
'run enter zone: outer-zone task-id: 4 arg: null',
|
||||
'run leave',
|
||||
'run leave'
|
||||
], log);
|
||||
log.clear();
|
||||
|
||||
nestedCompleter.complete("done");
|
||||
},
|
||||
zoneValues: {'name': 'inner-zone', 'taskMap': {}, 'taskIdMap': {}},
|
||||
zoneSpecification: new ZoneSpecification(
|
||||
createTask: createTaskHandler,
|
||||
runTask: runTaskHandler));
|
||||
|
||||
await nestedCompleter.future;
|
||||
testCompleter.complete("done");
|
||||
asyncEnd();
|
||||
},
|
||||
zoneValues: {'name': 'outer-zone', 'taskMap': {}, 'taskIdMap': {}},
|
||||
zoneSpecification: new ZoneSpecification(
|
||||
createTask: createTaskHandler,
|
||||
runTask: runTaskHandler));
|
||||
|
||||
return testCompleter.future;
|
||||
}
|
||||
|
||||
class TimerEntry {
|
||||
final int time;
|
||||
final SimulatedTimer timer;
|
||||
|
||||
TimerEntry(this.time, this.timer);
|
||||
}
|
||||
|
||||
class SimulatedTimer implements Timer {
|
||||
static int _idCounter = 0;
|
||||
|
||||
Zone _zone;
|
||||
final int _id = _idCounter++;
|
||||
final Duration _duration;
|
||||
final Function _callback;
|
||||
final bool _isPeriodic;
|
||||
bool _isActive = true;
|
||||
|
||||
SimulatedTimer(this._zone, this._duration, this._callback, this._isPeriodic);
|
||||
|
||||
bool get isActive => _isActive;
|
||||
|
||||
void cancel() {
|
||||
_isActive = false;
|
||||
}
|
||||
|
||||
void _run() {
|
||||
if (!isActive) return;
|
||||
_zone.runTask(_runTimer, this, null);
|
||||
}
|
||||
|
||||
static void _runTimer(SimulatedTimer timer, _) {
|
||||
if (timer._isPeriodic) {
|
||||
timer._callback(timer);
|
||||
} else {
|
||||
timer._callback();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
testSimulatedTimer() {
|
||||
List log = [];
|
||||
|
||||
var currentTime = 0;
|
||||
// Using a simple list as queue. Not very efficient, but the test has only
|
||||
// very few timers running at the same time.
|
||||
var queue = new DoubleLinkedQueue<TimerEntry>();
|
||||
|
||||
// Schedules the given callback at now + duration.
|
||||
void schedule(int scheduledTime, SimulatedTimer timer) {
|
||||
log.add("scheduling timer ${timer._id} for $scheduledTime");
|
||||
if (queue.isEmpty) {
|
||||
queue.add(new TimerEntry(scheduledTime, timer));
|
||||
} else {
|
||||
DoubleLinkedQueueEntry current = queue.firstEntry();
|
||||
while (current != null) {
|
||||
if (current.element.time <= scheduledTime) {
|
||||
current = current.nextEntry();
|
||||
} else {
|
||||
current.prepend(new TimerEntry(scheduledTime, timer));
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (current == null) {
|
||||
queue.add(new TimerEntry(scheduledTime, timer));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void runQueue() {
|
||||
while (queue.isNotEmpty) {
|
||||
var item = queue.removeFirst();
|
||||
// If multiple callbacks were scheduled at the same time, increment the
|
||||
// current time instead of staying at the same time.
|
||||
currentTime = item.time > currentTime ? item.time : currentTime + 1;
|
||||
SimulatedTimer timer = item.timer;
|
||||
log.add("running timer ${timer._id} at $currentTime "
|
||||
"(active?: ${timer.isActive})");
|
||||
if (!timer.isActive) continue;
|
||||
if (timer._isPeriodic) {
|
||||
schedule(currentTime + timer._duration.inMilliseconds, timer);
|
||||
}
|
||||
item.timer._run();
|
||||
}
|
||||
}
|
||||
|
||||
SimulatedTimer createSimulatedOneShotTimer(
|
||||
SingleShotTimerTaskSpecification spec, Zone zone) {
|
||||
var timer = new SimulatedTimer(zone, spec.duration, spec.callback, false);
|
||||
schedule(currentTime + spec.duration.inMilliseconds, timer);
|
||||
return timer;
|
||||
}
|
||||
|
||||
SimulatedTimer createSimulatedPeriodicTimer(
|
||||
PeriodicTimerTaskSpecification spec, Zone zone) {
|
||||
var timer = new SimulatedTimer(zone, spec.duration, spec.callback, true);
|
||||
schedule(currentTime + spec.duration.inMilliseconds, timer);
|
||||
return timer;
|
||||
}
|
||||
|
||||
Object createSimulatedTaskHandler(Zone self, ZoneDelegate parent, Zone zone,
|
||||
TaskCreate create, TaskSpecification specification) {
|
||||
var taskMap = self['taskMap'];
|
||||
var taskIdMap = self['taskIdMap'];
|
||||
if (specification is SingleShotTimerTaskSpecification) {
|
||||
log.add("create enter "
|
||||
"zone: ${self['name']} "
|
||||
"spec-duration: ${specification.duration} "
|
||||
"spec-oneshot?: ${specification.isOneShot}");
|
||||
var result =
|
||||
parent.createTask(zone, createSimulatedOneShotTimer, specification);
|
||||
log.add("create leave");
|
||||
return result;
|
||||
}
|
||||
if (specification is PeriodicTimerTaskSpecification) {
|
||||
log.add("create enter "
|
||||
"zone: ${self['name']} "
|
||||
"spec-duration: ${specification.duration} "
|
||||
"spec-oneshot?: ${specification.isOneShot}");
|
||||
var result =
|
||||
parent.createTask(zone, createSimulatedPeriodicTimer, specification);
|
||||
log.add("create leave");
|
||||
return result;
|
||||
}
|
||||
return parent.createTask(zone, create, specification);
|
||||
}
|
||||
|
||||
runZoned(() {
|
||||
Timer.run(() {
|
||||
log.add("running Timer.run");
|
||||
});
|
||||
|
||||
var timer0;
|
||||
|
||||
new Timer(const Duration(milliseconds: 10), () {
|
||||
log.add("running Timer(10)");
|
||||
timer0.cancel();
|
||||
log.add("canceled timer0");
|
||||
});
|
||||
|
||||
timer0 = new Timer(const Duration(milliseconds: 15), () {
|
||||
log.add("running Timer(15)");
|
||||
});
|
||||
|
||||
var counter1 = 0;
|
||||
new Timer.periodic(const Duration(milliseconds: 5), (Timer timer) {
|
||||
log.add("running periodic timer $counter1");
|
||||
if (counter1++ > 1) {
|
||||
timer.cancel();
|
||||
}
|
||||
});
|
||||
},
|
||||
zoneSpecification:
|
||||
new ZoneSpecification(createTask: createSimulatedTaskHandler));
|
||||
|
||||
runQueue();
|
||||
|
||||
Expect.listEquals([
|
||||
'create enter zone: null spec-duration: 0:00:00.000000 spec-oneshot?: true',
|
||||
'scheduling timer 0 for 0',
|
||||
'create leave',
|
||||
'create enter zone: null spec-duration: 0:00:00.010000 spec-oneshot?: true',
|
||||
'scheduling timer 1 for 10',
|
||||
'create leave',
|
||||
'create enter zone: null spec-duration: 0:00:00.015000 spec-oneshot?: true',
|
||||
'scheduling timer 2 for 15',
|
||||
'create leave',
|
||||
'create enter zone: null spec-duration: 0:00:00.005000 '
|
||||
'spec-oneshot?: false',
|
||||
'scheduling timer 3 for 5',
|
||||
'create leave',
|
||||
'running timer 0 at 1 (active?: true)',
|
||||
'running Timer.run',
|
||||
'running timer 3 at 5 (active?: true)',
|
||||
'scheduling timer 3 for 10',
|
||||
'running periodic timer 0',
|
||||
'running timer 1 at 10 (active?: true)',
|
||||
'running Timer(10)',
|
||||
'canceled timer0',
|
||||
'running timer 3 at 11 (active?: true)',
|
||||
'scheduling timer 3 for 16',
|
||||
'running periodic timer 1',
|
||||
'running timer 2 at 15 (active?: false)',
|
||||
'running timer 3 at 16 (active?: true)',
|
||||
'scheduling timer 3 for 21',
|
||||
'running periodic timer 2',
|
||||
'running timer 3 at 21 (active?: false)'
|
||||
], log);
|
||||
log.clear();
|
||||
}
|
||||
|
||||
runTests() async {
|
||||
await testTimerTask();
|
||||
await testTimerTask2();
|
||||
testSimulatedTimer();
|
||||
}
|
||||
|
||||
main() {
|
||||
asyncStart();
|
||||
runTests().then((_) {
|
||||
asyncEnd();
|
||||
});
|
||||
}
|
Loading…
Reference in a new issue