1 | | | import 'cancellation_token.dart'; |
2 | | | import 'channel.dart'; |
3 | | | import 'squadron.dart'; |
4 | | | import 'squadron_error.dart'; |
5 | | | import 'worker.dart'; |
6 | | |
|
7 | | | /// Class used to communicate from a [Channel] to the [Worker]. Typically a [WorkerRequest] consists of a command ID |
8 | | | /// and a list of arguments. The [command] ID is used by the [Worker] to dispatch the [WorkerRequest] to the method |
9 | | | /// responsible for handling it. The [WorkerRequest] is effectively sent to the [Worker] by calling the |
10 | | | /// [WorkerRequest.send] or [WorkerRequest.stream] method. These methods will serialize the [WorkerRequest] as a |
11 | | | /// [Map] to be transfered from the client to the worker and contains: |
12 | | | /// * the serialized [Channel] to communicate with the [Worker] |
13 | | | /// * the [command] ID |
14 | | | /// * the command's arguments [args] |
15 | | | /// The command's arguments are passed as a list and should only contain primitive values or objects that can be |
16 | | | /// transfered across workers. For applications running on a VM platform, Dart objects should be safe according to |
17 | | | /// Dart's documentation of [SendPort.send]. The worker is responsible for deserializing the messages it receives |
18 | | | /// using the [WorkerRequest.deserialize] constructor. [WorkerRequest] also implements specific requests used |
19 | | | /// for worker startup, token cancellation, and worker termination. |
20 | | | class WorkerRequest { |
21 | | | static const _noArgs = []; |
22 | | |
|
23 | | 1 | WorkerRequest._(dynamic channelInfo, this.command, this.id, this.args, |
24 | | | this.logLevel, this._cancelToken) |
25 | | 1 | : client = WorkerChannel.deserialize(channelInfo); |
26 | | |
|
27 | | | /// Creates a new request with the specified [command] ID and optional arguments. |
28 | | 2 | WorkerRequest(dynamic channelInfo, int command, |
29 | | | [List args = _noArgs, CancellationToken? cancelToken]) |
30 | | 2 | : this._(channelInfo, command, null, args, null, cancelToken); |
31 | | |
|
32 | | | /// Creates a new start request. |
33 | | 2 | WorkerRequest.start(dynamic channelInfo, String id, [List args = _noArgs]) |
34 | | 3 | : this._(channelInfo, _connectCommand, id, args, Squadron.logLevel, null); |
35 | | |
|
36 | | | /// Creates a new cancel request. |
37 | | 2 | WorkerRequest.cancel(CancellationToken cancelToken) |
38 | | 2 | : this._(null, _cancelCommand, null, _noArgs, null, cancelToken); |
39 | | |
|
40 | | | /// Creates a new termination request. |
41 | | 2 | WorkerRequest.stop() |
42 | | 1 | : this._( |
43 | | 1 | null, _terminateCommand, null, _noArgs, null, null); |
44 | | |
|
45 | | | static const _$client = 'a'; |
46 | | | static const _$command = 'b'; |
47 | | | static const _$args = 'c'; |
48 | | | static const _$token = 'd'; |
49 | | | static const _$id = 'e'; |
50 | | | static const _$logLevel = 'f'; |
51 | | |
|
52 | | | /// Creates a new [WorkerRequest] from a message received by the worker. |
53 | | 1 | static WorkerRequest? deserialize(Map? message) => (message == null) |
54 | | | ? null |
55 | | 1 | : WorkerRequest._( |
56 | | 2 | message[_$client], |
57 | | 2 | message[_$command], |
58 | | 2 | message[_$id], |
59 | | 2 | message[_$args] ?? const [], |
60 | | 2 | message[_$logLevel], |
61 | | 3 | CancellationToken.deserialize(message[_$token])); |
62 | | |
|
63 | | | /// [WorkerRequest] serialization. |
64 | | 2 | Map<String, dynamic> serialize() { |
65 | | 2 | if (terminate) { |
66 | | 1 | return const {_$command: _terminateCommand}; |
67 | | 2 | } else if (connect) { |
68 | | 2 | return { |
69 | | 4 | _$client: client?.serialize(), |
70 | | 1 | _$command: _connectCommand, |
71 | | 3 | _$id: id, |
72 | | 3 | _$logLevel: logLevel, |
73 | | 5 | if (args.isNotEmpty) _$args: args, |
74 | | | }; |
75 | | | } else { |
76 | | 2 | return { |
77 | | 5 | if (client != null) _$client: client?.serialize(), |
78 | | 2 | _$command: command, |
79 | | 5 | if (args.isNotEmpty) _$args: args, |
80 | | 5 | if (_cancelToken != null) _$token: _cancelToken!.serialize(), |
81 | | | }; |
82 | | | } |
83 | | 1 | } |
84 | | |
|
85 | | | /// The client's [WorkerChannel]. |
86 | | | final WorkerChannel? client; |
87 | | |
|
88 | | | /// Cancellation token. |
89 | | 2 | CancellationToken? get cancelToken => _cancelToken; |
90 | | | CancellationToken? _cancelToken; |
91 | | |
|
92 | | | /// The [command]'s ID. |
93 | | | final int command; |
94 | | |
|
95 | | | /// The command's arguments, if any. |
96 | | | final List args; |
97 | | |
|
98 | | | /// The worker id set by the caller, used for logging/debugging purpose. |
99 | | | /// This is only used for connection commands. |
100 | | | final String? id; |
101 | | |
|
102 | | | /// The current Squadron log level. |
103 | | | /// This is set automaticallt and only used for connection commands. |
104 | | | final int? logLevel; |
105 | | |
|
106 | | | /// flag for start requests. |
107 | | 3 | bool get connect => command == _connectCommand; |
108 | | |
|
109 | | | /// flag for cancel requests. |
110 | | 3 | bool get cancel => command == _cancelCommand; |
111 | | |
|
112 | | | /// flag for termination requests. |
113 | | 4 | bool get terminate => command == _terminateCommand; |
114 | | |
|
115 | | | static const int _connectCommand = -1; |
116 | | | static const int _cancelCommand = -2; |
117 | | | static const int _terminateCommand = -3; |
118 | | | } |
119 | | |
|
120 | | | // private implementation internal to Squadron |
121 | | | extension WorkerRequestExt on WorkerRequest { |
122 | | 1 | void overrideCancelToken(CancellationToken token) { |
123 | | 5 | if (_cancelToken == null || _cancelToken!.id != token.id) { |
124 | | 0 | throw newSquadronError('cancellation token mismatch'); |
125 | | | } |
126 | | 1 | _cancelToken = token; |
127 | | | } |
128 | | | } |