Funneling Objects through void*

Maurizio Cimadamore maurizio.cimadamore at oracle.com
Thu Jun 15 23:08:07 UTC 2023


Hi,
I wanted to reply on this point, since this is also an important one.

On 15/06/2023 20:30, Johannes Kuhn wrote:
> WRT Exceptions. I did notice that throwing an exception in an upcall 
> crashes the JVM. Not so great. Means I need to do some extra work 
> catching them, checking for pending exceptions (there should be none, 
> but you never know) and return the appropriate "abort enumerating" 
> constant.
>
> Not sure if this could be improved by specifying an "exception return 
> value" when creating the upcall and then rethrow the exception when 
> the underlying downcall returns. 

So, what should happen when an upcall fails with exceptional state? This 
is a question we asked ourselves many (many) times. There are many 
options, but we seem to always keep coming back to the same answer. It 
might be worth, nevertheless, expand a bit on what the rationale behind 
the current decision is.

The reason we terminate the JVM when there's an unhandled exception in 
an upcall is that if we didn't, we would have to pop all the native 
frames until we reached some Java frame, and then rethrow the exception 
there. While this gives a relatively intuitive behavior (the exception 
thrown in the upcall can be caught by the code that initiated the 
downcall to which the upcall was passed), there is a biggie issue: what 
happens to the native code that was popped? We were, after all, in the 
middle of executing code inside some function. Perhaps the function 
needed, after the upcall, to reset some state for the next iteration. 
Sadly, by simply stepping over the native code, we now have left the 
native library in an inconsistent state. If you catch the exception and 
try to call the same function again, there's no way to predict what's 
going to happen.

The C language (with the notable exception of Windows [1]) doesn't have 
a concept of stack unwinding (unlike C++). That said, stack unwinding is 
extremely platform/compiler-dependent. In some platfroms (notably, 
Windows) there is a clearly supported API to do it, in some others less 
so (Linux does have libunwind [2]). But even if we did unwinding 
correctly, while that might save C++ code that got caught in the middle 
of an exception, that would still do nothing for C code.

Overall, there's no silver bullet here. To recap:

* Admit that we don't know how to proceed, and terminate the JVM (what 
we do today);
* Pop all the native frames, leaving the native library in some 
undefined state;
* Attempt unwinding (assuming we can in all platforms), rescuing some 
C++ cases, but still leaving C code in undefined state.

What we do today is blunt, but honest. Other options involving 
popping/unwinding are possible, but the reality is, the underlying 
native library has potential to misbehave from the point at which the 
exception occurred.

In some APIs (e.g. libclang cursor visitors [3] work like that), an 
upcall can return a value which says "please visit no more". If this 
idiom was frequent enough, perhaps we could provide an upcall Linker 
option which defines a "fallback" return value, to be used in case of 
exceptions. For primitive returns that's a possibility, but if the 
return value is a pointer or a struct things are more difficult: the 
fallback segment could no longer be alive by the time it is accessd from 
the native code surrounding the upcall (which would lead, again, to a 
crash).

Overall, whenever we discussed this, the general feeling was always that 
there's no "right" or "wrong" answer: handling exceptions thrown from 
upcalls is a matter of "choosing the right policy" for the particular 
kind of upcall one needs to define. Sometimes it might be convenient to 
"return 0", sometimes to just crash, other times to pop all native 
frames, etc.

Cheers
Maurizio

[1] - 
https://learn.microsoft.com/en-us/cpp/cpp/structured-exception-handling-c-cpp?view=msvc-170
[2] - https://github.com/libunwind/libunwind
[3] - 
https://clang.llvm.org/doxygen/group__CINDEX__CURSOR__TRAVERSAL.html#ga99a9058656e696b622fbefaf5207d715


More information about the panama-dev mailing list