diff --git a/.changes/ensure-disposal-completion b/.changes/ensure-disposal-completion new file mode 100644 index 00000000..f1f20f85 --- /dev/null +++ b/.changes/ensure-disposal-completion @@ -0,0 +1 @@ +patch type="fixed" "Ensure all dispose functions are executed even if one throws" diff --git a/lib/src/support/disposable.dart b/lib/src/support/disposable.dart index 77c90c89..5d1ebeb5 100644 --- a/lib/src/support/disposable.dart +++ b/lib/src/support/disposable.dart @@ -39,7 +39,11 @@ mixin _Disposer { logger.finer('[$objectId] running ${_disposeFuncs.length} dispose funcs...'); // call dispose funcs in reverse order for (final disposeFunc in _disposeFuncs.reversed) { - await disposeFunc(); + try { + await disposeFunc(); + } catch (e, stack) { + logger.warning('[$objectId] error during dispose: $e', e, stack); + } } _disposeFuncs.clear(); logger.finer('[$objectId] dispose complete.'); diff --git a/test/support/disposable_test.dart b/test/support/disposable_test.dart new file mode 100644 index 00000000..306cfe05 --- /dev/null +++ b/test/support/disposable_test.dart @@ -0,0 +1,45 @@ +import 'package:flutter_test/flutter_test.dart'; + +import 'package:livekit_client/src/support/disposable.dart'; + +class TestDisposable extends Disposable { + @override + Future dispose() async { + return await super.dispose(); + } +} + +void main() { + group('Disposable', () { + test('should execute all dispose functions even if one fails', () async { + final disposable = TestDisposable(); + bool func1Called = false; + bool func2Called = false; + bool func3Called = false; + + // Dispose functions are called in reverse order of addition + + // Added 1st -> Called 3rd + disposable.onDispose(() async { + func1Called = true; + }); + + // Added 2nd -> Called 2nd + disposable.onDispose(() async { + func2Called = true; + throw Exception('fail'); + }); + + // Added 3rd -> Called 1st + disposable.onDispose(() async { + func3Called = true; + }); + + await disposable.dispose(); + + expect(func3Called, isTrue, reason: 'Last added (func3) should be called first'); + expect(func2Called, isTrue, reason: 'Middle added (func2) should be called'); + expect(func1Called, isTrue, reason: 'First added (func1) should be called even if func2 failed'); + }); + }); +}