|
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