-
Notifications
You must be signed in to change notification settings - Fork 762
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
Fix ToObservable operator can throw unhandled exception #2172
base: main
Are you sure you want to change the base?
Conversation
@dotnet-policy-service agree |
…nobservedTaskException
Hi @idg10, Apologies for the ping! Could you take a look at the fix when you have a chance? This bug is quite severe as it's causing the process to completely shut down. We’ve implemented internally a custom alternative as a .ToObservableSafe(), but it’s challenging to enforce this version consistently due to the presence of .ToObservable within System.Linq that seems really authoritative. |
Sorry for the delay - many of us at endjin have been unwell, and since nobody pays us to maintain Rx (and we only maintain Looking at this, I think there are 4 obvious options when
Although 2 is arguably logically like the right thing to do (because this is a case of failing even to get started) in practice this would involve sync-over-async, so we can't do that. For AsyncRx, So it does seem like 3 is the only reasonable option left if we want to report the exception. I see you've also implemented 4, allowing people to say they'd like exceptions to be swallowed. That's the one part of this I'm not sure about. I'm glad you've not made it the default, but I'm not sure whether we really want this at all. It seems to enable a bad practice. What was your rationale for allowing |
Thank you for your answer. Maybe we could just handle DisposeAsync exceptions like other exceptions? So invoking observer.OnError if the cancellation token is not canceled yet? |
Ah yes, I remember that So firstly, I think that in the case where a subscriber is still attached we definitely want to report it to I'm now looking at this code and I'm a bit confused: try
{
e = _source.GetAsyncEnumerator(ctd.Token);
}
catch (Exception ex)
{
if (!ctd.Token.IsCancellationRequested)
{
observer.OnError(ex);
}
return;
} If So I think we'd want to make the call to But if I'm wrong about that, that implies that this is a second "after unsubscription" case, in which case we'd need to make this also pay attention to that 'ignore' flag. |
I agree that the check for unsubscription is useless if the exception is thrown synchronously. Thank you! About that
If we choose the latter, in the case the subscription is already disposed:
Consider also that following that approach, if Also note with the current implementation, the process crashes also if any exception is thrown by DisposeAsync() |
Fixes #1677
The issue was that exceptions thrown by GetAsyncEnumerator() were not being propagated to observer.OnError(), resulting in unhandled exceptions. This has been addressed by wrapping the GetAsyncEnumerator() call in a try-catch block
The same issue appears also on DisposeAsync(). This is more complex, because we cannot rely on the observer anymore, because it is already completed at that point.
Currently the exceptioncauses the process to crash directly. In my opinion this is bad since there is nothing that warns the caller about that, and also it does not offer any solution.
Observable.FromAsync
already "solves" this problem, so I used the same approach here, creating an overload where is possible to specify if suppress the exception or not. If not, the exception would be routed to TaskScheduler.UnobservedTaskException, rather that be rethrown on the ThreadPool / Synchronization Context directly (causing with 99% probability the process to crash)