blob: 2538d51b88458aaf7522fa94751ebbd955003698 [file] [log] [blame]
// Copyright (c) 2012, 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.
/**
* Concurrent programming using _isolates_:
* independent workers that are similar to threads
* but don't share memory,
* communicating only via messages.
*
* To use this library in your code:
*
* import 'dart:isolate';
*/
library dart.isolate;
import "dart:async";
import "dart:collection" show HashMap;
import "dart:_internal" hide Symbol;
part "capability.dart";
/**
* Thrown when an isolate cannot be created.
*/
class IsolateSpawnException implements Exception {
/** Error message reported by the spawn operation. */
final String message;
IsolateSpawnException(this.message);
String toString() => "IsolateSpawnException: $message";
}
/**
* An isolated Dart execution context.
*
* All Dart code runs in an isolate, and code can access classes and values
* only from the same isolate. Different isolates can communicate by sending
* values through ports (see [ReceivePort], [SendPort]).
*
* An `Isolate` object is a reference to an isolate, usually different from
* the current isolate.
* It represents, and can be used control, the other isolate.
*
* When spawning a new isolate, the spawning isolate receives an `Isolate`
* object representing the new isolate when the spawn operation succeeds.
*
* Isolates run code in its own event loop, and each event may run smaller tasks
* in a nested microtask queue.
*
* An `Isolate` object allows other isolates to control the event loop
* of the isolate that it represents, and to inspect the isolate,
* for example by pausing the isolate or by getting events when the isolate
* has an uncaught error.
*
* The [controlPort] identifies and gives access to controlling the isolate,
* and the [pauseCapability] and [terminateCapability] guard access
* to some control operations.
* For example, calling [pause] on an `Isolate` object created without a
* [pauseCapability], has no effect.
*
* The `Isolate` object provided by a spawn operation will have the
* control port and capabilities needed to control the isolate.
* New isolate objects can be created without some of these capabilities
* if necessary, using the [Isolate.Isolate] constructor.
*
* An `Isolate` object cannot be sent over a `SendPort`, but the control port
* and capabilities can be sent, and can be used to create a new functioning
* `Isolate` object in the receiving port's isolate.
*/
class Isolate {
/** Argument to `ping` and `kill`: Ask for immediate action. */
static const int IMMEDIATE = 0;
/** Argument to `ping` and `kill`: Ask for action before the next event. */
static const int BEFORE_NEXT_EVENT = 1;
/**
* Control port used to send control messages to the isolate.
*
* The control port identifies the isolate.
*
* An `Isolate` object allows sending control messages
* through the control port.
*
* Some control messages require a specific capability to be passed along
* with the message (see [pauseCapability] and [terminateCapability]),
* otherwise the message is ignored by the isolate.
*/
final SendPort controlPort;
/**
* Capability granting the ability to pause the isolate.
*
* This capability is required by [pause].
* If the capability is `null`, or if it is not the correct pause capability
* of the isolate identified by [controlPort],
* then calls to [pause] will have no effect.
*
* If the isolate is spawned in a paused state, use this capability as
* argument to the [resume] method in order to resume the paused isolate.
*/
final Capability pauseCapability;
/**
* Capability granting the ability to terminate the isolate.
*
* This capability is required by [kill] and [setErrorsFatal].
* If the capability is `null`, or if it is not the correct termination
* capability of the isolate identified by [controlPort],
* then calls to those methods will have no effect.
*/
final Capability terminateCapability;
/**
* Create a new [Isolate] object with a restricted set of capabilities.
*
* The port should be a control port for an isolate, as taken from
* another `Isolate` object.
*
* The capabilities should be the subset of the capabilities that are
* available to the original isolate.
* Capabilities of an isolate are locked to that isolate, and have no effect
* anywhere else, so the capabilities should come from the same isolate as
* the control port.
*
* Can also be used to create an [Isolate] object from a control port, and
* any available capabilities, that have been sent through a [SendPort].
*
* Example:
* ```dart
* Isolate isolate = findSomeIsolate();
* Isolate restrictedIsolate = new Isolate(isolate.controlPort);
* untrustedCode(restrictedIsolate);
* ```
* This example creates a new `Isolate` object that cannot be used to
* pause or terminate the isolate. All the untrusted code can do is to
* inspect the isolate and see uncaught errors or when it terminates.
*/
Isolate(this.controlPort, {this.pauseCapability, this.terminateCapability});
/**
* Return an [Isolate] object representing the current isolate.
*
* The current isolate for code using [current]
* is the isolate running the code.
*
* The isolate object provides the capabilities required to inspect,
* pause or kill the isolate, and allows granting these capabilities
* to others.
*
* It is possible to pause the current isolate, but doing so *without*
* first passing the ability to resume it again to another isolate,
* is a sure way to hang your program.
*/
static Isolate get current => _currentIsolate;
/**
* Returns the package root of the current isolate, if any.
*
* If the isolate is using a [packageConfig] or the isolate has not been
* setup for package resolution, this getter returns `null`, otherwise it
* returns the package root - a directory that package URIs are resolved
* against.
*/
static Future<Uri> get packageRoot {
var hook = VMLibraryHooks.packageRootUriFuture;
if (hook == null) {
throw new UnsupportedError("Isolate.packageRoot");
}
return hook();
}
/**
* Returns the package root of the current isolate, if any.
*
* If the isolate is using a [packageRoot] or the isolate has not been
* setup for package resolution, this getter returns `null`, otherwise it
* returns the package config URI.
*/
static Future<Uri> get packageConfig {
var hook = VMLibraryHooks.packageConfigUriFuture;
if (hook == null) {
throw new UnsupportedError("Isolate.packageConfig");
}
return hook();
}
/**
* Maps a package: URI to a non-package Uri.
*
* If there is no valid mapping from the package: URI in the current
* isolate, then this call returns `null`. Non-package: URIs are
* returned unmodified.
*/
static Future<Uri> resolvePackageUri(Uri packageUri) {
var hook = VMLibraryHooks.resolvePackageUriFuture;
if (hook == null) {
throw new UnsupportedError("Isolate.resolvePackageUri");
}
return hook(packageUri);
}
/**
* Creates and spawns an isolate that shares the same code as the current
* isolate.
*
* The argument [entryPoint] specifies the initial function to call
* in the spawned isolate.
* The entry-point function is invoked in the new isolate with [message]
* as the only argument.
*
* The function must be a top-level function or a static method
* that can be called with a single argument,
* that is, a compile-time constant function value
* which accepts at least one positional parameter
* and has at most one required positional parameter.
* The function may accept any number of optional parameters,
* as long as it *can* be called with just a single argument.
* The function must not be the value of a function expression
* or an instance method tear-off.
*
* Usually the initial [message] contains a [SendPort] so
* that the spawner and spawnee can communicate with each other.
*
* If the [paused] parameter is set to `true`,
* the isolate will start up in a paused state,
* just before calling the [entryPoint] function with the [message],
* as if by an initial call of `isolate.pause(isolate.pauseCapability)`.
* To resume the isolate, call `isolate.resume(isolate.pauseCapability)`.
*
* If the [errorAreFatal], [onExit] and/or [onError] parameters are provided,
* the isolate will act as if, respectively, [setErrorsFatal],
* [addOnExitListener] and [addErrorListener] were called with the
* corresponding parameter and was processed before the isolate starts
* running.
*
* If [errorsAreFatal] is omitted, the platform may choose a default behavior
* or inherit the current isolate's behavior.
*
* You can also call the [setErrorsFatal], [addOnExitListener] and
* [addErrorListener] methods on the returned isolate, but unless the
* isolate was started as [paused], it may already have terminated
* before those methods can complete.
*
* Returns a future which will complete with an [Isolate] instance if the
* spawning succeeded. It will complete with an error otherwise.
*/
static Future<Isolate> spawn(void entryPoint(message), var message,
{bool paused: false,
bool errorsAreFatal,
SendPort onExit,
SendPort onError}) async {
// `paused` isn't handled yet.
RawReceivePort readyPort;
try {
// Check for the type of `entryPoint` on the spawning isolate to make
// error-handling easier.
if (entryPoint is! _UnaryFunction) {
throw new ArgumentError(entryPoint);
}
// The VM will invoke [_startIsolate] with entryPoint as argument.
readyPort = new RawReceivePort();
// We do not inherit the package root or package config settings
// from the parent isolate, instead we use the values that were
// set on the command line.
var packageRoot = VMLibraryHooks.packageRootString;
var packageConfig = VMLibraryHooks.packageConfigString;
var script = VMLibraryHooks.platformScript;
if (script == null) {
// We do not have enough information to support spawning the new
// isolate.
throw new UnsupportedError("Isolate.spawn");
}
if (script.scheme == "package") {
script = await Isolate.resolvePackageUri(script);
}
_spawnFunction(readyPort.sendPort, script.toString(), entryPoint, message,
paused, errorsAreFatal, onExit, onError, packageRoot, packageConfig);
return await _spawnCommon(readyPort);
} catch (e, st) {
if (readyPort != null) {
readyPort.close();
}
return await new Future<Isolate>.error(e, st);
}
}
/**
* Creates and spawns an isolate that runs the code from the library with
* the specified URI.
*
* The isolate starts executing the top-level `main` function of the library
* with the given URI.
*
* The target `main` must be callable with zero, one or two arguments.
* Examples:
*
* * `main()`
* * `main(args)`
* * `main(args, message)`
*
* When present, the parameter `args` is set to the provided [args] list.
* When present, the parameter `message` is set to the initial [message].
*
* If the [paused] parameter is set to `true`,
* the isolate will start up in a paused state,
* as if by an initial call of `isolate.pause(isolate.pauseCapability)`.
* To resume the isolate, call `isolate.resume(isolate.pauseCapability)`.
*
* If the [errorAreFatal], [onExit] and/or [onError] parameters are provided,
* the isolate will act as if, respectively, [setErrorsFatal],
* [addOnExitListener] and [addErrorListener] were called with the
* corresponding parameter and was processed before the isolate starts
* running.
*
* You can also call the [setErrorsFatal], [addOnExitListener] and
* [addErrorListener] methods on the returned isolate, but unless the
* isolate was started as [paused], it may already have terminated
* before those methods can complete.
*
* If the [checked] parameter is set to `true` or `false`,
* the new isolate will run code in checked mode,
* respectively in production mode, if possible.
* If the parameter is omitted, the new isolate will inherit the
* value from the current isolate.
*
* It may not always be possible to honor the `checked` parameter.
* If the isolate code was pre-compiled, it may not be possible to change
* the checked mode setting dynamically.
* In that case, the `checked` parameter is ignored.
*
* WARNING: The [checked] parameter is not implemented on all platforms yet.
*
* If the [packageRoot] parameter is provided, it is used to find the location
* of package sources in the spawned isolate.
*
* The `packageRoot` URI must be a "file" or "http"/"https" URI that specifies
* a directory. If it doesn't end in a slash, one will be added before
* using the URI, and any query or fragment parts are ignored.
* Package imports (like `"package:foo/bar.dart"`) in the new isolate are
* resolved against this location, as by
* `packageRoot.resolve("foo/bar.dart")`.
*
* If the [packageConfig] parameter is provided, then it is used to find the
* location of a package resolution configuration file for the spawned
* isolate.
*
* If the [automaticPackageResolution] parameter is provided, then the
* location of the package sources in the spawned isolate is automatically
* determined.
*
* The [environment] is a mapping from strings to strings which the
* spawned isolate uses when looking up [String.fromEnvironment] values.
* The system may add its own entries to environment as well.
* If `environment` is omitted, the spawned isolate has the same environment
* declarations as the spawning isolate.
*
* WARNING: The [environment] parameter is not implemented on all
* platforms yet.
*
* Returns a future that will complete with an [Isolate] instance if the
* spawning succeeded. It will complete with an error otherwise.
*/
static Future<Isolate> spawnUri(Uri uri, List<String> args, var message,
{bool paused: false,
SendPort onExit,
SendPort onError,
bool errorsAreFatal,
bool checked,
Map<String, String> environment,
Uri packageRoot,
Uri packageConfig,
bool automaticPackageResolution: false}) async {
RawReceivePort readyPort;
if (environment != null) {
throw new UnimplementedError("environment");
}
// Verify that no mutually exclusive arguments have been passed.
if (automaticPackageResolution) {
if (packageRoot != null) {
throw new ArgumentError("Cannot simultaneously request "
"automaticPackageResolution and specify a"
"packageRoot.");
}
if (packageConfig != null) {
throw new ArgumentError("Cannot simultaneously request "
"automaticPackageResolution and specify a"
"packageConfig.");
}
} else {
if ((packageRoot != null) && (packageConfig != null)) {
throw new ArgumentError("Cannot simultaneously specify a "
"packageRoot and a packageConfig.");
}
}
try {
// Resolve the uri against the current isolate's root Uri first.
var spawnedUri = _rootUri.resolveUri(uri);
// Inherit this isolate's package resolution setup if not overridden.
if (!automaticPackageResolution &&
(packageRoot == null) &&
(packageConfig == null)) {
if (Isolate._packageSupported()) {
packageRoot = await Isolate.packageRoot;
packageConfig = await Isolate.packageConfig;
}
}
// Ensure to resolve package: URIs being handed in as parameters.
if (packageRoot != null) {
// Avoid calling resolvePackageUri if not stricly necessary in case
// the API is not supported.
if (packageRoot.scheme == "package") {
packageRoot = await Isolate.resolvePackageUri(packageRoot);
}
} else if (packageConfig != null) {
// Avoid calling resolvePackageUri if not strictly necessary in case
// the API is not supported.
if (packageConfig.scheme == "package") {
packageConfig = await Isolate.resolvePackageUri(packageConfig);
}
}
// The VM will invoke [_startIsolate] and not `main`.
readyPort = new RawReceivePort();
var packageRootString = packageRoot?.toString();
var packageConfigString = packageConfig?.toString();
_spawnUri(
readyPort.sendPort,
spawnedUri.toString(),
args,
message,
paused,
onExit,
onError,
errorsAreFatal,
checked,
null,
/* environment */
packageRootString,
packageConfigString);
return await _spawnCommon(readyPort);
} catch (e, st) {
if (readyPort != null) {
readyPort.close();
}
rethrow;
}
}
/**
* Requests the isolate to pause.
*
* When the isolate receives the pause command, it stops
* processing events from the event loop queue.
* It may still add new events to the queue in response to, e.g., timers
* or receive-port messages. When the isolate is resumed,
* it starts handling the already enqueued events.
*
* The pause request is sent through the isolate's command port,
* which bypasses the receiving isolate's event loop.
* The pause takes effect when it is received, pausing the event loop
* as it is at that time.
*
* The [resumeCapability] is used to identity the pause,
* and must be used again to end the pause using [resume].
* If [resumeCapability] is omitted, a new capability object is created
* and used instead.
*
* If an isolate is paused more than once using the same capability,
* only one resume with that capability is needed to end the pause.
*
* If an isolate is paused using more than one capability,
* each pause must be individually ended before the isolate resumes.
*
* Returns the capability that must be used to end the pause.
* This is either [resumeCapability], or a new capability when
* [resumeCapability] is omitted.
*
* If [pauseCapability] is `null`, or it's not the pause capability
* of the isolate identified by [controlPort],
* the pause request is ignored by the receiving isolate.
*/
Capability pause([Capability resumeCapability]) {
resumeCapability ??= new Capability();
_pause(resumeCapability);
return resumeCapability;
}
/** Internal implementation of [pause]. */
void _pause(Capability resumeCapability) {
var msg = new List(4)
..[0] = 0 // Make room for OOB message type.
..[1] = _PAUSE
..[2] = pauseCapability
..[3] = resumeCapability;
_sendOOB(controlPort, msg);
}
/**
* Resumes a paused isolate.
*
* Sends a message to an isolate requesting that it ends a pause
* that was previously requested.
*
* When all active pause requests have been cancelled, the isolate
* will continue processing events and handling normal messages.
*
* If the [resumeCapability] is not one that has previously been used
* to pause the isolate, or it has already been used to resume from
* that pause, the resume call has no effect.
*/
void resume(Capability resumeCapability) {
var msg = new List(4)
..[0] = 0 // Make room for OOB message type.
..[1] = _RESUME
..[2] = pauseCapability
..[3] = resumeCapability;
_sendOOB(controlPort, msg);
}
/**
* Requests an exist message on [responsePort] when the isolate terminates.
*
* The isolate will send [response] as a message on [responsePort] as the last
* thing before it terminates. It will run no further code after the message
* has been sent.
*
* Adding the same port more than once will only cause it to receive one exit
* message, using the last response value that was added,
* and it only needs to be removed once using [removeOnExitListener].
*
* If the isolate has terminated before it can receive this request,
* no exit message will be sent.
*
* The [response] object must follow the same restrictions as enforced by
* [SendPort.send].
* It is recommended to only use simple values that can be sent to all
* isolates, like `null`, booleans, numbers or strings.
*
* Since isolates run concurrently, it's possible for it to exit before the
* exit listener is established, and in that case no response will be
* sent on [responsePort].
* To avoid this, either use the corresponding parameter to the spawn
* function, or start the isolate paused, add the listener and
* then resume the isolate.
*/
/* TODO(lrn): Can we do better? Can the system recognize this message and
* send a reply if the receiving isolate is dead?
*/
void addOnExitListener(SendPort responsePort, {Object response}) {
var msg = new List(4)
..[0] = 0 // Make room for OOB message type.
..[1] = _ADD_EXIT
..[2] = responsePort
..[3] = response;
_sendOOB(controlPort, msg);
}
/**
* Stop listening for exit messages from the isolate.
*
* Requests for the isolate to not send exit messages on [responsePort].
* If the isolate isn't expecting to send exit messages on [responsePort],
* because the port hasn't been added using [addOnExitListener],
* or because it has already been removed, the request is ignored.
*
* If the same port has been passed via [addOnExitListener] more than once,
* only one call to `removeOnExitListener` is needed to stop it from receiving
* exit messagees.
*
* Closing the receive port at the end of the send port will not stop the
* isolate from sending exit messages, they are just going to be lost.
*
* An exit message may still be sent if the isolate terminates
* before this request is received and processed.
*/
void removeOnExitListener(SendPort responsePort) {
var msg = new List(3)
..[0] = 0 // Make room for OOB message type.
..[1] = _DEL_EXIT
..[2] = responsePort;
_sendOOB(controlPort, msg);
}
/**
* Set whether uncaught errors will terminate the isolate.
*
* If errors are fatal, any uncaught error will terminate the isolate
* event loop and shut down the isolate.
*
* This call requires the [terminateCapability] for the isolate.
* If the capability is absent or incorrect, no change is made.
*
* Since isolates run concurrently, it's possible for it to exit due to an
* error before errors are set non-fatal.
* To avoid this, either use the corresponding parameter to the spawn
* function, or start the isolate paused, set errors non-fatal and
* then resume the isolate.
*/
void setErrorsFatal(bool errorsAreFatal) {
var msg = new List(4)
..[0] = 0 // Make room for OOB message type.
..[1] = _ERROR_FATAL
..[2] = terminateCapability
..[3] = errorsAreFatal;
_sendOOB(controlPort, msg);
}
/**
* Requests the isolate to shut down.
*
* The isolate is requested to terminate itself.
* The [priority] argument specifies when this must happen.
*
* The [priority], when provided, must be one of [IMMEDIATE] or
* [BEFORE_NEXT_EVENT] (the default).
* The shutdown is performed at different times depending on the priority:
*
* * `IMMEDIATE`: The isolate shuts down as soon as possible.
* Control messages are handled in order, so all previously sent control
* events from this isolate will all have been processed.
* The shutdown should happen no later than if sent with
* `BEFORE_NEXT_EVENT`.
* It may happen earlier if the system has a way to shut down cleanly
* at an earlier time, even during the execution of another event.
* * `BEFORE_NEXT_EVENT`: The shutdown is scheduled for the next time
* control returns to the event loop of the receiving isolate,
* after the current event, and any already scheduled control events,
* are completed.
*
* If [terminateCapability] is `null`, or it's not the terminate capability
* of the isolate identified by [controlPort],
* the kill request is ignored by the receiving isolate.
*/
void kill({int priority: BEFORE_NEXT_EVENT}) {
var msg = new List(4)
..[0] = 0 // Make room for OOB message type.
..[1] = _KILL
..[2] = terminateCapability
..[3] = priority;
_sendOOB(controlPort, msg);
}
/**
* Request that the isolate send [response] on the [responsePort].
*
* The [response] object must follow the same restrictions as enforced by
* [SendPort.send].
* It is recommended to only use simple values that can be sent to all
* isolates, like `null`, booleans, numbers or strings.
*
* If the isolate is alive, it will eventually send `response`
* (defaulting to `null`) on the response port.
*
* The [priority] must be one of [IMMEDIATE] or [BEFORE_NEXT_EVENT].
* The response is sent at different times depending on the ping type:
*
* * `IMMEDIATE`: The isolate responds as soon as it receives the
* control message. This is after any previous control message
* from the same isolate has been received and processed,
* but may be during execution of another event.
* * `BEFORE_NEXT_EVENT`: The response is scheduled for the next time
* control returns to the event loop of the receiving isolate,
* after the current event, and any already scheduled control events,
* are completed.
*/
void ping(SendPort responsePort, {Object response, int priority: IMMEDIATE}) {
var msg = new List(5)
..[0] = 0 // Make room for OOM message type.
..[1] = _PING
..[2] = responsePort
..[3] = priority
..[4] = response;
_sendOOB(controlPort, msg);
}
/**
* Requests that uncaught errors of the isolate are sent back to [port].
*
* The errors are sent back as two elements lists.
* The first element is a `String` representation of the error, usually
* created by calling `toString` on the error.
* The second element is a `String` representation of an accompanying
* stack trace, or `null` if no stack trace was provided.
* To convert this back to a [StackTrace] object, use [StackTrace.fromString].
*
* Listening using the same port more than once does nothing.
* A port will only receive each error once,
* and will only need to be removed once using [removeErrorListener].
*
* Since isolates run concurrently, it's possible for it to exit before the
* error listener is established. To avoid this, start the isolate paused,
* add the listener and then resume the isolate.
*/
void addErrorListener(SendPort port) {
var msg = new List(3)
..[0] = 0 // Make room for OOB message type.
..[1] = _ADD_ERROR
..[2] = port;
_sendOOB(controlPort, msg);
}
/**
* Stop listening for uncaught errors from the isolate.
*
* Requests for the isolate to not send uncaught errors on [responsePort].
* If the isolate isn't expecting to send uncaught errors on [responsePort],
* because the port hasn't been added using [addErrorListener],
* or because it has already been removed, the request is ignored.
*
* If the same port has been passed via [addErrorListener] more than once,
* only one call to `removeErrorListener` is needed to stop it from receiving
* unaught errors.
*
* Closing the receive port at the end of the send port will not stop the
* isolate from sending uncaught errors, they are just going to be lost.
*
* Uncaught errors message may still be sent by the isolate
* until this request is received and processed.
*/
void removeErrorListener(SendPort port) {
var msg = new List(3)
..[0] = 0 // Make room for OOB message type.
..[1] = _DEL_ERROR
..[2] = port;
_sendOOB(controlPort, msg);
}
/**
* Returns a broadcast stream of uncaught errors from the isolate.
*
* Each error is provided as an error event on the stream.
*
* The actual error object and stackTraces will not necessarily
* be the same object types as in the actual isolate, but they will
* always have the same [Object.toString] result.
*
* This stream is based on [addErrorListener] and [removeErrorListener].
*/
Stream get errors {
StreamController controller;
RawReceivePort port;
void handleError(message) {
String errorDescription = message[0];
String stackDescription = message[1];
var error = new RemoteError(errorDescription, stackDescription);
controller.addError(error, error.stackTrace);
}
controller = new StreamController.broadcast(
sync: true,
onListen: () {
port = new RawReceivePort(handleError);
this.addErrorListener(port.sendPort);
},
onCancel: () {
this.removeErrorListener(port.sendPort);
port.close();
port = null;
});
return controller.stream;
}
static const _PAUSE = 1;
static Future<Isolate> _spawnCommon(RawReceivePort readyPort) {
Completer completer = new Completer<Isolate>.sync();
readyPort.handler = (readyMessage) {
readyPort.close();
if (readyMessage is List && readyMessage.length == 2) {
SendPort controlPort = readyMessage[0];
List capabilities = readyMessage[1];
completer.complete(new Isolate(controlPort,
pauseCapability: capabilities[0],
terminateCapability: capabilities[1]));
} else if (readyMessage is String) {
// We encountered an error while starting the new isolate.
completer.completeError(new IsolateSpawnException(
'Unable to spawn isolate: ${readyMessage}'));
} else {
// This shouldn't happen.
completer.completeError(new IsolateSpawnException(
"Internal error: unexpected format for ready message: "
"'${readyMessage}'"));
}
};
return completer.future;
}
static final _currentIsolate = _getCurrentIsolate();
static const _RESUME = 2;
static const _PING = 3;
static const _KILL = 4;
static const _ADD_EXIT = 5;
static const _DEL_EXIT = 6;
static const _ADD_ERROR = 7;
static const _DEL_ERROR = 8;
static const _ERROR_FATAL = 9;
static void _spawnFunction(
SendPort readyPort,
String uri,
Function topLevelFunction,
var message,
bool paused,
bool errorsAreFatal,
SendPort onExit,
SendPort onError,
String packageRoot,
String packageConfig) native "Isolate_spawnFunction";
static void _spawnUri(
SendPort readyPort,
String uri,
List<String> args,
var message,
bool paused,
SendPort onExit,
SendPort onError,
bool errorsAreFatal,
bool checked,
List environment,
String packageRoot,
String packageConfig) native "Isolate_spawnUri";
static void _sendOOB(port, msg) native "Isolate_sendOOB";
static Isolate _getCurrentIsolate() {
List portAndCapabilities = _getPortAndCapabilitiesOfCurrentIsolate();
return new Isolate(portAndCapabilities[0],
pauseCapability: portAndCapabilities[1],
terminateCapability: portAndCapabilities[2]);
}
static List _getPortAndCapabilitiesOfCurrentIsolate()
native "Isolate_getPortAndCapabilitiesOfCurrentIsolate";
static Uri _getCurrentRootUri() {
try {
return Uri.parse(_getCurrentRootUriStr());
} catch (e, s) {
return null;
}
}
static String _getCurrentRootUriStr() native "Isolate_getCurrentRootUriStr";
static final _rootUri = _getCurrentRootUri();
static bool _packageSupported() =>
(VMLibraryHooks.packageRootUriFuture != null) &&
(VMLibraryHooks.packageConfigUriFuture != null) &&
(VMLibraryHooks.resolvePackageUriFuture != null);
}
/**
* Sends messages to its [ReceivePort]s.
*
* [SendPort]s are created from [ReceivePort]s. Any message sent through
* a [SendPort] is delivered to its corresponding [ReceivePort]. There might be
* many [SendPort]s for the same [ReceivePort].
*
* [SendPort]s can be transmitted to other isolates, and they preserve equality
* when sent.
*/
abstract class SendPort implements Capability {
/**
* Sends an asynchronous [message] through this send port, to its
* corresponding `ReceivePort`.
*
* The content of [message] can be: primitive values (null, num, bool, double,
* String), instances of [SendPort], and lists and maps whose elements are any
* of these. List and maps are also allowed to be cyclic.
*
* In the special circumstances when two isolates share the same code and are
* running in the same process (e.g. isolates created via [Isolate.spawn]), it
* is also possible to send object instances (which would be copied in the
* process). This is currently only supported by the dartvm. For now, the
* dart2js compiler only supports the restricted messages described above.
*
* The send happens immediately and doesn't block. The corresponding receive
* port can receive the message as soon as its isolate's event loop is ready
* to deliver it, independently of what the sending isolate is doing.
*/
void send(var message);
/**
* Tests whether [other] is a [SendPort] pointing to the same
* [ReceivePort] as this one.
*/
bool operator ==(var other);
/**
* Returns an immutable hash code for this send port that is
* consistent with the == operator.
*/
int get hashCode;
}
/**
* Together with [SendPort], the only means of communication between isolates.
*
* [ReceivePort]s have a `sendPort` getter which returns a [SendPort].
* Any message that is sent through this [SendPort]
* is delivered to the [ReceivePort] it has been created from. There, the
* message is dispatched to the `ReceivePort`'s listener.
*
* A [ReceivePort] is a non-broadcast stream. This means that it buffers
* incoming messages until a listener is registered. Only one listener can
* receive messages. See [Stream.asBroadcastStream] for transforming the port
* to a broadcast stream.
*
* A [ReceivePort] may have many [SendPort]s.
*/
abstract class ReceivePort implements Stream {
/**
* Opens a long-lived port for receiving messages.
*
* A [ReceivePort] is a non-broadcast stream. This means that it buffers
* incoming messages until a listener is registered. Only one listener can
* receive messages. See [Stream.asBroadcastStream] for transforming the port
* to a broadcast stream.
*
* A receive port is closed by canceling its subscription.
*/
factory ReceivePort() = _ReceivePortImpl;
/**
* Creates a [ReceivePort] from a [RawReceivePort].
*
* The handler of the given [rawPort] is overwritten during the construction
* of the result.
*/
factory ReceivePort.fromRawReceivePort(RawReceivePort rawPort) =
_ReceivePortImpl.fromRawReceivePort;
/**
* Inherited from [Stream].
*
* Note that [onError] and [cancelOnError] are ignored since a ReceivePort
* will never receive an error.
*
* The [onDone] handler will be called when the stream closes.
* The stream closes when [close] is called.
*/
StreamSubscription listen(void onData(var message),
{Function onError, void onDone(), bool cancelOnError});
/**
* Closes `this`.
*
* If the stream has not been canceled yet, adds a close-event to the event
* queue and discards any further incoming messages.
*
* If the stream has already been canceled this method has no effect.
*/
void close();
/**
* Returns a [SendPort] that sends to this receive port.
*/
SendPort get sendPort;
}
abstract class RawReceivePort {
/**
* Opens a long-lived port for receiving messages.
*
* A [RawReceivePort] is low level and does not work with [Zone]s. It
* can not be paused. The data-handler must be set before the first
* event is received.
*/
factory RawReceivePort([void handler(event)]) {
_RawReceivePortImpl result = new _RawReceivePortImpl();
result.handler = handler;
return result;
}
/**
* Sets the handler that is invoked for every incoming message.
*
* The handler is invoked in the root-zone ([Zone.ROOT]).
*/
void set handler(Function newHandler);
/**
* Closes the port.
*
* After a call to this method any incoming message is silently dropped.
*/
void close();
/**
* Returns a [SendPort] that sends to this raw receive port.
*/
SendPort get sendPort;
}
/**
* Description of an error from another isolate.
*
* This error has the same `toString()` and `stackTrace.toString()` behavior
* as the original error, but has no other features of the original error.
*/
class RemoteError implements Error {
final String _description;
final StackTrace stackTrace;
RemoteError(String description, String stackDescription)
: _description = description,
stackTrace = new StackTrace.fromString(stackDescription);
String toString() => _description;
}
class _CapabilityImpl implements Capability {
factory _CapabilityImpl() native "CapabilityImpl_factory";
bool operator ==(var other) {
return (other is _CapabilityImpl) && _equals(other);
}
int get hashCode {
return _get_hashcode();
}
_equals(other) native "CapabilityImpl_equals";
_get_hashcode() native "CapabilityImpl_get_hashcode";
}
class _ReceivePortImpl extends Stream implements ReceivePort {
_ReceivePortImpl() : this.fromRawReceivePort(new RawReceivePort());
_ReceivePortImpl.fromRawReceivePort(this._rawPort) {
_controller = new StreamController(onCancel: close, sync: true);
_rawPort.handler = _controller.add;
}
SendPort get sendPort {
return _rawPort.sendPort;
}
StreamSubscription listen(void onData(var message),
{Function onError, void onDone(), bool cancelOnError}) {
return _controller.stream.listen(onData,
onError: onError, onDone: onDone, cancelOnError: cancelOnError);
}
close() {
_rawPort.close();
_controller.close();
}
final RawReceivePort _rawPort;
StreamController _controller;
}
typedef void _ImmediateCallback();
/// The callback that has been registered through `scheduleImmediate`.
_ImmediateCallback _pendingImmediateCallback;
/// The closure that should be used as scheduleImmediateClosure, when the VM
/// is responsible for the event loop.
void _isolateScheduleImmediate(void callback()) {
assert(_pendingImmediateCallback == null);
_pendingImmediateCallback = callback;
}
void _runPendingImmediateCallback() {
if (_pendingImmediateCallback != null) {
var callback = _pendingImmediateCallback;
_pendingImmediateCallback = null;
callback();
}
}
_ImmediateCallback _removePendingImmediateCallback() {
var callback = _pendingImmediateCallback;
_pendingImmediateCallback = null;
return callback;
}
/// The embedder can execute this function to get hold of
/// [_isolateScheduleImmediate] above.
Function _getIsolateScheduleImmediateClosure() {
return _isolateScheduleImmediate;
}
class _RawReceivePortImpl implements RawReceivePort {
factory _RawReceivePortImpl() native "RawReceivePortImpl_factory";
close() {
// Close the port and remove it from the handler map.
_handlerMap.remove(this._closeInternal());
}
SendPort get sendPort {
return _get_sendport();
}
bool operator ==(var other) {
return (other is _RawReceivePortImpl) &&
(this._get_id() == other._get_id());
}
int get hashCode {
return sendPort.hashCode;
}
/**** Internal implementation details ****/
int _get_id() native "RawReceivePortImpl_get_id";
SendPort _get_sendport() native "RawReceivePortImpl_get_sendport";
// Called from the VM to retrieve the handler for a message.
static _lookupHandler(int id) {
var result = _handlerMap[id];
return result;
}
// Called from the VM to dispatch to the handler.
static void _handleMessage(Function handler, var message) {
// TODO(floitsch): this relies on the fact that any exception aborts the
// VM. Once we have non-fatal global exceptions we need to catch errors
// so that we can run the immediate callbacks.
handler(message);
_runPendingImmediateCallback();
}
// Call into the VM to close the VM maintained mappings.
int _closeInternal() native "RawReceivePortImpl_closeInternal";
void set handler(Function value) {
_handlerMap[this._get_id()] = value;
}
// TODO(iposva): Ideally keep this map in the VM.
// id to handler mapping.
static Map<int, Function> _initHandlerMap() {
// TODO(18511): Workaround bad CheckSmi hoisting.
return new HashMap<int, Function>();
}
static final Map<int, Function> _handlerMap = _initHandlerMap();
}
class _SendPortImpl implements SendPort {
/*--- public interface ---*/
void send(var message) {
_sendInternal(message);
}
bool operator ==(var other) {
return (other is _SendPortImpl) && (this._get_id() == other._get_id());
}
int get hashCode {
return _get_hashcode();
}
/*--- private implementation ---*/
_get_id() native "SendPortImpl_get_id";
_get_hashcode() native "SendPortImpl_get_hashcode";
// Forward the implementation of sending messages to the VM.
void _sendInternal(var message) native "SendPortImpl_sendInternal_";
}
typedef _NullaryFunction();
typedef _UnaryFunction(args);
typedef _BinaryFunction(args, message);
/**
* Takes the real entry point as argument and invokes it with the
* initial message. Defers execution of the entry point until the
* isolate is in the message loop.
*/
void _startMainIsolate(Function entryPoint, List<String> args) {
_startIsolate(
null, // no parent port
entryPoint,
args,
null, // no message
true, // isSpawnUri
null, // no control port
null); // no capabilities
}
/**
* Takes the real entry point as argument and invokes it with the initial
* message.
*/
void _startIsolate(
SendPort parentPort,
Function entryPoint,
List<String> args,
var message,
bool isSpawnUri,
RawReceivePort controlPort,
List capabilities) {
// The control port (aka the main isolate port) does not handle any messages.
if (controlPort != null) {
controlPort.handler = (_) {}; // Nobody home on the control port.
}
if (parentPort != null) {
// Build a message to our parent isolate providing access to the
// current isolate's control port and capabilities.
//
// TODO(floitsch): Send an error message if we can't find the entry point.
var readyMessage = new List(2);
readyMessage[0] = controlPort.sendPort;
readyMessage[1] = capabilities;
// Out of an excess of paranoia we clear the capabilities from the
// stack. Not really necessary.
capabilities = null;
parentPort.send(readyMessage);
}
assert(capabilities == null);
// Delay all user code handling to the next run of the message loop. This
// allows us to intercept certain conditions in the event dispatch, such as
// starting in paused state.
RawReceivePort port = new RawReceivePort();
port.handler = (_) {
port.close();
if (isSpawnUri) {
if (entryPoint is _BinaryFunction) {
entryPoint(args, message);
} else if (entryPoint is _UnaryFunction) {
entryPoint(args);
} else {
entryPoint();
}
} else {
entryPoint(message);
}
};
// Make sure the message handler is triggered.
port.sendPort.send(null);
}
class _TimerHeap {
List<_Timer> _list;
int _used = 0;
_TimerHeap([int initSize = 7]) : _list = new List<_Timer>(initSize);
bool get isEmpty => _used == 0;
_Timer get first => _list[0];
bool isFirst(_Timer timer) => timer._indexOrNext == 0;
void add(_Timer timer) {
if (_used == _list.length) {
_resize();
}
timer._indexOrNext = _used++;
_list[timer._indexOrNext] = timer;
_bubbleUp(timer);
}
_Timer removeFirst() {
var f = first;
remove(f);
return f;
}
void remove(_Timer timer) {
_used--;
if (isEmpty) {
_list[0] = null;
timer._indexOrNext = null;
return;
}
var last = _list[_used];
if (!identical(last, timer)) {
last._indexOrNext = timer._indexOrNext;
_list[last._indexOrNext] = last;
if (last._compareTo(timer) < 0) {
_bubbleUp(last);
} else {
_bubbleDown(last);
}
}
_list[_used] = null;
timer._indexOrNext = null;
}
void _resize() {
var newList = new List(_list.length * 2 + 1);
newList.setRange(0, _used, _list);
_list = newList;
}
void _bubbleUp(_Timer timer) {
while (!isFirst(timer)) {
Timer parent = _parent(timer);
if (timer._compareTo(parent) < 0) {
_swap(timer, parent);
} else {
break;
}
}
}
void _bubbleDown(_Timer timer) {
while (true) {
int leftIndex = _leftChildIndex(timer._indexOrNext);
int rightIndex = _rightChildIndex(timer._indexOrNext);
_Timer newest = timer;
if (leftIndex < _used && _list[leftIndex]._compareTo(newest) < 0) {
newest = _list[leftIndex];
}
if (rightIndex < _used && _list[rightIndex]._compareTo(newest) < 0) {
newest = _list[rightIndex];
}
if (identical(newest, timer)) {
// We are where we should be, break.
break;
}
_swap(newest, timer);
}
}
void _swap(_Timer first, _Timer second) {
int tmp = first._indexOrNext;
first._indexOrNext = second._indexOrNext;
second._indexOrNext = tmp;
_list[first._indexOrNext] = first;
_list[second._indexOrNext] = second;
}
Timer _parent(_Timer timer) => _list[_parentIndex(timer._indexOrNext)];
Timer _leftChild(_Timer timer) => _list[_leftChildIndex(timer._indexOrNext)];
Timer _rightChild(_Timer timer) =>
_list[_rightChildIndex(timer._indexOrNext)];
static int _parentIndex(int index) => (index - 1) ~/ 2;
static int _leftChildIndex(int index) => 2 * index + 1;
static int _rightChildIndex(int index) => 2 * index + 2;
}
class _Timer implements Timer {
// Cancels the timer in the event handler.
static const _NO_TIMER = -1;
// We distinguish what kind of message arrived based on the value being sent.
static const _ZERO_EVENT = 1;
static const _TIMEOUT_EVENT = null;
// Timers are ordered by wakeup time. Timers with a timeout value of > 0 do
// end up on the TimerHeap. Timers with a timeout of 0 are queued in a list.
static _TimerHeap _heap = new _TimerHeap();
static _Timer _firstZeroTimer;
static _Timer _lastZeroTimer;
// We use an id to be able to sort timers with the same expiration time.
// ids are recycled after ID_MASK enqueues or when the timer queue is empty.
static const _ID_MASK = 0x1fffffff;
static int _idCount = 0;
static RawReceivePort _receivePort;
static SendPort _sendPort;
static int _scheduledWakeupTime;
static bool _handlingCallbacks = false;
Function _callback; // Closure to call when timer fires. null if canceled.
int _wakeupTime; // Expiration time.
final int _milliSeconds; // Duration specified at creation.
final bool _repeating; // Indicates periodic timers.
var _indexOrNext; // Index if part of the TimerHeap, link otherwise.
int _id; // Incrementing id to enable sorting of timers with same expiry.
// Get the next available id. We accept collisions and reordering when the
// _idCount overflows and the timers expire at the same millisecond.
static int _nextId() {
var result = _idCount;
_idCount = (_idCount + 1) & _ID_MASK;
return result;
}
_Timer._internal(
this._callback, this._wakeupTime, this._milliSeconds, this._repeating)
: _id = _nextId();
static Timer _createTimer(
void callback(Timer timer), int milliSeconds, bool repeating) {
// Negative timeouts are treated as if 0 timeout.
if (milliSeconds < 0) {
milliSeconds = 0;
}
// Add one because DateTime.now() is assumed to round down
// to nearest millisecond, not up, so that time + duration is before
// duration milliseconds from now. Using microsecond timers like
// Stopwatch allows detecting that the timer fires early.
int now = VMLibraryHooks.timerMillisecondClock();
int wakeupTime = (milliSeconds == 0) ? now : (now + 1 + milliSeconds);
_Timer timer =
new _Timer._internal(callback, wakeupTime, milliSeconds, repeating);
// Enqueue this newly created timer in the appropriate structure and
// notify if necessary.
timer._enqueue();
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);
}
bool get _isInHeap => _indexOrNext is int;
int _compareTo(_Timer other) {
int c = _wakeupTime - other._wakeupTime;
if (c != 0) return c;
return _id - other._id;
}
bool get isActive => _callback != null;
// Cancels a set timer. The timer is removed from the timer heap if it is a
// non-zero timer. Zero timers are kept in the list as they need to consume
// the corresponding pending message.
void cancel() {
_callback = null;
// Only heap timers are really removed. Zero timers need to consume their
// corresponding wakeup message so they are left in the queue.
if (!_isInHeap) return;
bool update = _heap.isFirst(this);
_heap.remove(this);
if (update) {
_notifyEventHandler();
}
}
void _advanceWakeupTime() {
// Recalculate the next wakeup time. For repeating timers with a 0 timeout
// the next wakeup time is now.
_id = _nextId();
if (_milliSeconds > 0) {
_wakeupTime += _milliSeconds;
} else {
_wakeupTime = VMLibraryHooks.timerMillisecondClock();
}
}
// Adds a timer to the heap or timer list. Timers with the same wakeup time
// are enqueued in order and notified in FIFO order.
void _enqueue() {
if (_milliSeconds == 0) {
if (_firstZeroTimer == null) {
_lastZeroTimer = this;
_firstZeroTimer = this;
} else {
_lastZeroTimer._indexOrNext = this;
_lastZeroTimer = this;
}
// Every zero timer gets its own event.
_notifyZeroHandler();
} else {
_heap.add(this);
if (_heap.isFirst(this)) {
_notifyEventHandler();
}
}
}
// Enqeue one message for each zero timer. To be able to distinguish from
// EventHandler messages we send a _ZERO_EVENT instead of a _TIMEOUT_EVENT.
static void _notifyZeroHandler() {
if (_sendPort == null) {
_createTimerHandler();
}
_sendPort.send(_ZERO_EVENT);
}
// Handle the notification of a zero timer. Make sure to also execute non-zero
// timers with a lower expiration time.
static List _queueFromZeroEvent() {
var pendingTimers = new List();
assert(_firstZeroTimer != null);
// Collect pending timers from the timer heap that have an expiration prior
// to the currently notified zero timer.
var timer;
while (!_heap.isEmpty && (_heap.first._compareTo(_firstZeroTimer) < 0)) {
timer = _heap.removeFirst();
pendingTimers.add(timer);
}
// Append the first zero timer to the pending timers.
timer = _firstZeroTimer;
_firstZeroTimer = timer._indexOrNext;
timer._indexOrNext = null;
pendingTimers.add(timer);
return pendingTimers;
}
static void _notifyEventHandler() {
if (_handlingCallbacks) {
// 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 there are no pending timers. Close down the receive port.
if ((_firstZeroTimer == null) && _heap.isEmpty) {
// No pending timers: Close the receive port and let the event handler
// know.
if (_sendPort != null) {
_cancelWakeup();
_shutdownTimerHandler();
}
return;
} else if (_heap.isEmpty) {
// Only zero timers are left. Cancel any scheduled wakeups.
_cancelWakeup();
return;
}
// Only send a message if the requested wakeup time differs from the
// already scheduled wakeup time.
var wakeupTime = _heap.first._wakeupTime;
if ((_scheduledWakeupTime == null) ||
(wakeupTime != _scheduledWakeupTime)) {
_scheduleWakeup(wakeupTime);
}
}
static List _queueFromTimeoutEvent() {
var pendingTimers = new List();
if (_firstZeroTimer != null) {
// Collect pending timers from the timer heap that have an expiration
// prior to the next zero timer.
// By definition the first zero timer has been scheduled before the
// current time, meaning all timers which are "less than" the first zero
// timer are expired. The first zero timer will be dispatched when its
// corresponding message is delivered.
var timer;
while (!_heap.isEmpty && (_heap.first._compareTo(_firstZeroTimer) < 0)) {
timer = _heap.removeFirst();
pendingTimers.add(timer);
}
} else {
// Collect pending timers from the timer heap which have expired at this
// time.
var currentTime = VMLibraryHooks.timerMillisecondClock();
var timer;
while (!_heap.isEmpty && (_heap.first._wakeupTime <= currentTime)) {
timer = _heap.removeFirst();
pendingTimers.add(timer);
}
}
return pendingTimers;
}
static void _runTimers(List pendingTimers) {
// If there are no pending timers currently reset the id space before we
// have a chance to enqueue new timers.
if (_heap.isEmpty && (_firstZeroTimer == null)) {
_idCount = 0;
}
// Fast exit if no pending timers.
if (pendingTimers.length == 0) {
return;
}
// 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.
_handlingCallbacks = true;
try {
for (var i = 0; i < pendingTimers.length; i++) {
// Next pending timer.
var timer = pendingTimers[i];
timer._indexOrNext = null;
// One of the timers in the pending_timers list can cancel
// one of the later timers which will set the callback to
// null. Or the pending zero timer has been canceled earlier.
if (timer._callback != null) {
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();
timer._enqueue();
}
// Execute pending micro tasks.
var immediateCallback = _removePendingImmediateCallback();
if (immediateCallback != null) {
immediateCallback();
}
}
}
} finally {
_handlingCallbacks = false;
}
}
static void _handleMessage(msg) {
var pendingTimers;
if (msg == _ZERO_EVENT) {
pendingTimers = _queueFromZeroEvent();
assert(pendingTimers.length > 0);
} else {
assert(msg == _TIMEOUT_EVENT);
_scheduledWakeupTime = null; // Consumed the last scheduled wakeup now.
pendingTimers = _queueFromTimeoutEvent();
}
_runTimers(pendingTimers);
// Notify the event handler or shutdown the port if no more pending
// timers are present.
_notifyEventHandler();
}
// Tell the event handler to wake this isolate at a specific time.
static void _scheduleWakeup(int wakeupTime) {
if (_sendPort == null) {
_createTimerHandler();
}
VMLibraryHooks.eventHandlerSendData(null, _sendPort, wakeupTime);
_scheduledWakeupTime = wakeupTime;
}
// Cancel pending wakeups in the event handler.
static void _cancelWakeup() {
assert(_sendPort != null);
VMLibraryHooks.eventHandlerSendData(null, _sendPort, _NO_TIMER);
_scheduledWakeupTime = null;
}
// Create a receive port and register a message handler for the timer
// events.
static void _createTimerHandler() {
assert(_receivePort == null);
assert(_sendPort == null);
_receivePort = new RawReceivePort(_handleMessage);
_sendPort = _receivePort.sendPort;
_scheduledWakeupTime = null;
}
static void _shutdownTimerHandler() {
_receivePort.close();
_receivePort = null;
_sendPort = null;
_scheduledWakeupTime = null;
}
// The Timer factory registered with the dart:async library by the embedder.
static Timer _factory(
int milliSeconds, void callback(Timer timer), bool repeating) {
if (repeating) {
return new _Timer.periodic(milliSeconds, callback);
}
return new _Timer(milliSeconds, callback);
}
}
_setupHooks() {
VMLibraryHooks.timerFactory = _Timer._factory;
}