mirror of
https://github.com/immich-app/immich.git
synced 2026-01-20 00:30:54 -08:00
graceful exit
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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(),
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user