On Tue, Nov 12, 2013 at 9:42 PM, Kevin Ballard <[email protected]> wrote:

> I guess I was being too vague when I said “C exceptions”, because you’re
> right, that’s not actually a specific thing. More concretely, I was
> thinking of either C++ exceptions, or Obj-C exceptions.
>
> One situation I was thinking of would be a rust library that exposes
> `extern “C”` functions. Let’s say one of these functions, frob(), takes an
> `extern “C”` callback and calls it. Now let’s say I’m writing an Obj-C app,
> and I do something like the following:
>
> static void callback() {
>     [NSException raise:@“Foo Exception” format:@“I just felt like
> unwinding”];
> }
>
> @try {
>     frob(callback)
> }
> @catch (NSException *e) {
>     NSLog(@“I caught my exception!”);
> }
>
> This will unwind through the Rust code, before being caught again. It’s
> hard to come up with a scenario where this actually results in unsafe state
> within the rust library, but let’s say `frob()` uses some sort of shared
> state (e.g. a RWArc<>), and is in the middle of modifying the shared state
> in a way that causes invariants to be broken temporarily. If unwinding
> happens when the invariants are in the broken state, then they’ll be left
> in the broken state and the next bit of Rust code that tries to access this
> shared state will likely blow up.
>
> This is obviously pretty contrived, but the question is, is this legal to
> do, and if so, do we need to do anything?
>
> Daniel’s response says that it’s undefined for a C++ function to throw an
> exception past an `extern “C”` boundary. This is something I did not know.
> But what about Obj-C exceptions? I haven’t heard about any undefined
> behavior regarding Obj-C exceptions. It is generally recognized that
> throwing an Obj-C exception past a framework boundary is unsafe (i.e.
> throwing an exception in user code that unwinds through Foundation code),
> but this is because Obj-C code rarely bothers with @try/@finally and
> doesn’t have stack objects, so unwinding through code that doesn’t expect
> it will often leave data structures in invalid states.
>
> If all forms of unwinding are considered to be undefined when passing
> through an `extern “C”` boundary, then I guess we can consider this issue
> to be covered by undefined behavior. Although this doesn’t make me
> particularly happy, because it means that it may be impossible to truly
> contain the unsafety in FFI functions. One possible way to mitigate this
> would be to provide a way to wrap an FFI function with a stub that catches
> C++/Obj-C exceptions (I *think* Obj-C exceptions are unified with the C++
> exception machinery on all modern platforms (i.e. not 32-bit PPC, and I’m
> not sure about 32-bit x86)) and triggers task failure. This would mean any
> attempt to unwind through a Rust function (using C++ or Obj-C exceptions)
> would be contained, after a fashion.
>
> Though this does raise another question. What if a C++ function calls a
> Rust function, and the Rust function triggers task failure? Is it possible
> for the C++ function to catch the task failure using a catch(…) block?
> Furthermore, since Daniel said it’s undefined for a C++ exception to be
> thrown past an `extern “C”` boundary, does this mean that it’s technically
> undefined for Rust to trigger task failure in any function that’s rooted in
> an `extern “C”` function?
>
> -Kevin
>

It's completely possible to write safe bindings to a C++ library. The
process involves wrapping the whole thing with `extern "C"` functions using
catch blocks for any function possibly throwing an exception. Keep in mind
that libraries already have to do this to support usage from most other
languages, so it's a sunken cost.

If a library takes a callback, writing safe Rust bindings isn't going to
turn out well. Rust functions can fail, so the Rust API can't pass an
arbitrary function to the callback.
_______________________________________________
Rust-dev mailing list
[email protected]
https://mail.mozilla.org/listinfo/rust-dev

Reply via email to