-
Notifications
You must be signed in to change notification settings - Fork 240
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
Ensure panic safety for all FFI methods #221
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like it a lot!!
A couple of things we can address here, or get this merged and address separately:
- We could add tests for panics
- We'll need to also create the mapping for panic codes to explicit errors (for all the errors defined with the
[Error]
attribute). Right now they map only to codes1..numOfVariants
Also with this merged, we would have completely lost support for Python in all our examples, which might make testing for #215 and #214 a little annoying, but this is a higher priority 😄
I think Python is broken now because methods without Edit: Hahaha, I wrote that literally at the same time as your review! 😅 I'll go ahead and add a test for panics now. For mapping these built-in codes to explicit errors...we could either have |
Will there be any conflicts or problems if #215 lands ahead of this PR, or should we hold on of that one until this one is landed? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We'll need to also create the mapping for panic codes to explicit errors (for all the errors
defined with the [Error] attribute). Right now they map only to codes 1..numOfVariants
Naively, as a consumer I think I would expect a panicking [Throws(E)]
function to throw an InternalError.Panic
, rather than throwing e.g. some sort of E.InternalError.Panic
variant. Does that mesh with what's being proposed here?
case emptyResult | ||
case panic(message: String) | ||
case invalidHandle(message: String) | ||
case unknownError(code: Int32) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Possibly contrary opinion: I'm not convinced it's valuable to define an elaborate series of cases for each individual type of internal error that might be thrown. If anyone ever finds themselves matching on these different cases and doing anything other than freaking out and filing a bug against uniffi, something has gone terribly wrong 😅.
In that spirit, the most important thing is that these errors surface with a useful human-readable message that helps us find and fix the bug. If enumerating all the cases helps us achieve that, awesome. But if they're just adding layers of indirection to a fixed string value, then I worry they might set incorrect expectations about how these internal errors should be handled (which is: they shouldn't be handled).
I'm not going to r- for that though, just my 2c.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, thinking some more about this, I'm coming around to what you and Tarik suggested—assert for any kind of internal error instead of bubbling it up as an exception. In real-world code, [Throws]
is for defining error cases that are meaningfully recoverable—a network error or duplicate experiment name, for example. A Rust-side panic might be, but...how do you fix that? If the app were fully in Rust, it would crash. The only reason we catch panics is to avoid stack unwinding into the foreign language, not as a graceful error recovery mechanism.
@rfk I think we can land #215. And after this merges, we can spin off another issue to tackle panics in Python based on what we come with here! Thoughts?
Hmm ya this makes more sense... Although I can't instantly think of a clean way to do this (since we pass the error type as a part of the This makes me wonder if we should just I defer to @linacambridge's judgement though 😅 |
Yep. I'll probably try to tackle that as part of my big make-python-work refactor over in #214 (and I'd hate to derail any critical-path nimbus stuff in the service of the python bindings). |
Yes, let's land #215 first, and then this. I'll go ahead and make the changes to fatally assert on an internal error, instead of throwing. |
2fd5b8c
to
f82e783
Compare
} | ||
} | ||
|
||
class InternalException(message: String) : Exception(message) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, I simplified the error hierarchy here so that we have a catch-all "internal error" instead of getting fancy with reporting panic and invalid handle codes. We might want to report these specially to Sentry, for example, but I totally agree consumers shouldn't be matching on them. WDYT?
I went ahead and did a cleanup pass and added a quick test—would you mind taking another look please, @tarikeshaq? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!
We can tackle standarizing this behavior with fallible functions in a follow-up!
This commit changes all FFI functions to take an `out_err` param, so that they're called using `call_with_result`. Any functions that don't have a `[Throws]` annotation in the IDL will throw a generic `InternalError` instead. Closes #32.
This commit changes all FFI functions to take an
out_err
param, sothat they're called using
call_with_result
. Any functions that don'thave a
[Throws]
annotation in the IDL will throw a genericInternalError
instead.Closes #32.