RFR: 8286830: ~HandshakeState should not touch oops

Daniel D.Daugherty dcubed at openjdk.java.net
Fri Jun 3 20:56:37 UTC 2022


On Thu, 19 May 2022 19:17:09 GMT, Patricio Chilano Mateo <pchilanomate 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

Sorry for the late review. I only found minor things. Please don't
do anything with my review yet. I still need to add my tracker
code and run this fix thru my stress testing.

Very nice job resolving this issue.

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.

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?

src/hotspot/share/runtime/thread.cpp line 1539:

> 1537:   // Remove from list of active threads list, and notify VM thread if we are the last non-daemon thread.
> 1538:   // We call BarrierSet::barrier_set()->on_thread_detach() here so no touching of oops after this point.
> 1539:   Threads::remove(this, daemon);

Thanks for adding the new comment line!

test/hotspot/jtreg/runtime/Thread/StopAtExit.java line 95:

> 93:         threadCreator.setDaemon(true);
> 94:         threadCreator.start();
> 95:         test(timeMax);

Okay, but now the test is going to execute for twice `timeMax` which might be
a bit of a surprise... In particular the usage() mesg is now wrong.

test/hotspot/jtreg/runtime/Thread/SuspendAtExit.java line 101:

> 99:         threadCreator.setDaemon(true);
> 100:         threadCreator.start();
> 101:         test(timeMax);

Okay, but now the test is going to execute for twice `timeMax` which might be
a bit of a surprise... In particular the usage() mesg is now wrong.

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

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


More information about the hotspot-runtime-dev mailing list