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
