| // 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. |
| /// |
| /// *NOTE*: The `dart:isolate` library is currently only supported by the |
| /// [Dart Native](https://dart.dev/overview#platform) platform. |
| |
| /// |
| /// To use this library in your code: |
| /// ```dart |
| /// import 'dart:isolate'; |
| /// ``` |
| /// {@category VM} |
| library dart.isolate; |
| |
| import "dart:_internal" show Since; |
| import "dart:async"; |
| import "dart:typed_data" show ByteBuffer, TypedData, Uint8List; |
| |
| part "capability.dart"; |
| |
| /// Thrown when an isolate cannot be created. |
| class IsolateSpawnException implements Exception { |
| /// Error message reported by the spawn operation. |
| final String message; |
| @pragma("vm:entry-point") |
| 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 to 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 beforeNextEvent = 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; |
| |
| /// The name of the [Isolate] displayed for debug purposes. |
| /// |
| /// This can be set using the `debugName` parameter in [spawn] and [spawnUri]. |
| /// |
| /// This name does not uniquely identify an isolate. Multiple isolates in the |
| /// same process may have the same `debugName`. |
| /// |
| /// For a given isolate, this value will be the same as the values returned by |
| /// `Dart_DebugName` in the C embedding API and the `debugName` property in |
| /// [IsolateMirror]. |
| @Since("2.3") |
| external String? get debugName; |
| |
| /// Creates 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 = 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}); |
| |
| /// 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. |
| external static Isolate get current; |
| |
| /// The location of the package configuration of the current isolate, if any. |
| /// |
| /// If the isolate has not been setup for package resolution, |
| /// this location is `null`, |
| /// otherwise it is a URI referencing the package config file. |
| external static Future<Uri?> get packageConfig; |
| |
| /// 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. |
| external static Future<Uri?> resolvePackageUri(Uri 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 [errorsAreFatal], [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 [debugName] is provided, the spawned [Isolate] will be identifiable by |
| /// this name in debuggers and logging. |
| /// |
| /// 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. |
| external static Future<Isolate> spawn<T>( |
| void entryPoint(T message), T message, |
| {bool paused = false, |
| bool errorsAreFatal = true, |
| SendPort? onExit, |
| SendPort? onError, |
| @Since("2.3") String? debugName}); |
| |
| /// 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 [errorsAreFatal], [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 (enabling asserts and type |
| /// checks), respectively in production mode (disabling asserts and type |
| /// checks), if possible. If the parameter is omitted, the new isolate will |
| /// inherit the value from the current isolate. |
| /// |
| /// In Dart2 strong mode, the `checked` parameter only controls asserts, but |
| /// not type checks. |
| /// |
| /// 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 [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. |
| /// |
| /// If [debugName] is provided, the spawned [Isolate] will be identifiable by |
| /// this name in debuggers and logging. |
| /// |
| /// Returns a future that will complete with an [Isolate] instance if the |
| /// spawning succeeded. It will complete with an error otherwise. |
| external static Future<Isolate> spawnUri( |
| Uri uri, |
| List<String> args, |
| var message, |
| {bool paused = false, |
| SendPort? onExit, |
| SendPort? onError, |
| bool errorsAreFatal = true, |
| bool? checked, |
| Map<String, String>? environment, |
| @Deprecated('The packages/ dir is not supported in Dart 2') |
| Uri? packageRoot, |
| Uri? packageConfig, |
| bool automaticPackageResolution = false, |
| @Since("2.3") |
| String? debugName}); |
| |
| /// 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]. |
| external void _pause(Capability resumeCapability); |
| |
| /// 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. |
| external void resume(Capability resumeCapability); |
| |
| /// Requests an exit 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? |
| */ |
| external void addOnExitListener(SendPort responsePort, {Object? response}); |
| |
| /// Stops 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 messages. |
| /// |
| /// Closing the receive port that is associated with the [responsePort] does |
| /// not stop the isolate from sending uncaught errors, 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. |
| external void removeOnExitListener(SendPort responsePort); |
| |
| /// Sets 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 the receiving isolate |
| /// to exit due to an error, before a request, using this method, has been |
| /// received and processed. |
| /// 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. |
| external void setErrorsFatal(bool errorsAreFatal); |
| |
| /// 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 |
| /// [beforeNextEvent] (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 |
| /// `beforeNextEvent`. |
| /// 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. |
| /// * `beforeNextEvent`: 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. |
| external void kill({int priority = beforeNextEvent}); |
| |
| /// Requests 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 [beforeNextEvent]. |
| /// 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. |
| /// * `beforeNextEvent`: 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. |
| external void ping(SendPort responsePort, |
| {Object? response, int priority = immediate}); |
| |
| /// Requests that uncaught errors of the isolate are sent back to [port]. |
| /// |
| /// The errors are sent back as two-element 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]. |
| /// |
| /// Closing the receive port that is associated with the port does not stop |
| /// the isolate from sending uncaught errors, they are just going to be lost. |
| /// Instead use [removeErrorListener] to stop receiving errors on [port]. |
| /// |
| /// 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. |
| external void addErrorListener(SendPort port); |
| |
| /// Stops listening for uncaught errors from the isolate. |
| /// |
| /// Requests for the isolate to not send uncaught errors on [port]. |
| /// If the isolate isn't expecting to send uncaught errors on [port], |
| /// 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 |
| /// uncaught errors. |
| /// |
| /// Uncaught errors message may still be sent by the isolate |
| /// until this request is received and processed. |
| external void removeErrorListener(SendPort port); |
| |
| /// 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 = StreamController.broadcast(sync: true); |
| RawReceivePort? port; |
| void handleError(Object? message) { |
| var listMessage = message as List<Object?>; |
| var errorDescription = listMessage[0] as String; |
| var stackDescription = listMessage[1] as String; |
| var error = new RemoteError(errorDescription, stackDescription); |
| controller.addError(error, error.stackTrace); |
| } |
| |
| controller.onListen = () { |
| RawReceivePort receivePort = new RawReceivePort(handleError); |
| port = receivePort; |
| this.addErrorListener(receivePort.sendPort); |
| }; |
| controller.onCancel = () { |
| var listenPort = port!; |
| port = null; |
| this.removeErrorListener(listenPort.sendPort); |
| listenPort.close(); |
| }; |
| return controller.stream; |
| } |
| |
| /// Terminates the current isolate synchronously. |
| /// |
| /// This operation is potentially dangerous and should be used judiciously. |
| /// The isolate stops operating *immediately*. It throws if the optional |
| /// [message] does not adhere to the limitations on what can be sent from one |
| /// isolate to another (see [SendPort.send] for more details). It also throws |
| /// if a [finalMessagePort] is associated with an isolate spawned outside of |
| /// current isolate group, spawned via [spawnUri]. |
| /// |
| /// If successful, a call to this method does not return. Pending `finally` |
| /// blocks are not executed, control flow will not go back to the event loop, |
| /// scheduled asynchronous asks will never run, and even pending isolate |
| /// control commands may be ignored. (The isolate will send messages to ports |
| /// already registered using [Isolate.addOnExitListener], but no further Dart |
| /// code will run in the isolate.) |
| /// |
| /// If [finalMessagePort] is provided, and the [message] can be sent through |
| /// it (see [SendPort.send] for more details), then the message is sent |
| /// through that port as the final operation of the current isolate. The |
| /// isolate terminates immediately after that [SendPort.send] call returns. |
| /// |
| /// If the port is a native port -- one provided by [ReceivePort.sendPort] or |
| /// [RawReceivePort.sendPort] -- the system may be able to send this final |
| /// message more efficiently than normal port communication between live |
| /// isolates. In these cases this final message object graph will be |
| /// reassigned to the receiving isolate without copying. Further, the |
| /// receiving isolate will in most cases be able to receive the message |
| /// in constant time. |
| external static Never exit([SendPort? finalMessagePort, Object? message]); |
| } |
| |
| /// 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 transitive object graph of [message] can contain the following |
| /// objects: |
| /// - [Null] |
| /// - [bool] |
| /// - [int] |
| /// - [double] |
| /// - [String] |
| /// - [List] or [Map] (whose elements are any of these) |
| /// - [TransferableTypedData] |
| /// - [SendPort] |
| /// - [Capability] |
| /// |
| /// If the sender and receiver isolate share the same code (e.g. isolates |
| /// created via [Isolate.spawn]), the transitive object graph of [message] can |
| /// contain any object, with the following exceptions: |
| /// |
| /// - Objects with native resources (subclasses of e.g. |
| /// `NativeFieldWrapperClass1`). A [Socket] object for example referrs |
| /// internally to objects that have native resources attached and can |
| /// therefore not be sent. |
| /// - [ReceivePort] |
| /// - [DynamicLibrary] |
| /// - [Pointer] |
| /// - [UserTag] |
| /// - `MirrorReference` |
| /// |
| /// Apart from those exceptions any object can be sent. Objects that are |
| /// identified as immutable (e.g. strings) will be shared whereas all other |
| /// objects will be copied. |
| /// |
| /// The send happens immediately and may have a linear time cost to copy the |
| /// transtive object graph. The send itself doesn't block (i.e. doesn't wait |
| /// until the receiver has received the message). 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. |
| /// |
| /// Note: Due to an implementation choice the Dart VM made for how closures |
| /// represent captured state, closures can currently capture more state than |
| /// they need, which can cause the transitive closure to be larger than |
| /// needed. Open bug to address this: http://dartbug.com/36983 |
| void send(Object? message); |
| |
| /// Tests whether [other] is a [SendPort] pointing to the same |
| /// [ReceivePort] as this one. |
| bool operator ==(var other); |
| |
| /// A 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<dynamic> { |
| /// 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. |
| /// |
| /// The optional `debugName` parameter can be set to associate a name with |
| /// this port that can be displayed in tooling. |
| /// |
| /// A receive port is closed by canceling its subscription. |
| external factory ReceivePort([String debugName = '']); |
| |
| /// Creates a [ReceivePort] from a [RawReceivePort]. |
| /// |
| /// The handler of the given [rawPort] is overwritten during the construction |
| /// of the result. |
| external factory ReceivePort.fromRawReceivePort(RawReceivePort rawPort); |
| |
| /// Listen for events from [Stream]. |
| /// |
| /// See [Stream.listen]. |
| /// |
| /// 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<dynamic> listen(void onData(var message)?, |
| {Function? onError, void onDone()?, bool? cancelOnError}); |
| |
| /// Closes the receive port. |
| /// |
| /// No further events will be received by the receive port, |
| /// or emitted as stream events. |
| /// |
| /// If [listen] has been called and the [StreamSubscription] has not |
| /// been canceled yet, the subscription will be closed with a "done" |
| /// event. |
| /// |
| /// If the stream has already been canceled this method has no effect. |
| void close(); |
| |
| /// A [SendPort] which sends messages to this receive port. |
| SendPort get sendPort; |
| } |
| |
| /// A low-level asynchronous message receiver. |
| /// |
| /// A [RawReceivePort] is low level feature, and is not [Zone] aware. |
| /// The [handler] will always be invoked in the [Zone.root] zone. |
| /// |
| /// The port cannot be paused. The data-handler must be set before the first |
| /// message is received, otherwise the message is lost. |
| /// |
| /// Messages can be sent to this port using [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 |
| /// cannot be paused. The data-handler must be set before the first |
| /// message is received, otherwise the message is lost. |
| /// |
| /// If [handler] is provided, it's set as the [RawReceivePort.handler]. |
| /// |
| /// The optional `debugName` parameter can be set to associate a name with |
| /// this port that can be displayed in tooling. |
| external factory RawReceivePort([Function? handler, String debugName = '']); |
| |
| /// Sets the handler that is invoked for every incoming message. |
| /// |
| /// The handler is invoked in the [Zone.root] zone. |
| /// If the handler should be invoked in the current zone, do: |
| /// ```dart |
| /// rawPort.handler = Zone.current.bind(actualHandler); |
| /// ``` |
| /// |
| /// The handler must be a function which can accept one argument |
| /// of the type of the messages sent to this port. |
| /// This means that if it is known that messages will all be [String]s, |
| /// a handler of type `void Function(String)` can be used. |
| /// The function is invoked dynamically with the actual messages, |
| /// and if this invocation fails, |
| /// the error becomes a top-level uncaught error in the [Zone.root] zone. |
| // TODO(44659): Change parameter type to `void Function(Never)` to only |
| // accept functions which can be called with one argument. |
| void set handler(Function? newHandler); |
| |
| /// Closes the port. |
| /// |
| /// After a call to this method, any incoming message is silently dropped. |
| /// The [handler] will never be called again. |
| void close(); |
| |
| /// Returns a [SendPort] that sends messages 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; |
| } |
| |
| /// An efficiently transferable sequence of byte values. |
| /// |
| /// A [TransferableTypedData] is created from a number of bytes. |
| /// This will take time proportional to the number of bytes. |
| /// |
| /// The [TransferableTypedData] can be moved between isolates, so |
| /// sending it through a send port will only take constant time. |
| /// |
| /// When sent this way, the local transferable can no longer be materialized, |
| /// and the received object is now the only way to materialize the data. |
| @Since("2.3.2") |
| abstract class TransferableTypedData { |
| /// Creates a new [TransferableTypedData] containing the bytes of [list]. |
| /// |
| /// It must be possible to create a single [Uint8List] containing the |
| /// bytes, so if there are more bytes than what the platform allows in |
| /// a single [Uint8List], then creation fails. |
| external factory TransferableTypedData.fromList(List<TypedData> list); |
| |
| /// Creates a new [ByteBuffer] containing the bytes stored in this [TransferableTypedData]. |
| /// |
| /// The [TransferableTypedData] is a cross-isolate single-use resource. |
| /// This method must not be called more than once on the same underlying |
| /// transferable bytes, even if the calls occur in different isolates. |
| ByteBuffer materialize(); |
| } |