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