JJ_MKVMaker/lib/app/controller/job_queue.dart

65 lines
2.0 KiB
Dart

import 'dart:async';
import '../../l10n/tr.dart';
/// 무거운 작업 (AI 자막 · 자막 번역 · MKV 만들기 - CPU · 메모리를 많이 써서 하나씩) 의 대기열.
/// 약속: [enqueue] 는 작업 중이면 대기열에 넣고 아니면 바로 실행한다. 끝나면 다음 작업을 이어서 실행.
/// 돌려주는 Future 는 그 작업이 실제로 끝날 때 (대기 중에 [cancelPending] 으로 빠지면 바로) 끝난다.
/// 상태가 바뀔 때마다 [onChanged], 대기열에 넣을 때 [log].
class JobQueue {
final void Function() onChanged;
final void Function(String msg) log;
JobQueue({required this.onChanged, required this.log});
/// 작업 중인지 (화면 시험은 직접 정하기도 한다)
bool busy = false;
/// 실행 중인 작업 이름 (없으면 null)
String? current;
/// 대기 중인 작업 이름 (차례대로)
final List<String> pending = [];
final List<(Future<void> Function(), Completer<void>)> _runs = [];
Future<void> enqueue(String label, Future<void> Function() job) {
if (busy) {
final done = Completer<void>();
pending.add(label);
_runs.add((job, done));
log(trf('대기열에 추가: {0} (대기 {1}개)', [label, pending.length]));
onChanged();
return done.future;
}
return _run(label, job);
}
Future<void> _run(String label, Future<void> Function() job) async {
busy = true;
current = label;
onChanged();
try {
await job();
} finally {
busy = false;
current = null;
onChanged();
if (_runs.isNotEmpty) {
final (next, done) = _runs.removeAt(0);
final nextLabel = pending.removeAt(0);
unawaited(_run(nextLabel, next).then((_) => done.complete(), onError: done.completeError));
}
}
}
/// 대기 중인 작업을 모두 뺀다 (지금 작업은 그대로). 뺀 수
int cancelPending() {
final n = pending.length;
pending.clear();
for (final (_, done) in _runs) {
done.complete();
}
_runs.clear();
return n;
}
}