Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Async Methods swallow exceptions #961

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

vb2ae
Copy link
Member

@vb2ae vb2ae commented Jan 1, 2025

This pull request introduces several changes to improve exception handling across various files in the Caliburn.Micro framework. The most significant changes include adding exception handling mechanisms in asynchronous methods and updating the ResultCompletionEventArgs class to include an exception property.

Exception Handling Enhancements:

Coroutine Exception Handling:

Miscellaneous Changes:

These changes enhance the robustness of the framework by ensuring that exceptions are properly logged and handled, preventing potential crashes and improving debugging capabilities.

Closing #863, #745

Added a private field `_exception` and a constructor to `ResultCompletionEventArgs` to handle exceptions. Wrapped initialization and activation logic in `Screen.cs` with try-catch blocks to log errors and handle exceptions. Introduced new virtual methods `OnActivatedAsyncException`, `OnDeactivateAsyncException`, and `OnInitializedAsyncException` in `Screen.cs` to handle exceptions during asynchronous operations. Added a new method `CoroutineException` in `CaliburnApplication.cs`, `FormsApplication.cs`, `CaliburnApplicationDelegate.cs`, and `Bootstrapper.cs` to handle exceptions during coroutine execution. Added event handlers for `Coroutine.Completed` in these files to invoke `CoroutineException` when an error occurs. Made minor formatting changes and added missing namespaces in various files to ensure consistency and proper functionality. Removed unused `using` directives and added necessary ones in `BootstrapperBase.cs` and other files. Added logging for exceptions in `BootstrapperBase.cs` and ensured that coroutine exceptions are logged.
@vb2ae vb2ae linked an issue Jan 1, 2025 that may be closed by this pull request
@vb2ae vb2ae requested a review from Copilot January 1, 2025 17:13

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot reviewed 5 out of 12 changed files in this pull request and generated no comments.

Files not reviewed (7)
  • src/Caliburn.Micro.Core/ResultCompletionEventArgs.cs: Evaluated as low risk
  • src/Caliburn.Micro.Core/Screen.cs: Evaluated as low risk
  • src/Caliburn.Micro.Platform/Platforms/Android/CaliburnApplication.cs: Evaluated as low risk
  • src/Caliburn.Micro.Platform/Platforms/Maui/Android/CaliburnApplication.cs: Evaluated as low risk
  • src/Caliburn.Micro.Platform/Platforms/Maui/FormsApplication.cs: Evaluated as low risk
  • src/Caliburn.Micro.Platform/Platforms/Maui/Windows/CaliburnApplication.cs: Evaluated as low risk
  • src/Caliburn.Micro.Platform/Platforms/Maui/iOS/CaliburnApplicationDelegate.cs: Evaluated as low risk
src/Caliburn.Micro.Core/Screen.cs Dismissed Show dismissed Hide dismissed
src/Caliburn.Micro.Core/Screen.cs Fixed Show fixed Hide fixed
src/Caliburn.Micro.Core/Screen.cs Dismissed Show dismissed Hide dismissed
src/Caliburn.Micro.Core/Screen.cs Fixed Show fixed Hide fixed
src/Caliburn.Micro.Core/Screen.cs Fixed Show fixed Hide fixed
@vb2ae
Copy link
Member Author

vb2ae commented Jan 1, 2025

@darxis Hope you had a Happy New Year. Could you look at this when you get a chance?

@vb2ae vb2ae requested a review from Copilot January 1, 2025 19:00

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot reviewed 5 out of 12 changed files in this pull request and generated no comments.

Files not reviewed (7)
  • src/Caliburn.Micro.Core/ResultCompletionEventArgs.cs: Evaluated as low risk
  • src/Caliburn.Micro.Platform/Platforms/net46/Bootstrapper.cs: Evaluated as low risk
  • src/Caliburn.Micro.Platform/Platforms/netcore-avalonia/BootstrapperBase.cs: Evaluated as low risk
  • src/Caliburn.Micro.Platform/Platforms/uap/CaliburnApplication.cs: Evaluated as low risk
  • src/Caliburn.Micro.Platform/Platforms/Android/CaliburnApplication.cs: Evaluated as low risk
  • src/Caliburn.Micro.Platform/Platforms/iOS/CaliburnApplicationDelegate.cs: Evaluated as low risk
  • src/Caliburn.Micro.Core/Screen.cs: Evaluated as low risk
@vb2ae vb2ae changed the title Enhance exception handling and logging across the app Async Methods swallow exceptions Jan 1, 2025
@vb2ae vb2ae requested a review from KasperSK January 1, 2025 21:28
@vb2ae vb2ae linked an issue Jan 1, 2025 that may be closed by this pull request
Copy link
Contributor

@darxis darxis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to have some tests that check OnActivatedAsyncException etc. methods are properly called.

/// </summary>
/// <param name="sender">The sender of the event.</param>
/// <param name="e">The event arguments containing exception details.</param>
protected virtual void CoroutineException(object sender, ResultCompletionEventArgs e)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will this ever be called? Isn't it missing the following in Initialize method?

            Coroutine.Completed += (s, e) =>
            {
                if (e.Error != null)
                {
                    CoroutineException(s, e);
                }
            };

If yes then please also check:

/// <summary>
/// Called when exception called in OnActivatedAsync.
/// </summary>
protected virtual void OnActivatedAsyncException(Exception thrownException)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This gets called when OnActivateAsync or OnActivatedAsync throws an exception but the name OnActivatedAsyncException suggests it is related to the OnActivatedAsync method. Maybe we should split this into two methods? Like OnActivateAsyncException and OnActivatedAsyncException? Any other ideas?

PS: Same for OnInitializedAsyncException

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the rename from OnActivateAsync to OnActivatedAsync should be reverted, since that function is called when the framework wants to activate the screen and not when it is already activated.

}
catch (Exception ex)
{
Log.Error(ex);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing call to OnInitializeAsyncException/OnInitializedAsyncException

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, either all the methods should log the exception or there should be a OnException method that the user can hook into.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

@@ -174,5 +183,12 @@ protected virtual IEnumerable<object> GetAllInstances(Type service)
protected virtual void BuildUp(object instance)
{
}

/// <summary>
/// Called by the bootstrapper's constructor at design time to start the framework.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The event args.</param>
protected virtual void OnUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will it ever be called? I can't see a call to this within this PR. Also why is this only added for the netcore-avalonia platform?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In WPF, this method subscribes to the the Application class DispatcherUnhandledException, is there something similar in Avalonia?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removing avalonia ui does not have the DisplatcherUnhandledException

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In your comment you said that there was a private field added to this class but I only see the added constructor?

IsInitialized = initialized = true;
await OnInitializedAsync(cancellationToken);
IsInitialized = initialized = true;
await OnInitializedAsync(cancellationToken);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not something that was done here in this PR, but why was the name changed from OnInitializeAsync to OnInitializedAsync? This method is called when initializing so the initializing happens in the OnInitializeAsync thus the name OnInitializeAsync makes more sense then OnInitializedAsync since OnInitializedAsync implies that the initialization is already completed.

}
catch (Exception ex)
{
Log.Error(ex);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, either all the methods should log the exception or there should be a OnException method that the user can hook into.

/// <summary>
/// Called when exception called in OnActivatedAsync.
/// </summary>
protected virtual void OnActivatedAsyncException(Exception thrownException)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the rename from OnActivateAsync to OnActivatedAsync should be reverted, since that function is called when the framework wants to activate the screen and not when it is already activated.

/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The event args.</param>
protected virtual void OnUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In WPF, this method subscribes to the the Application class DispatcherUnhandledException, is there something similar in Avalonia?

Comment on lines +115 to +119
catch (Exception ex)
{
Log.Error(ex);
OnInitializedAsyncException(ex);
}

Check notice

Code scanning / CodeQL

Generic catch clause Note

Generic catch clause.
Comment on lines +130 to +134
catch (Exception ex)
{
Log.Error(ex);
OnActivatedAsyncException(ex);
}

Check notice

Code scanning / CodeQL

Generic catch clause Note

Generic catch clause.
Comment on lines +156 to +160
catch (Exception ex)
{
Log.Error(ex);
OnDeactivateAsyncException(ex);
}

Check notice

Code scanning / CodeQL

Generic catch clause Note

Generic catch clause.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
3 participants