AvaloniaUI / Avalonia

Develop Desktop, Embedded, Mobile and WebAssembly apps with C# and XAML. The most popular .NET UI client technology
https://avaloniaui.net
MIT License
25.17k stars 2.18k forks source link

Application doesn't terminate when unhandled exception is thrown from ReactiveCommand handler #16017

Open yudindm opened 2 months ago

yudindm commented 2 months ago

Describe the bug

As I understand the Avalonia and Reactive UI documentation when unhandled exception is thrown from ReactiveCommand an application should terminate. But this does not happen.

To Reproduce

  1. Create application from Avalonia MVVM template.

  2. Add ReactiveCommand to MainWindowViewModel which just throws exception.

    // in MainWindowViewModel class
        public ReactiveCommand<Unit, Unit> ThrowExceptionCommand { get; }
    
    // in MainWindowViewModel constructor
        ThrowExceptionCommand = ReactiveCommand.Create(() =>
        {
            throw new Exception("Exception should terminate app.");
        });
  3. Add Button to invoke ReactiveCommand added in step 2 in MainWindow.axaml.

    // in MainWindow.axaml
    <StackPanel>
        <TextBlock Text="{Binding Greeting}"/>
        <Button Content="Throw Exception Command" Command="{Binding ThrowExceptionCommand}"/>
    </StackPanel>
  4. Start application and press the Throw Exception Command Button

  5. See that nothing happens. Application stays open.

Expected behavior

Applcation should terminate.

Avalonia version

11.0.6

OS

No response

Additional context

No response

maxkatz6 commented 2 months ago

Override RxApp.DefaultExceptionHandler if you want a proper control over unhandled exceptions in Rx pipeline.

maxkatz6 commented 2 months ago

About this issue, it seems Reactive doesn't behave quite right, when scheduler has an optimization for the current thread. See https://github.com/reactiveui/ReactiveUI/issues/1859 and https://github.com/reactiveui/ReactiveUI/pull/2043.

cc @worldbeater @kekekeks

yudindm commented 2 months ago

Override RxApp.DefaultExceptionHandler if you want a proper control over unhandled exceptions in Rx pipeline.

From my point of view the proper handling of an unhandled exception is to terminate an application. As a workaround I added RxApp.DefaultExceptionHandler to bypass AvaloniaScheduler current thread optimization like that:

RxApp.DefaultExceptionHandler = Observer.Create<Exception>(exception =>
        {
            Dispatcher.UIThread.Post(() => throw new UnhandledErrorException("Unhandled exception was thrown.", exception),
                DispatcherPriority.Send);
        });