RFR: 8286830: ~HandshakeState should not touch oops

Patricio Chilano Mateo pchilanomate at openjdk.java.net
Fri Jun 3 22:37:42 UTC 2022


On Fri, 3 Jun 2022 20:34:36 GMT, Daniel D. Daugherty <dcubed at openjdk.org> wrote:

>> On 8284632 we added ~HandshakeState to cleanup any potential async operations left in the handshake queue. This could trigger the release of an oophandle from its associated oop-storage when trying to delete an AsyncExceptionHandshake object. Since the exiting JT already executed on_thread_detach() this is forbidden. 
>> To fix this I used the original approach that @dcubed-ojdk posted on the 8284632 fix, which is cleaning up those operations before on_thread_detach() is executed. To make sure that no other async exception operation is added to the queue after that I did two things:
>> - Added a check in install_async_exception() to avoid installing the operation if the target already moved to the _thread_exiting state.
>> - Move the setting of the _thread_exiting state to include the case where JavaThread::exit() is called with destroy_vm=true.
>> 
>> I also added another run to tests StopAtExit.java and SuspendAtExit.java with extra contention on the Threads_lock. That increases the odds of the target being handshake safe while being in the _thread_exiting state which allows testing for more pathological cases.
>> 
>> I was able to reproduce the issue by running StopAtExit.java with -XX:+UseShenandoahGC and verified that the test now passes. I also run mach5 tiers1-3. Will run the upper tiers also and will do an additional pass after that.
>> 
>> Thanks,
>> Patricio
>
> src/hotspot/share/runtime/thread.cpp line 1367:
> 
>> 1365: void JavaThread::exit(bool destroy_vm, ExitType exit_type) {
>> 1366:   assert(this == JavaThread::current(), "thread consistency check");
>> 1367:   assert(!is_exiting(), "should not be exiting or terminated already");
> 
> Hmmm... The old `destroy_vm == true` branch assert was:
> 
> `assert(!is_terminated() && !is_exiting(), "must not be exiting");`
> 
> and the new assert mesg has "or terminated", but the assert is only
> checking `!is_exiting()` and doesn't not include `!is_terminated()`.
> So the old assert checked both conditions and only mumbled about
> "exiting" and the new assert checks one condition and mumbles
> about both conditions.

is_exiting() also checks is_terminated() internally, so it returns true for the _thread_exiting, _thread_terminated and _vm_exited cases (false only for the _not_terminated case). Since the extra !is_terminated() check was redundant I removed it. Also since the assert will trigger for both the exiting and terminated cases I added both to the assert msg.

> src/hotspot/share/runtime/thread.cpp line 1444:
> 
>> 1442:   // Also, no more async exceptions will be added to the queue after this point.
>> 1443:   set_terminated(_thread_exiting);
>> 1444:   ThreadService::current_thread_exiting(this, is_daemon(threadObj()));
> 
> Previously, we didn't call `ThreadService::current_thread_exiting()` in the
> `destroy_vm == true` branch. Is that going to result in some change in
> behavior?

That call decrements a counter used for some stats and for determining the size of the thread array in ThreadListEnumerator. But I don't see any issues on doing that since this thread is actually exiting.

-------------

PR: https://git.openjdk.java.net/jdk/pull/8795


More information about the hotspot-runtime-dev mailing list