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