LCOV - code coverage report

Current view
top level - src/_impl/web - _channel.dart
Test
lcov.info
Date
2024-11-13
Legend
Lines
hit
not hit
Branches
taken
not taken
# not executed
HitTotalCoverage
Lines728386.7%
Functions00-
Branches00-
Each row represents a line of source code
LineBranchHitsSource code
1import 'dart:async';
2import 'dart:js_interop';
3
4import 'package:logger/web.dart';
5import 'package:web/web.dart' as web;
6
7import '../../channel.dart';
8import '../../exceptions/exception_manager.dart';
9import '../../exceptions/squadron_error.dart';
10import '../../exceptions/squadron_exception.dart';
11import '../../exceptions/worker_exception.dart';
12import '../../tokens/_squadron_cancelation_token.dart';
13import '../../typedefs.dart';
14import '../../worker/worker_channel.dart';
15import '../../worker/worker_request.dart';
16import '../../worker/worker_response.dart';
17import '../xplat/_disconnected_channel.dart';
18import '../xplat/_result_stream.dart';
19import '../xplat/_transferables.dart';
20import '_entry_point_uri.dart';
21import '_event_buffer.dart';
22import '_patch.dart';
23import '_uri_checker.dart';
24
25part '_channel_impl.dart';
26
27/// Stub implementations
28
29/// Starts a [web.Worker] using the [entryPoint] and sends a start
30/// [WorkerRequest] with [startArguments]. The future completes after the
31/// [web.Worker]'s main program has provided the [web.MessagePort] via
32/// [WorkerChannel.connect].
3310Future<Channel> openChannel(
34 EntryPoint entryPoint,
35 ExceptionManager exceptionManager,
36 Logger? logger,
37 List startArguments, [
38 PlatformThreadHook? hook,
39]) async {
4010 final completer = Completer<Channel>();
41 final ready = Completer<bool>();
42 Channel? channel;
43
44 final com = web.MessageChannel();
4510 final webEntryPoint = EntryPointUri.from(entryPoint);
4610 late web.Worker worker;
47
483 void fail(SquadronException ex) {
493 if (!ready.isCompleted) ready.completeError(ex);
503 if (!completer.isCompleted) completer.completeError(ex);
513 }
52
539 void success(Channel channel) {
549 if (!ready.isCompleted) {
550 throw SquadronErrorExt.create('Invalid state: worker is not ready');
56 }
579 if (!completer.isCompleted) completer.complete(channel);
589 }
59
603 try {
6110 worker = web.Worker(webEntryPoint.uri.toJS);
62
632 void $errorHandler(web.ErrorEvent? e) {
642 final err = getError(e), error = SquadronErrorExt.create(err.toString());
652 logger?.e(() => 'Connection to Web Worker failed: $error');
662 fail(error);
67
682 UriChecker.exists(entryPoint).then((found) {
692 try {
702 final msg = (e != null)
712 ? '$entryPoint => ${err.runtimeType} $err [${e.filename}(${e.lineno})]'
720 : '$entryPoint => ${err.runtimeType} $err';
731 logger?.e(() => 'Unhandled error from Web Worker: $msg.');
740 if (!found) {
752 logger?.e(() => 'It seems no Web Worker lives at $entryPoint.');
76 }
77 } catch (_) {
78 // ignore
79 }
802 });
812 }
82
8310 worker.onerror = $errorHandler.toJS;
8410 worker.onmessageerror = $errorHandler.toJS;
85
86 final disconnected = DisconnectedChannel(exceptionManager, logger);
87
8810 worker.onmessage = (web.MessageEvent? e) {
8910 try {
9010 final response = WorkerResponseExt.from(getMessageEventData(e) as List);
9110 if (!response.unwrapInPlace(disconnected)) {
920 return;
93 }
94
95 final error = response.error;
9610 if (error != null) {
972 logger?.e(() => 'Connection to Web Worker failed: $error');
982 fail(error);
999 } else if (!ready.isCompleted) {
1002 logger?.t('Web Worker is ready');
1019 ready.complete(response.result);
102 }
1030 } catch (ex, st) {
1040 return fail(SquadronException.from(ex, st));
105 }
10610 }.toJS;
107
10810 final res = await ready.future;
109 if (!res) {
1100 throw SquadronErrorExt.create('Web Worker is not ready');
111 }
112
113 final startRequest = WorkerRequest.start(com.port2, startArguments);
114
1159 com.port1.onmessage = (web.MessageEvent e) {
1169 final response = WorkerResponseExt.from(getMessageEventData(e) as List);
1179 if (!response.unwrapInPlace(disconnected)) {
1189 return;
119 }
120
121 final error = response.error;
1229 if (error != null) {
1232 logger?.e(() => 'Connection to Web Worker failed: $error');
1242 fail(error);
125 } else if (response.endOfStream) {
1261 logger?.w('Disconnecting from Isolate');
1271 channel?.close();
1289 } else if (!completer.isCompleted) {
1292 logger?.t('Connected to Web Worker');
1309 channel = _WebChannel._(response.result, logger, exceptionManager);
1319 success(channel!);
132 } else {
1332 logger?.d(() => 'Unexpected response: $response');
134 }
1359 }.toJS;
136
1370 try {
1389 final data = startRequest.wrapInPlace();
139 final msg = data.jsify();
1409 final transfer = Transferables.get(data);
141 if (transfer == null || transfer.isEmpty) {
142 worker.postMessage(msg);
143 } else {
1449 final jsTransfer = transfer.jsify() as JSArray;
145 worker.postMessage(msg, jsTransfer);
146 }
1470 } catch (ex, st) {
1482 logger?.e(() => 'Failed to post connection request $startRequest: $ex');
1490 throw SquadronErrorExt.create(
1500 'Failed to post connection request: $ex', st);
151 }
152
1532 try {
1549 final channel = await completer.future;
1559 await hook?.call(worker);
1562 logger?.t('Created Web Worker for $entryPoint');
1579 return channel;
158 } catch (ex) {
1592 logger?.e(() => 'Connection to Isolate failed: $ex');
1602 rethrow;
161 }
1623 } catch (ex, st) {
1633 ready.future.ignore();
1643 completer.future.ignore();
1651 logger?.t('Failed to create Web Worker for $entryPoint');
166 com.port1.close();
167 com.port2.close();
168 worker.terminate();
1693 throw SquadronException.from(ex, st);
170 } finally {
171 webEntryPoint.release();
172 }
17310}
174
175/// Creates a [_WebChannel] from a [web.MessagePort].
176Channel? deserialize(PlatformChannel? channelInfo,
177 [Logger? logger, ExceptionManager? exceptionManager]) =>
178 (channelInfo == null)
179 ? null
180 : _WebChannel._(
181 channelInfo,
182 logger,
183 exceptionManager ?? ExceptionManager(),
184 );
Choose Features