Line data Source code
1 : import 'dart:async'; 2 : import 'dart:collection'; 3 : 4 : import 'package:combine/src/combine_info.dart'; 5 : import 'package:combine/src/combine_isolate/combine_isolate.dart'; 6 : import 'package:combine/src/combine_singleton.dart'; 7 : import 'package:combine/src/combine_worker/tasks.dart'; 8 : import 'package:combine/src/combine_worker_singleton.dart'; 9 : import 'package:combine/src/id_generator.dart/id_generator.dart'; 10 : import 'package:combine/src/isolate_context.dart'; 11 : import 'package:combine/src/isolate_messenger/isolate_messenger.dart'; 12 : 13 : class CombineTaskExecutor { 14 1 : CombineTaskExecutor._( 15 : this._combineInfo, 16 : this._tasksQueue, 17 : this._tasksPerIsolate, [ 18 : IdGenerator? idGenerator, 19 1 : ]) : _idGenerator = idGenerator = IdGenerator(), 20 1 : _isolateMessenger = _combineInfo.messenger; 21 : 22 : final Queue<TaskInfo> _tasksQueue; 23 : final CombineInfo _combineInfo; 24 : final IdGenerator _idGenerator; 25 : final IsolateMessenger _isolateMessenger; 26 : final int _tasksPerIsolate; 27 : final List<Completer> _currentTasksCompleters = []; 28 : 29 5 : bool get isFullOfTasks => _currentTasksCompleters.length == _tasksPerIsolate; 30 3 : bool get isWorking => _currentTasksCompleters.isNotEmpty; 31 : 32 1 : static Future<CombineTaskExecutor> createExecutor( 33 : Queue<TaskInfo> actionsQueue, 34 : int tasksPerIsolate, 35 : WorkerInitializer? initializer, 36 : String debugName, 37 : ) async { 38 2 : final combineInfo = await Combine().spawn( 39 : _isolateEntryPoint, 40 : argument: initializer, 41 : errorsAreFatal: false, 42 : debugName: debugName, 43 : ); 44 1 : return CombineTaskExecutor._(combineInfo, actionsQueue, tasksPerIsolate); 45 : } 46 : 47 : /// Executes actions from [_tasksQueue] if any and if it is not working. 48 1 : Future<void> tryToExecuteActionIfAny() async { 49 3 : if (_tasksQueue.isNotEmpty && !isFullOfTasks) { 50 2 : final task = _tasksQueue.removeFirst(); 51 3 : _currentTasksCompleters.add(task.resultCompleter); 52 1 : await _sendMessageAndReceiveResponse(task); 53 3 : _currentTasksCompleters.remove(task.resultCompleter); 54 2 : unawaited(tryToExecuteActionIfAny()); 55 : } 56 : } 57 : 58 : /// Kills [CombineIsolate]. 59 1 : void close() { 60 3 : _combineInfo.isolate.kill(); 61 2 : for (final currentTaskCompleter in _currentTasksCompleters) { 62 2 : currentTaskCompleter.completeError(CombineWorkerClosedException()); 63 : } 64 : } 65 : 66 1 : Future<void> _sendMessageAndReceiveResponse(TaskInfo taskInfo) async { 67 : try { 68 2 : final taskId = _idGenerator(); 69 4 : _isolateMessenger.send(_ExecutableTaskRequest(taskId, taskInfo.task)); 70 3 : final response = await _isolateMessenger.messages.firstWhere( 71 4 : (msg) => msg is _ExecutableTaskResponse && msg.taskId == taskId, 72 : ) as _ExecutableTaskResponse; 73 3 : response.taskResponse.complete(taskInfo.resultCompleter); 74 : } catch (error, stackTrace) { 75 2 : taskInfo.resultCompleter.completeError(error, stackTrace); 76 : } 77 : } 78 : 79 1 : static Future<void> _isolateEntryPoint(IsolateContext context) async { 80 1 : final initializer = context.argument; 81 1 : final messenger = context.messenger; 82 : 83 1 : if (initializer is WorkerInitializer) { 84 1 : await initializer(); 85 : } 86 3 : await for (final request in messenger.messages) { 87 1 : if (request is _ExecutableTaskRequest) { 88 : late TaskResponse taskResponse; 89 : try { 90 3 : taskResponse = TaskValueResponse(await request.task.execute()); 91 : } catch (error, stackTrace) { 92 1 : taskResponse = TaskErrorResponse(error, stackTrace); 93 : } 94 : try { 95 3 : messenger.send(_ExecutableTaskResponse(request.taskId, taskResponse)); 96 : } catch (error, stackTrace) { 97 1 : messenger.send( 98 1 : _ExecutableTaskResponse( 99 1 : request.taskId, 100 1 : TaskErrorResponse(error, stackTrace), 101 : ), 102 : ); 103 : } 104 : } 105 : } 106 : } 107 : } 108 : 109 : class _ExecutableTaskRequest<T> { 110 1 : _ExecutableTaskRequest(this.taskId, this.task); 111 : 112 : final int taskId; 113 : final ExecutableTask<T> task; 114 : } 115 : 116 : class _ExecutableTaskResponse<T> { 117 1 : _ExecutableTaskResponse(this.taskId, this.taskResponse); 118 : 119 : final int taskId; 120 : final TaskResponse<T> taskResponse; 121 : }