Details
-
Enhancement
-
Resolution: Unresolved
-
P4
-
None
-
repo-panama
Description
During a review of the Panama VM changes, a discussion sprung up around exception handling here: https://github.com/openjdk/jdk/pull/7959#discussion_r869676790
In short: it is possible for exceptions to occur outside of user code during an upcall (for instance in the method handle internal frames). This includes async exceptions, as well as stack overflow errors (and perhaps others). Such exceptions would end up in one of the fallback exception handlers which end up terminating the VM.
At this time it is not clear if it is worth it to address this corner case. But some ideas are as follows:
The stack more or less looks like this during an upcall:
| ---
| <younger frames> |
| ---
| <1: user define try block with exception handler (maybe)> |
| ---
| <2: user code start> |
| ---
| <3: method handle impl frames 1> |
| ---
| <4: upcall wrapper class with fallback handler 1> |
| ---
| <5: method handle impl frames 2> |
| ---
| <6: upcallk stub with fallback handler 2> |
| <7: unknown native code>
| ---
| <older frames> |
- Do nothing special for async exceptions. i.e. if they happen anywhere between 1. and 6. they will end up in one of the fallback handlers and the VM will be terminated.
- Block async exceptions in all code up from 6.
- Somehow only block async exceptions only between 6. and 1.
I think that is possible by changing the API so that the user passes us a method handle to their fallback exception handler, and we invoke it in our code in 4. We would need 2 methods for blocking and unblocking async exceptions from Java. Then we could disable async exceptions at the start of 6. enabled them at the start of the try block in 4. (around the call to user code), and disable them at the end of this try block. Then finally re-enable them at the end of 6. If an exception occurs in the try block in 4., delegate to the user-defined exception handler (but use the VM terminate strategy as a fallback for when another exception occurs). The other problem I see with that is that to make that fast enough (i.e. not incur a ~1.5-2x cost on call overhead), we would need compiler intrinsics for the blocking/unblocking, and in the past we've been unable to define 'critical' sections of code like that in C2 (it's an unsolved problem at this point).
Another idea was also suggested by Vladimir Ivanov:
As an alternative solution, exception handling for upcalls can be handled by another upcall (into catch handler when pending exception is encountered). As a bonus, it allows to handle repeated exceptions. In the worst case, it would manifest as a hang (when async exceptions are continuously delivered).
In short: it is possible for exceptions to occur outside of user code during an upcall (for instance in the method handle internal frames). This includes async exceptions, as well as stack overflow errors (and perhaps others). Such exceptions would end up in one of the fallback exception handlers which end up terminating the VM.
At this time it is not clear if it is worth it to address this corner case. But some ideas are as follows:
The stack more or less looks like this during an upcall:
| ---
| <younger frames> |
| ---
| <1: user define try block with exception handler (maybe)> |
| ---
| <2: user code start> |
| ---
| <3: method handle impl frames 1> |
| ---
| <4: upcall wrapper class with fallback handler 1> |
| ---
| <5: method handle impl frames 2> |
| ---
| <6: upcallk stub with fallback handler 2> |
| <7: unknown native code>
| ---
| <older frames> |
- Do nothing special for async exceptions. i.e. if they happen anywhere between 1. and 6. they will end up in one of the fallback handlers and the VM will be terminated.
- Block async exceptions in all code up from 6.
- Somehow only block async exceptions only between 6. and 1.
I think that is possible by changing the API so that the user passes us a method handle to their fallback exception handler, and we invoke it in our code in 4. We would need 2 methods for blocking and unblocking async exceptions from Java. Then we could disable async exceptions at the start of 6. enabled them at the start of the try block in 4. (around the call to user code), and disable them at the end of this try block. Then finally re-enable them at the end of 6. If an exception occurs in the try block in 4., delegate to the user-defined exception handler (but use the VM terminate strategy as a fallback for when another exception occurs). The other problem I see with that is that to make that fast enough (i.e. not incur a ~1.5-2x cost on call overhead), we would need compiler intrinsics for the blocking/unblocking, and in the past we've been unable to define 'critical' sections of code like that in C2 (it's an unsolved problem at this point).
Another idea was also suggested by Vladimir Ivanov:
As an alternative solution, exception handling for upcalls can be handled by another upcall (into catch handler when pending exception is encountered). As a bonus, it allows to handle repeated exceptions. In the worst case, it would manifest as a hang (when async exceptions are continuously delivered).