|
1 | | -import 'dart:async'; |
2 | | -import 'dart:collection'; |
3 | | -import 'dart:isolate'; |
4 | | - |
5 | | -abstract class PortClient { |
6 | | - Future<T> post<T>(Object message); |
7 | | - void fire(Object message); |
8 | | - |
9 | | - factory PortClient.parent() { |
10 | | - return ParentPortClient(); |
11 | | - } |
12 | | - |
13 | | - factory PortClient.child(SendPort upstream) { |
14 | | - return ChildPortClient(upstream); |
15 | | - } |
16 | | -} |
17 | | - |
18 | | -class ParentPortClient implements PortClient { |
19 | | - late Future<SendPort> sendPortFuture; |
20 | | - SendPort? sendPort; |
21 | | - final ReceivePort _receivePort = ReceivePort(); |
22 | | - final ReceivePort _errorPort = ReceivePort(); |
23 | | - bool closed = false; |
24 | | - Object? _closeError; |
25 | | - String? _isolateDebugName; |
26 | | - int _nextId = 1; |
27 | | - |
28 | | - Map<int, Completer<Object?>> handlers = HashMap(); |
29 | | - |
30 | | - ParentPortClient() { |
31 | | - final initCompleter = Completer<SendPort>.sync(); |
32 | | - sendPortFuture = initCompleter.future; |
33 | | - sendPortFuture.then((value) { |
34 | | - sendPort = value; |
35 | | - }); |
36 | | - _receivePort.listen((message) { |
37 | | - if (message is _InitMessage) { |
38 | | - assert(!initCompleter.isCompleted); |
39 | | - initCompleter.complete(message.port); |
40 | | - } else if (message is _PortChannelResult) { |
41 | | - final handler = handlers.remove(message.requestId); |
42 | | - assert(handler != null); |
43 | | - if (message.success) { |
44 | | - handler!.complete(message.result); |
45 | | - } else { |
46 | | - handler!.completeError(message.error, message.stackTrace); |
47 | | - } |
48 | | - } else if (message == _closeMessage) { |
49 | | - close(); |
50 | | - } |
51 | | - }, onError: (e) { |
52 | | - if (!initCompleter.isCompleted) { |
53 | | - initCompleter.completeError(e); |
54 | | - } |
55 | | - |
56 | | - close(); |
57 | | - }, onDone: () { |
58 | | - if (!initCompleter.isCompleted) { |
59 | | - initCompleter.completeError(ClosedException()); |
60 | | - } |
61 | | - close(); |
62 | | - }); |
63 | | - _errorPort.listen((message) { |
64 | | - final [error, stackTraceString] = message; |
65 | | - final stackTrace = stackTraceString == null |
66 | | - ? null |
67 | | - : StackTrace.fromString(stackTraceString); |
68 | | - if (!initCompleter.isCompleted) { |
69 | | - initCompleter.completeError(error, stackTrace); |
70 | | - } |
71 | | - _close(IsolateError(cause: error, isolateDebugName: _isolateDebugName), |
72 | | - stackTrace); |
73 | | - }); |
74 | | - } |
75 | | - |
76 | | - Future<void> get ready async { |
77 | | - await sendPortFuture; |
78 | | - } |
79 | | - |
80 | | - void _cancelAll(Object error, [StackTrace? stackTrace]) { |
81 | | - var handlers = this.handlers; |
82 | | - this.handlers = {}; |
83 | | - for (var message in handlers.values) { |
84 | | - message.completeError(error, stackTrace); |
85 | | - } |
86 | | - } |
87 | | - |
88 | | - @override |
89 | | - Future<T> post<T>(Object message) async { |
90 | | - if (closed) { |
91 | | - throw _closeError ?? const ClosedException(); |
92 | | - } |
93 | | - var completer = Completer<T>.sync(); |
94 | | - var id = _nextId++; |
95 | | - handlers[id] = completer; |
96 | | - final port = sendPort ?? await sendPortFuture; |
97 | | - port.send(_RequestMessage(id, message, null)); |
98 | | - return await completer.future; |
99 | | - } |
100 | | - |
101 | | - @override |
102 | | - void fire(Object message) async { |
103 | | - if (closed) { |
104 | | - throw _closeError ?? ClosedException(); |
105 | | - } |
106 | | - final port = sendPort ?? await sendPortFuture; |
107 | | - port.send(_FireMessage(message)); |
108 | | - } |
109 | | - |
110 | | - RequestPortServer server() { |
111 | | - return RequestPortServer(_receivePort.sendPort); |
112 | | - } |
113 | | - |
114 | | - void _close([Object? error, StackTrace? stackTrace]) { |
115 | | - if (!closed) { |
116 | | - closed = true; |
117 | | - |
118 | | - _receivePort.close(); |
119 | | - _errorPort.close(); |
120 | | - if (error == null) { |
121 | | - _cancelAll(const ClosedException()); |
122 | | - } else { |
123 | | - _closeError = error; |
124 | | - _cancelAll(error, stackTrace); |
125 | | - } |
126 | | - } |
127 | | - } |
128 | | - |
129 | | - void close() { |
130 | | - _close(); |
131 | | - } |
132 | | - |
133 | | - tieToIsolate(Isolate isolate) { |
134 | | - _isolateDebugName = isolate.debugName; |
135 | | - isolate.addErrorListener(_errorPort.sendPort); |
136 | | - isolate.addOnExitListener(_receivePort.sendPort, response: _closeMessage); |
137 | | - } |
138 | | -} |
139 | | - |
140 | | -class SerializedPortClient { |
141 | | - final SendPort sendPort; |
142 | | - |
143 | | - SerializedPortClient(this.sendPort); |
144 | | - |
145 | | - ChildPortClient open() { |
146 | | - return ChildPortClient(sendPort); |
147 | | - } |
148 | | -} |
149 | | - |
150 | | -class ChildPortClient implements PortClient { |
151 | | - final SendPort sendPort; |
152 | | - final ReceivePort receivePort = ReceivePort(); |
153 | | - int _nextId = 1; |
154 | | - bool closed = false; |
155 | | - |
156 | | - final Map<int, Completer<Object?>> handlers = HashMap(); |
157 | | - |
158 | | - ChildPortClient(this.sendPort) { |
159 | | - receivePort.listen((message) { |
160 | | - if (message is _PortChannelResult) { |
161 | | - final handler = handlers.remove(message.requestId); |
162 | | - assert(handler != null); |
163 | | - if (message.success) { |
164 | | - handler!.complete(message.result); |
165 | | - } else { |
166 | | - handler!.completeError(message.error, message.stackTrace); |
167 | | - } |
168 | | - } |
169 | | - }); |
170 | | - } |
171 | | - |
172 | | - @override |
173 | | - Future<T> post<T>(Object message) async { |
174 | | - if (closed) { |
175 | | - throw const ClosedException(); |
176 | | - } |
177 | | - var completer = Completer<T>.sync(); |
178 | | - var id = _nextId++; |
179 | | - handlers[id] = completer; |
180 | | - sendPort.send(_RequestMessage(id, message, receivePort.sendPort)); |
181 | | - return await completer.future; |
182 | | - } |
183 | | - |
184 | | - @override |
185 | | - void fire(Object message) { |
186 | | - if (closed) { |
187 | | - throw ClosedException(); |
188 | | - } |
189 | | - sendPort.send(_FireMessage(message)); |
190 | | - } |
191 | | - |
192 | | - void _cancelAll(Object error) { |
193 | | - var handlers = HashMap<int, Completer<Object?>>.from(this.handlers); |
194 | | - this.handlers.clear(); |
195 | | - for (var message in handlers.values) { |
196 | | - message.completeError(error); |
197 | | - } |
198 | | - } |
199 | | - |
200 | | - void close() { |
201 | | - closed = true; |
202 | | - _cancelAll(const ClosedException()); |
203 | | - receivePort.close(); |
204 | | - } |
205 | | -} |
206 | | - |
207 | | -class RequestPortServer { |
208 | | - final SendPort port; |
209 | | - |
210 | | - RequestPortServer(this.port); |
211 | | - |
212 | | - open(Future<Object?> Function(Object? message) handle) { |
213 | | - return PortServer.forSendPort(port, handle); |
214 | | - } |
215 | | -} |
216 | | - |
217 | | -class PortServer { |
218 | | - final ReceivePort _receivePort = ReceivePort(); |
219 | | - final Future<Object?> Function(Object? message) handle; |
220 | | - final SendPort? replyPort; |
221 | | - |
222 | | - PortServer(this.handle) : replyPort = null { |
223 | | - _init(); |
224 | | - } |
225 | | - |
226 | | - PortServer.forSendPort(SendPort port, this.handle) : replyPort = port { |
227 | | - port.send(_InitMessage(_receivePort.sendPort)); |
228 | | - _init(); |
229 | | - } |
230 | | - |
231 | | - SendPort get sendPort { |
232 | | - return _receivePort.sendPort; |
233 | | - } |
234 | | - |
235 | | - SerializedPortClient client() { |
236 | | - return SerializedPortClient(sendPort); |
237 | | - } |
238 | | - |
239 | | - void close() { |
240 | | - _receivePort.close(); |
241 | | - } |
242 | | - |
243 | | - void _init() { |
244 | | - _receivePort.listen((request) async { |
245 | | - if (request is _FireMessage) { |
246 | | - handle(request.message); |
247 | | - } else if (request is _RequestMessage) { |
248 | | - if (request.id == 0) { |
249 | | - // Fire and forget |
250 | | - handle(request.message); |
251 | | - } else { |
252 | | - final replyPort = request.reply ?? this.replyPort; |
253 | | - try { |
254 | | - var result = await handle(request.message); |
255 | | - replyPort!.send(_PortChannelResult.success(request.id, result)); |
256 | | - } catch (e, stacktrace) { |
257 | | - replyPort! |
258 | | - .send(_PortChannelResult.error(request.id, e, stacktrace)); |
259 | | - } |
260 | | - } |
261 | | - } |
262 | | - }); |
263 | | - } |
264 | | -} |
265 | | - |
266 | | -const _closeMessage = '_Close'; |
267 | | - |
268 | | -class _InitMessage { |
269 | | - final SendPort port; |
270 | | - |
271 | | - _InitMessage(this.port); |
272 | | -} |
273 | | - |
274 | | -class _FireMessage { |
275 | | - final Object message; |
276 | | - |
277 | | - const _FireMessage(this.message); |
278 | | -} |
279 | | - |
280 | | -class _RequestMessage { |
281 | | - final int id; |
282 | | - final Object message; |
283 | | - final SendPort? reply; |
284 | | - |
285 | | - _RequestMessage(this.id, this.message, this.reply); |
286 | | -} |
287 | | - |
288 | | -class ClosedException implements Exception { |
289 | | - const ClosedException(); |
290 | | - |
291 | | - @override |
292 | | - String toString() { |
293 | | - return 'ClosedException'; |
294 | | - } |
295 | | -} |
296 | | - |
297 | | -class IsolateError extends Error { |
298 | | - final Object cause; |
299 | | - final String? isolateDebugName; |
300 | | - |
301 | | - IsolateError({required this.cause, this.isolateDebugName}); |
302 | | - |
303 | | - @override |
304 | | - String toString() { |
305 | | - if (isolateDebugName != null) { |
306 | | - return 'IsolateError in $isolateDebugName: $cause'; |
307 | | - } else { |
308 | | - return 'IsolateError: $cause'; |
309 | | - } |
310 | | - } |
311 | | -} |
312 | | - |
313 | | -class _PortChannelResult<T> { |
314 | | - final int requestId; |
315 | | - final bool success; |
316 | | - final T? _result; |
317 | | - final Object? _error; |
318 | | - final StackTrace? stackTrace; |
319 | | - |
320 | | - const _PortChannelResult.success(this.requestId, T result) |
321 | | - : success = true, |
322 | | - _error = null, |
323 | | - stackTrace = null, |
324 | | - _result = result; |
325 | | - const _PortChannelResult.error(this.requestId, Object error, |
326 | | - [this.stackTrace]) |
327 | | - : success = false, |
328 | | - _result = null, |
329 | | - _error = error; |
330 | | - |
331 | | - T get value { |
332 | | - if (success) { |
333 | | - return _result as T; |
334 | | - } else { |
335 | | - if (_error != null && stackTrace != null) { |
336 | | - Error.throwWithStackTrace(_error, stackTrace!); |
337 | | - } else { |
338 | | - throw _error!; |
339 | | - } |
340 | | - } |
341 | | - } |
342 | | - |
343 | | - T get result { |
344 | | - assert(success); |
345 | | - return _result as T; |
346 | | - } |
347 | | - |
348 | | - Object get error { |
349 | | - assert(!success); |
350 | | - return _error!; |
351 | | - } |
352 | | -} |
| 1 | +export 'port_channel_native.dart' |
| 2 | + if (dart.library.js_interop) 'port_channel_stub.dart'; |
0 commit comments