import 'dart:async' as async; class RuntimeTaskRegistry { RuntimeTaskRegistry({required this.cancelledValue}); final T cancelledValue; final Set> _tasks = {}; final Map>> _tasksByScope = {}; bool _disposed = false; int get activeTaskCount => _tasks.length; int scopedTaskCount(String scope) => _tasksByScope[scope]?.length ?? 0; RuntimeTask create({String? scope}) { if (_disposed) { throw StateError('RuntimeTaskRegistry has been disposed'); } late final RuntimeTask task; task = RuntimeTask._( scope: scope, cancelledValue: cancelledValue, onComplete: _unregister, ); _tasks.add(task); if (scope != null) { _tasksByScope.putIfAbsent(scope, () => {}).add(task); } return task; } void cancelScope(String scope) { final tasks = _tasksByScope[scope]?.toList(growable: false) ?? const []; for (final task in tasks) { task.cancel(); } } void dispose() { if (_disposed) { return; } _disposed = true; final tasks = _tasks.toList(growable: false); for (final task in tasks) { task.cancel(); } _tasks.clear(); _tasksByScope.clear(); } void _unregister(RuntimeTask task) { _tasks.remove(task); final scope = task.scope; if (scope == null) { return; } final scopedTasks = _tasksByScope[scope]; scopedTasks?.remove(task); if (scopedTasks != null && scopedTasks.isEmpty) { _tasksByScope.remove(scope); } } } class RuntimeTask { RuntimeTask._({ required this.scope, required this.cancelledValue, required void Function(RuntimeTask task) onComplete, }) : _onComplete = onComplete; final String? scope; final T cancelledValue; final void Function(RuntimeTask task) _onComplete; final async.Completer _completer = async.Completer(); final Set _timers = {}; final List _cancelCallbacks = []; bool _cancelled = false; Future get future => _completer.future; bool get isCancelled => _cancelled; void addTimer(async.Timer timer) { if (_cancelled) { timer.cancel(); return; } _timers.add(timer); } void removeTimer(async.Timer timer) { _timers.remove(timer); } void addCancelCallback(void Function() callback) { if (_cancelled) { callback(); return; } _cancelCallbacks.add(callback); } void complete(T result) { if (_completer.isCompleted) { return; } _completer.complete(result); _onComplete(this); } void cancel() { if (_cancelled) { return; } _cancelled = true; for (final timer in _timers) { timer.cancel(); } _timers.clear(); for (final callback in _cancelCallbacks) { callback(); } _cancelCallbacks.clear(); complete(cancelledValue); } }