Line data Source code
1 : // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
2 : // for details. All rights reserved. Use of this source code is governed by a
3 : // BSD-style license that can be found in the LICENSE file.
4 :
5 : import 'dart:async';
6 : import 'dart:isolate';
7 :
8 : import 'package:async/async.dart';
9 : import 'package:stack_trace/stack_trace.dart';
10 :
11 : import '../stream_channel.dart';
12 :
13 : /// A [StreamChannel] that communicates over a [ReceivePort]/[SendPort] pair,
14 : /// presumably with another isolate.
15 : ///
16 : /// The remote endpoint doesn't necessarily need to be running an
17 : /// [IsolateChannel]. This can be used with any two ports, although the
18 : /// [StreamChannel] semantics mean that this class will treat them as being
19 : /// paired (for example, closing the [sink] will cause the [stream] to stop
20 : /// emitting events).
21 : ///
22 : /// The underlying isolate ports have no notion of closing connections. This
23 : /// means that [stream] won't close unless [sink] is closed, and that closing
24 : /// [sink] won't cause the remote endpoint to close. Users should take care to
25 : /// ensure that they always close the [sink] of every [IsolateChannel] they use
26 : /// to avoid leaving dangling [ReceivePort]s.
27 : class IsolateChannel<T> extends StreamChannelMixin<T> {
28 : final Stream<T> stream;
29 : final StreamSink<T> sink;
30 :
31 : /// Connects to a remote channel that was created with
32 : /// [IsolateChannel.connectSend].
33 : ///
34 : /// These constructors establish a connection using only a single
35 : /// [SendPort]/[ReceivePort] pair, as long as each side uses one of the
36 : /// connect constructors.
37 : ///
38 : /// The connection protocol is guaranteed to remain compatible across versions
39 : /// at least until the next major version release. If the protocol is
40 : /// violated, the resulting channel will emit a single value on its stream and
41 : /// then close.
42 : factory IsolateChannel.connectReceive(ReceivePort receivePort) {
43 : // We can't use a [StreamChannelCompleter] here because we need the return
44 : // value to be an [IsolateChannel].
45 0 : var streamCompleter = new StreamCompleter<T>();
46 0 : var sinkCompleter = new StreamSinkCompleter<T>();
47 : var channel =
48 0 : new IsolateChannel<T>._(streamCompleter.stream, sinkCompleter.sink);
49 :
50 : // The first message across the ReceivePort should be a SendPort pointing to
51 : // the remote end. If it's not, we'll make the stream emit an error
52 : // complaining.
53 : var subscription;
54 0 : subscription = receivePort.listen((message) {
55 0 : if (message is SendPort) {
56 0 : var controller = new StreamChannelController<T>(
57 : allowForeignErrors: false, sync: true);
58 0 : new SubscriptionStream(subscription).pipe(controller.local.sink);
59 0 : controller.local.stream
60 0 : .listen((data) => message.send(data), onDone: receivePort.close);
61 :
62 0 : streamCompleter.setSourceStream(controller.foreign.stream);
63 0 : sinkCompleter.setDestinationSink(controller.foreign.sink);
64 : return;
65 : }
66 :
67 0 : streamCompleter.setError(
68 0 : new StateError('Unexpected Isolate response "$message".'),
69 0 : new Trace.current());
70 0 : sinkCompleter.setDestinationSink(new NullStreamSink<T>());
71 0 : subscription.cancel();
72 : });
73 :
74 : return channel;
75 : }
76 :
77 : /// Connects to a remote channel that was created with
78 : /// [IsolateChannel.connectReceive].
79 : ///
80 : /// These constructors establish a connection using only a single
81 : /// [SendPort]/[ReceivePort] pair, as long as each side uses one of the
82 : /// connect constructors.
83 : ///
84 : /// The connection protocol is guaranteed to remain compatible across versions
85 : /// at least until the next major version release.
86 : factory IsolateChannel.connectSend(SendPort sendPort) {
87 0 : var receivePort = new ReceivePort();
88 0 : sendPort.send(receivePort.sendPort);
89 0 : return new IsolateChannel(receivePort, sendPort);
90 : }
91 :
92 : /// Creates a stream channel that receives messages from [receivePort] and
93 : /// sends them over [sendPort].
94 : factory IsolateChannel(ReceivePort receivePort, SendPort sendPort) {
95 : var controller =
96 0 : new StreamChannelController<T>(allowForeignErrors: false, sync: true);
97 0 : receivePort.pipe(controller.local.sink);
98 0 : controller.local.stream
99 0 : .listen((data) => sendPort.send(data), onDone: receivePort.close);
100 0 : return new IsolateChannel._(
101 0 : controller.foreign.stream, controller.foreign.sink);
102 : }
103 :
104 0 : IsolateChannel._(this.stream, this.sink);
105 : }
|