LCOV - code coverage report

Current view
top level - /src/native - _channel.dart
Test
lcov.info
Date
2022-04-02
Legend
Lines
hit
not hit
Branches
taken
not taken
# not executed
HitTotalCoverage
Lines929992.9%
Functions00-
Branches00-
Each row represents a line of source code
LineBranchHitsSource code
1import 'dart:async';
2import 'dart:isolate';
3
4import '../cancellation_token.dart';
5import '../channel.dart' show Channel, WorkerChannel;
6import '../squadron.dart';
7import '../squadron_exception.dart';
8import '../worker_exception.dart';
9import '../worker_request.dart';
10import '../worker_response.dart';
11
12class _SendPort {
13 /// [SendPort] to communicate with the [Isolate] if the channel is owned by the worker owner.
14 /// Otherwise, [SendPort] to return values to the client.
15 SendPort? _sendPort;
16
17 /// [Channel] serialization in Native world returns the [SendPort].
182 dynamic serialize() => _sendPort;
19
201 void _postRequest(WorkerRequest req) {
211 final message = req.serialize();
22 try {
232 _sendPort!.send(message);
24 } catch (ex) {
252 Squadron.severe('failed to post request $message: error $ex');
26 rethrow;
27 }
28 }
29
301 void _postResponse(WorkerResponse res) {
311 final message = res.serialize();
32 try {
332 _sendPort!.send(message);
34 } catch (ex) {
352 Squadron.severe('failed to post response $message: error $ex');
36 rethrow;
37 }
38 }
39}
40
41/// [Channel] implementation for the Native world.
42class _VmChannel extends _SendPort implements Channel {
431 _VmChannel._();
44
45 /// [Channel] sharing in Native world returns the same instance.
461 @override
47 Channel share() => this;
48
49 /// Sends a termination [WorkerRequest] to the [Isolate] and clears the [SendPort].
501 @override
51 FutureOr close() {
521 if (_sendPort != null) {
532 _postRequest(WorkerRequest.stop());
541 _sendPort = null;
55 }
56 }
57
58 /// Creates a [web.MessageChannel] and a [WorkerRequest] and sends it to the [web.Worker].
59 /// This method expects a single value from the [web.Worker].
601 @override
61 void notifyCancellation(CancellationToken token) {
621 if (token.cancelled) {
632 _postRequest(WorkerRequest.cancel(token));
64 }
65 }
66
67 /// creates a [ReceivePort] and a [WorkerRequest] and sends it to the [Isolate]
68 /// this method expects a single value from the [Isolate]
69 @override
701 Future<T> sendRequest<T>(int command, List args,
71 {CancellationToken? token}) async {
721 final receiver = ReceivePort();
733 _postRequest(WorkerRequest(receiver.sendPort, command, args, token));
743 final res = WorkerResponse.deserialize(await receiver.first);
751 return res.result as T;
76 }
77
78 /// Creates a [ReceivePort] and a [WorkerRequest] and sends it to the [Isolate].
79 /// This method expects a stream of values from the [Isolate].
80 /// The [Isolate] must send a [WorkerResponse.endOfStream] to close the [Stream].
811 @override
82 Stream<T> sendStreamingRequest<T>(int command, List args,
83 {CancellationToken? token}) {
841 final controller = StreamController<T>();
851 final receiver = ReceivePort();
862 receiver.listen((message) {
871 final res = WorkerResponse.deserialize(message);
881 if (res.endOfStream) {
891 controller.close();
901 receiver.close();
911 } else if (res.hasError) {
924 controller.addError(res.error!, res.error!.stackTrace);
931 controller.close();
941 receiver.close();
95 } else {
962 controller.add(res.result);
97 }
98 });
993 _postRequest(WorkerRequest(receiver.sendPort, command, args, token));
1001 return controller.stream;
101 }
102}
103
104/// [WorkerChannel] implementation for the native world.
105class _VmWorkerChannel extends _SendPort implements WorkerChannel {
1061 _VmWorkerChannel._();
107
108 /// Sends the [SendPort] to communicate with the [Isolate].
109 /// This method must be called by the [Isolate] upon startup.
1101 @override
111 void connect(Object channelInfo) {
1121 if (channelInfo is ReceivePort) {
1132 reply(channelInfo.sendPort);
114 } else {
1150 throw WorkerException(
1160 'invalid channelInfo ${channelInfo.runtimeType}: ReceivePort expected');
117 }
118 }
119
120 /// Sends a [WorkerResponse] with the specified data to the worker client.
121 /// This method must be called from the [Isolate] only.
1221 @override
1232 void reply(dynamic data) => _postResponse(WorkerResponse(data));
124
125 /// Sends a [WorkerResponse.closeStream] to the worker client.
126 /// This method must be called from the [Isolate] only.
1271 @override
1281 void closeStream() => _postResponse(WorkerResponse.closeStream);
129
130 /// Sends the [WorkerException] to the worker client.
131 /// This method must be called from the [Isolate] only.
1321 @override
133 void error(SquadronException error) {
1342 Squadron.finer('replying with error: $error');
1352 _postResponse(WorkerResponse.withError(error));
136 }
137}
138
139/// Stub implementations.
140
141int _counter = 0;
1421String _getId() {
1431 _counter++;
1442 return '${Squadron.id}.$_counter';
145}
146
147/// Starts an [Isolate] using the [entryPoint] and sends a start [WorkerRequest] with [startArguments].
148/// The future completes after the [Isolate]'s main program has provided the [SendPort] via [_VmWorkerChannel.connect].
1491Future<Channel> openChannel(dynamic entryPoint, List startArguments) async {
1501 final completer = Completer<Channel>();
1511 final channel = _VmChannel._();
1521 final receiver = ReceivePort();
1531 Isolate.spawn(
154 entryPoint,
1553 WorkerRequest.start(receiver.sendPort, _getId(), startArguments)
1561 .serialize(),
157 paused: true)
1582 .then((isolate) {
1591 final exitPort = ReceivePort();
1602 exitPort.listen((message) {
1611 channel.close();
162 });
1632 isolate.addOnExitListener(exitPort.sendPort);
1641 final errorPort = ReceivePort();
1652 errorPort.listen((message) {
1661 dynamic error = message[0];
1671 if (error is String) {
1681 error = SquadronException.fromString(error);
169 }
1701 if (error is! SquadronException) {
1710 error = SquadronException.from(
1720 error: message[0] ?? 'unspecified error',
1730 stackTrace: SquadronException.loadStackTrace(message[1]));
174 }
1751 if (!completer.isCompleted) {
1761 completer.completeError(error);
177 } else {
1780 Squadron.warning('unhandled error $error');
179 }
180 });
1812 isolate.addErrorListener(errorPort.sendPort);
1822 isolate.resume(isolate.pauseCapability!);
1833 Squadron.config('created Isolate #${isolate.hashCode}');
1843 receiver.first.then((message) {
1851 final response = WorkerResponse.deserialize(message);
1861 if (!completer.isCompleted) {
1871 if (response.hasError) {
1881 isolate.kill(priority: Isolate.immediate);
1891 Squadron.severe(
1903 'connection to Isolate #${isolate.hashCode} failed: ${response.error}');
1914 completer.completeError(response.error!, response.error!.stackTrace);
192 } else {
1932 channel._sendPort = response.result;
1943 Squadron.config('connected to Isolate #${isolate.hashCode}');
1951 completer.complete(channel);
196 }
197 }
198 });
1991 }).catchError((error, stackTrace) {
2000 completer.completeError(error, stackTrace);
201 });
2021 return completer.future;
203}
204
205/// Creates a [_VmChannel] from a [SendPort].
2061Channel? deserializeChannel(dynamic channelInfo) =>
2072 (channelInfo == null) ? null : (_VmChannel._().._sendPort = channelInfo);
208
209/// Creates a [_VmWorkerChannel] from a [SendPort].
2101WorkerChannel? deserializeWorkerChannel(dynamic channelInfo) =>
211 (channelInfo == null)
212 ? null
2132 : (_VmWorkerChannel._().._sendPort = channelInfo);
Choose Features