What happens when you throw an error from a `@cfunction`?

If I define a C-callable function pointer (via @cfunction) which can internally throw an error, and then perform a ccall which would then invoke the function and trigger the error, what actually happens? I assume it does some sort of longjmp back into the Julia runtime, but I don’t actually know for sure.

3 Likes

It is undefined. You destroy the state of the library you escaped from (via the throw), which may then do anything.

2 Likes

Okay, so we shouldn’t do that (and that should probably be documented), but what actually does happen?

throw and catch are currently implemented via setjmp/longjmp or similar (actually custom assembly on windows and sigsetjmp/siglongjmp elsewhere)

What I expect to happen is as you guessed — you’ll longjmp back into the Julia landing pad and resume execution from there. It might even work if the library you’re jumping out of doesn’t do any resource management, or alternatively it may also work if the library does do resource management in the same way that the Julia runtime does (ie, uses the Julia GC to allocate memory and manually roots resources as part of its operation).

However… I’m unsure whether the code emitted by ccall assumes that it won’t be jumped over. Looking briefly I can’t see any such assumptions in the code but they could be there.

4 Likes

Here’s what I did in GLPK to work-around this:

The cfunction has a try-catch to trap errors. In the catch, we gracefully exit from C tidying up after ourselves, and save the error for later
https://github.com/jump-dev/GLPK.jl/blob/5cee5af5cbd78ba074fa391f04f99ac8daafda92/src/MOI_wrapper/MOI_wrapper.jl#L209-L235
Then in the Julia function that calls everything, we run the ccall, and then re-throw the error if our cfunction trapped one:
https://github.com/jump-dev/GLPK.jl/blob/5cee5af5cbd78ba074fa391f04f99ac8daafda92/src/MOI_wrapper/MOI_wrapper.jl#L1382-L1405

1 Like