130 lines
2.9 KiB
Dart
130 lines
2.9 KiB
Dart
import 'dart:async' as async;
|
|
|
|
class RuntimeTaskRegistry<T> {
|
|
RuntimeTaskRegistry({required this.cancelledValue});
|
|
|
|
final T cancelledValue;
|
|
final Set<RuntimeTask<T>> _tasks = {};
|
|
final Map<String, Set<RuntimeTask<T>>> _tasksByScope = {};
|
|
bool _disposed = false;
|
|
|
|
int get activeTaskCount => _tasks.length;
|
|
|
|
int scopedTaskCount(String scope) => _tasksByScope[scope]?.length ?? 0;
|
|
|
|
RuntimeTask<T> create({String? scope}) {
|
|
if (_disposed) {
|
|
throw StateError('RuntimeTaskRegistry has been disposed');
|
|
}
|
|
|
|
late final RuntimeTask<T> task;
|
|
task = RuntimeTask<T>._(
|
|
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<T> 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<T> {
|
|
RuntimeTask._({
|
|
required this.scope,
|
|
required this.cancelledValue,
|
|
required void Function(RuntimeTask<T> task) onComplete,
|
|
}) : _onComplete = onComplete;
|
|
|
|
final String? scope;
|
|
final T cancelledValue;
|
|
final void Function(RuntimeTask<T> task) _onComplete;
|
|
final async.Completer<T> _completer = async.Completer<T>();
|
|
final Set<async.Timer> _timers = {};
|
|
final List<void Function()> _cancelCallbacks = [];
|
|
bool _cancelled = false;
|
|
|
|
Future<T> 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);
|
|
}
|
|
}
|