Line data Source code
1 : import 'dart:async';
2 : import 'dart:isolate';
3 :
4 : ///
5 : /// A helper class which starts an Isolate and lets you execute Functions.
6 : ///
7 :
8 : class IsolateManager {
9 : Isolate? _isolate;
10 : SendPort? _sendPort;
11 :
12 7 : SendPort get sendPort {
13 7 : if (!isReady) throw Exception("IsolateManager not ready");
14 7 : return _sendPort!;
15 : }
16 :
17 : final _receivePort = ReceivePort();
18 21 : late final _receivePortBroadcast = _receivePort.asBroadcastStream();
19 :
20 : final completer = Completer<void>();
21 :
22 21 : bool get isReady => completer.isCompleted;
23 :
24 : bool isInitializing = false;
25 :
26 7 : Future<void> _init() async {
27 7 : isInitializing = true;
28 14 : Stopwatch stopwatch = Stopwatch()..start();
29 7 : ReceivePort receivePort = ReceivePort();
30 14 : _isolate = await Isolate.spawn<SendPort>(
31 : _isolateEntry,
32 7 : receivePort.sendPort,
33 : );
34 14 : _sendPort = await receivePort.first;
35 28 : _sendPort!.send(_receivePort.sendPort);
36 :
37 14 : completer.complete();
38 :
39 7 : print(
40 14 : "IsolateManager initialized in ${stopwatch.elapsedMilliseconds}ms",
41 : );
42 7 : isInitializing = false;
43 : return;
44 : }
45 :
46 7 : Future<T> executeTask<T, A>(IsolateTask<T, A> task) async {
47 21 : if (!isReady && !isInitializing) _init();
48 14 : await completer.future;
49 :
50 14 : sendPort.send(task);
51 21 : final result = await _receivePortBroadcast.firstWhere((result) {
52 7 : if (result is IsolateResult) {
53 21 : return result.id == task.id;
54 : }
55 : return false;
56 : });
57 :
58 7 : if (result is IsolateResult) {
59 7 : return result.result;
60 : }
61 0 : throw Exception("IsolateResult not found");
62 : }
63 :
64 7 : void dispose() {
65 14 : _isolate?.kill(priority: Isolate.immediate);
66 : }
67 :
68 7 : static void _isolateEntry(
69 : SendPort sendPort,
70 : ) async {
71 7 : ReceivePort receivePort = ReceivePort();
72 14 : sendPort.send(receivePort.sendPort);
73 :
74 7 : Stream receiveBroadcast = receivePort.asBroadcastStream();
75 7 : SendPort responsePort = await receiveBroadcast.first;
76 :
77 14 : receiveBroadcast.listen((task) async {
78 7 : if (task is IsolateTask) {
79 : try {
80 7 : final result = await task.call();
81 14 : responsePort.send(IsolateResult(
82 : result: result,
83 7 : id: task.id,
84 : ));
85 : } catch (e, s) {
86 0 : print("task failed: $task; $e $s");
87 : }
88 : }
89 : });
90 : }
91 : }
92 :
93 : class IsolateTask<T, A> {
94 : final FutureOr<T> Function(A arg) task;
95 : final A argument;
96 : final String id;
97 :
98 7 : IsolateTask({
99 : required this.task,
100 : required this.argument,
101 21 : }) : id = DateTime.now().microsecondsSinceEpoch.toString();
102 :
103 28 : FutureOr<T> call() => task(argument);
104 : }
105 :
106 : class IsolateResult<T> {
107 : final T result;
108 : final String id;
109 :
110 7 : const IsolateResult({
111 : required this.result,
112 : required this.id,
113 : });
114 : }
|