graceful exit

This commit is contained in:
shenlong-tanwen
2026-01-19 19:53:21 +05:30
parent 39b2af1940
commit 74d463c19c
4 changed files with 56 additions and 50 deletions

View File

@@ -203,8 +203,8 @@ class BackgroundWorkerBgService extends BackgroundWorkerFlutterApi {
nativeSyncApi?.cancelHashing(),
LogService.I.dispose(),
Store.dispose(),
backgroundSyncManager?.cancel(immediate: true),
backgroundSyncManager?.cancelLocal(immediate: true),
backgroundSyncManager?.cancel(),
backgroundSyncManager?.cancelLocal(),
];
if (_isar.isOpen) {

View File

@@ -48,11 +48,11 @@ class BackgroundSyncManager {
this.onCloudIdSyncError,
});
Future<void> cancel({bool immediate = false}) async {
_syncTask!.cancel(immediate: immediate);
_syncWebsocketTask!.cancel(immediate: immediate);
_cloudIdSyncTask!.cancel(immediate: immediate);
_linkedAlbumSyncTask!.cancel(immediate: immediate);
Future<void> cancel() async {
_syncTask!.cancel();
_syncWebsocketTask!.cancel();
_cloudIdSyncTask!.cancel();
_linkedAlbumSyncTask!.cancel();
try {
await Future.wait(
@@ -73,9 +73,9 @@ class BackgroundSyncManager {
_linkedAlbumSyncTask = null;
}
Future<void> cancelLocal({bool immediate = false}) async {
_hashTask!.cancel(immediate: immediate);
_deviceAlbumSyncTask!.cancel(immediate: immediate);
Future<void> cancelLocal() async {
_hashTask!.cancel();
_deviceAlbumSyncTask!.cancel();
try {
await Future.wait([_hashTask?.future, _deviceAlbumSyncTask?.future].nonNulls);

View File

@@ -222,8 +222,8 @@ class AppLifeCycleNotifier extends StateNotifier<AppLifeCycleEnum> {
Future.wait([
_ref.read(backgroundWorkerLockServiceProvider).unlock(),
_ref.read(nativeSyncApiProvider).cancelHashing(),
_ref.read(backgroundSyncProvider).cancel(immediate: true),
_ref.read(backgroundSyncProvider).cancelLocal(immediate: true),
_ref.read(backgroundSyncProvider).cancel(),
_ref.read(backgroundSyncProvider).cancelLocal(),
]),
);
}

View File

@@ -19,7 +19,7 @@ import 'package:logging/logging.dart';
class CancellableTask<T> {
final Future<T?> future;
final void Function({bool immediate}) cancel;
final void Function() cancel;
const CancellableTask({required this.future, required this.cancel});
@@ -57,7 +57,7 @@ class _ResultMessage extends _IsolateMessage {
class _ErrorMessage extends _IsolateMessage {
final Object? error;
final StackTrace? stackTrace;
const _ErrorMessage(this.error, this.stackTrace);
const _ErrorMessage(this.error, [this.stackTrace]);
}
class _DoneMessage extends _IsolateMessage {
@@ -115,24 +115,14 @@ class _IsolateTaskRunner<T> {
}
}
void cancel({bool immediate = false}) {
void cancel() {
if (_isCancelled || _isCleanedUp) return;
_isCancelled = true;
dPrint(() => "[$debugLabel] Cancellation requested");
if (immediate) {
_isolate?.kill(priority: Isolate.immediate);
if (!_completer.isCompleted) {
_completer.completeError(Exception("Isolate task cancelled immediately"));
}
dPrint(() => "[$debugLabel] Isolate killed immediately");
_cleanup();
return;
}
_isolateSendPort?.send(const _CancelMessage());
_cleanupTimeoutTimer = Timer(const Duration(seconds: 2), () {
_cleanupTimeoutTimer = Timer(const Duration(seconds: 4), () {
if (!_isCleanedUp) {
dPrint(() => "[$debugLabel] Cleanup timeout - force killing isolate");
_isolate?.kill(priority: Isolate.immediate);
@@ -196,17 +186,35 @@ class _IsolateTaskRunner<T> {
Future<T?> get future => _completer.future;
}
Future<void> _cleanupResources<T>(ProviderContainer? ref, Isar isar, Drift drift, DriftLogger logDb) async {
try {
final cleanupFutures = <Future>[
Store.dispose(),
LogService.I.dispose(),
logDb.close(),
drift.close(),
if (isar.isOpen) isar.close().catchError((_) => false),
];
ref?.dispose();
await Future.wait(cleanupFutures).timeout(
const Duration(seconds: 2),
onTimeout: () {
dPrint(() => "Cleanup timeout - some resources may not be closed");
return [];
},
);
} catch (error, stack) {
dPrint(() => "Error during isolate cleanup: $error with stack: $stack");
}
}
Future<void> _isolateEntryPoint<T>(_IsolateTaskConfig<T> config) async {
final receivePort = ReceivePort();
config.mainSendPort.send(_InitMessage(receivePort.sendPort));
bool isCancelled = false;
final subscription = receivePort.listen((message) {
if (message is _CancelMessage) {
isCancelled = true;
}
});
ProviderContainer? ref;
final Isar isar;
final Drift drift;
@@ -226,6 +234,20 @@ Future<void> _isolateEntryPoint<T>(_IsolateTaskConfig<T> config) async {
return;
}
final subscription = receivePort.listen((message) async {
if (message is _CancelMessage) {
isCancelled = true;
try {
receivePort.close();
await _cleanupResources(ref, isar, drift, logDb);
} catch (error, stack) {
dPrint(() => "Error during isolate cancellation cleanup: $error with stack: $stack");
} finally {
config.mainSendPort.send(const _ErrorMessage("Isolate task cancelled"));
}
}
});
final log = Logger("IsolateWorker[${config.debugLabel}]");
try {
await runZonedGuarded(
@@ -259,24 +281,8 @@ Future<void> _isolateEntryPoint<T>(_IsolateTaskConfig<T> config) async {
} finally {
try {
receivePort.close();
final cleanupFutures = <Future>[
Store.dispose(),
LogService.I.dispose(),
logDb.close(),
drift.close(),
subscription.cancel(),
if (isar.isOpen) isar.close().catchError((_) => false),
];
ref?.dispose();
await Future.wait(cleanupFutures).timeout(
const Duration(seconds: 2),
onTimeout: () {
dPrint(() => "Cleanup timeout - some resources may not be closed");
return [];
},
);
unawaited(subscription.cancel());
await _cleanupResources(ref, isar, drift, logDb);
} catch (error, stack) {
dPrint(() => "Error during isolate cleanup: $error with stack: $stack");
} finally {