RFR: 8255384: Remove special_runtime_exit_condition() check from SS::block()
Patricio Chilano Mateo
pchilanomate at openjdk.java.net
Thu Oct 29 18:40:47 UTC 2020
On Thu, 29 Oct 2020 05:40:49 GMT, David Holmes <dholmes at openjdk.org> wrote:
>> Hi all,
>>
>> Please review the following patch that removes the call to handle_special_runtime_exit_condition() from SS::block(). This avoids recursive calls when transitioning and stopping for safepoints and also makes the code simpler to read since it is not trivial to deduce why we need to execute the check for certain states but not others, i.e. what exact scenarios we are trying to guard against.
>>
>> Method handle_special_runtime_exit_condition() checks for external suspends, object deoptimization, async exceptions and JFR suspends. All these need to be checked when transitioning to java and when transitioning out of native (except for async exceptions when transitioning to thread_in_vm). In SS::block() this check is executed for the _thread_new_trans, _thread_in_native_trans and thread_in_Java cases. For _thread_new_trans, we know the JT will have to go through JavaCallWrapper() the first time it transitions to Java and that already has a check for handle_special_runtime_exit_condition(). For _thread_in_native_trans, transitioning out of native already has checks for external suspends, object deoptimization and JFR suspends in check_safepoint_and_suspend_for_native_trans() which is called from ThreadStateTransition::transition_from_native()(called either directly or through the ThreadStateTransition wrappers) and check_special_condition_for_native_trans (for native wrappers c
ase). So that leaves the thread_in_Java case.
>> Careful analysis shows the handle_special_runtime_exit_condition() call in SS::block() prevents JTs transitioning back to Java from escaping after being externally suspended. This can happen when calling SafepointMechanism::process_if_requested() while transitiong back to java without a later check for external suspend. Looking at the callers of SafepointMechanism::process_if_requested() we see that this can happen from handle_polling_page_exception(), java_suspend_self_with_safepoint_check() and check_safepoint_and_suspend_for_native_trans(). An example of this can be shown for the handle_polling_page_exception() case:
>> - JT hits a poll exception while executing nmethod.
>> - JT calls handle_polling_page_exception() ( which doesn't use ThreadStateTransition wrappers) and calls SafepointMechanism::process_if_requested()
>> - Stops for a safepoint due to a VM_ThreadSuspend request
>> - Upon unblocking from the safepoint, unless we have the check in SS::block() the JT will transition back to java without actually suspending
>>
>> The "escape from suspend" scenarios for the other callers of SafepointMechanism::process_if_requested() are described in the comments of the bug as well as the proper fixes.
>>
>> I have tested the patch several times in mach5 tiers1-7 and saw no issues. Let me know if you think I should run any other special tests.
>>
>> Thanks,
>> Patricio
>
> src/hotspot/share/runtime/safepoint.cpp line 935:
>
>> 933: // Process pending operation
>> 934: {
>> 935: ThreadInVMfromJava tivm(self);
>
> This doesn't look good to me. We are in low-level safepoint/handshake related code, but we call a higher-level abstraction for thread-state transition management, just to get the side-effect of processing the safepoint/handshake correctly. To me this suggests we're missing an appropriate API entry point to do what needs to be done. I would rather see something like:
> SafepointMechanism::process_if_requested(self);
> self->handle_special_runtime_exit_condition(true /* check asyncs */);
> (assuming that is the correct action of course).
Ok, initially I thought of doing that but then decided to use transition wrappers. I'll change it.
I think ideally we would like to use a transition wrapper in SafepointSynchronize::handle_polling_page_exception() but given the complexity of ThreadSafepointState::handle_polling_page_exception() we have to do things more manual unfortunately.
> src/hotspot/share/runtime/safepoint.cpp line 958:
>
>> 956: // be delivered. (Polling at a return point is ok though). Sure is
>> 957: // a lot of bother for a deprecated feature...
>> 958: ThreadInVMfromJavaNoAsyncException tivm(self);
>
> Same comment as above.
Ok, I'll change it.
> src/hotspot/share/interpreter/zero/bytecodeInterpreter.cpp line 110:
>
>> 108: HandleMarkCleaner __hmc(THREAD); \
>> 109: if (SafepointMechanism::should_process(THREAD)) { \
>> 110: CALL_VM({ThreadInVMfromJava tiv(THREAD);}, handle_exception); \
>
> I initially thought this was an okay convenience technique to get the desired effects, but seeing it used elsewhere I no longer think that - see comments "above".
In this case I thought using TIVFJ seemed better because when reading CALL_VM() one would expect a transition from _thread_in_Java to _thread_in_vm and then a call to the appropiate method (like with JRT_ENTRY). It's just that in this case the call that we want to make (SafepointMechanism::process_if_requested()) is already included in the TIVFJ. Anyways, doing it the other way is okay too.
-------------
PR: https://git.openjdk.java.net/jdk/pull/913
More information about the hotspot-runtime-dev
mailing list