Line data Source code
1 : import 'dart:async'; 2 : import 'dart:collection'; 3 : 4 : import 'package:combine/src/combine_worker/combine_task_executor.dart'; 5 : import 'package:combine/src/combine_worker/combine_worker_manager.dart'; 6 : import 'package:combine/src/combine_worker/tasks.dart'; 7 : import 'package:combine/src/combine_worker_singleton.dart'; 8 : 9 : class NativeWorkerManager extends CombineWorkerManager { 10 1 : NativeWorkerManager({ 11 : required this.isolatesCount, 12 : required this.tasksPerIsolate, 13 : }); 14 : 15 : final int isolatesCount; 16 : final int tasksPerIsolate; 17 : final _taskExecutors = <CombineTaskExecutor>[]; 18 : final _tasksQueue = Queue<TaskInfo>(); 19 : final _initializationCompleter = Completer(); 20 : final _lastTaskCompleter = Completer(); 21 : var _isClosed = false; 22 : 23 1 : @override 24 : Future<void> initialize({ 25 : WorkerInitializer? initializer, 26 : required String isolatesPrefix, 27 : }) async { 28 : assert( 29 3 : !_initializationCompleter.isCompleted, 30 : "Internal error. Worker manager is initialized twice.", 31 : ); 32 1 : await Future.wait( 33 1 : [ 34 3 : for (var i = 0; i < isolatesCount; i++) 35 1 : CombineTaskExecutor.createExecutor( 36 1 : _tasksQueue, 37 1 : tasksPerIsolate, 38 : initializer, 39 2 : '$isolatesPrefix-${i + 1}', 40 2 : ).then(_addTaskExecutor), 41 : ], 42 : ); 43 2 : _initializationCompleter.complete(); 44 : } 45 : 46 1 : void _addTaskExecutor(CombineTaskExecutor taskExecutor) { 47 2 : _taskExecutors.add(taskExecutor); 48 1 : _tryToStartExecution(); 49 : } 50 : 51 1 : @override 52 : Future<T> execute<T>(ExecutableTask<T> task) async { 53 2 : assert(!_isClosed, "Internal error. Can't execute task in closed manager"); 54 : 55 1 : final completer = Completer(); 56 3 : _tasksQueue.add(TaskInfo(task, completer)); 57 1 : _tryToStartExecution(); 58 1 : final result = await completer.future; 59 1 : _markLastTaskAsCompletedIfNeeded(); 60 : return result; 61 : } 62 : 63 : /// Schedule execution. 64 : /// 65 : /// Firstly try to schedule for free executors which are not working. 66 : /// Then try to schedule for working but not busy executors. 67 1 : void _tryToStartExecution() { 68 1 : _taskExecutors 69 3 : .where((executor) => !executor.isWorking) 70 2 : .forEach(_triggerExecutor); 71 1 : _taskExecutors 72 3 : .where((executor) => !executor.isFullOfTasks) 73 2 : .forEach(_triggerExecutor); 74 : } 75 : 76 1 : void _triggerExecutor(CombineTaskExecutor executor) { 77 1 : executor.tryToExecuteActionIfAny(); 78 : } 79 : 80 1 : @override 81 : Future<void> close({bool waitForRemainingTasks = false}) async { 82 : // Ensure that initialization is completed and isolates are created. 83 2 : await _initializationCompleter.future; 84 : assert( 85 2 : _taskExecutors.isNotEmpty, 86 : "Internal error. " 87 : "Seems like initialization is incomplete and isolates are not created", 88 : ); 89 1 : _isClosed = true; 90 : 91 : if (waitForRemainingTasks) { 92 1 : _markLastTaskAsCompletedIfNeeded(); 93 2 : await _lastTaskCompleter.future; 94 : } else { 95 2 : for (final task in _tasksQueue) { 96 3 : task.resultCompleter.completeError(CombineWorkerClosedException()); 97 : } 98 : } 99 : 100 2 : for (final taskExecutor in _taskExecutors) { 101 1 : taskExecutor.close(); 102 : } 103 : } 104 : 105 : /// This function is used to wait for remaining tasks 106 : /// when worker is [close]d with `waitForRemainingTasks` parameter. 107 1 : void _markLastTaskAsCompletedIfNeeded() { 108 2 : final executorsAreWorking = _taskExecutors.any( 109 2 : (executor) => executor.isWorking, 110 : ); 111 3 : if (_isClosed && !executorsAreWorking && _tasksQueue.isEmpty) { 112 2 : _lastTaskCompleter.complete(); 113 : } 114 : } 115 : }