New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
8271348: Add stronger sanity check of thread state when polling for safepoint/handshakes #4936
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -705,45 +705,34 @@ void SafepointSynchronize::block(JavaThread *thread) { | ||
} | ||
|
||
JavaThreadState state = thread->thread_state(); | ||
assert(SafepointSynchronize::is_a_block_safe_state(state), "Illegal threadstate encountered: %d", state); | ||
thread->frame_anchor()->make_walkable(thread); | ||
|
||
uint64_t safepoint_id = SafepointSynchronize::safepoint_counter(); | ||
// Check that we have a valid thread_state at this point | ||
switch(state) { | ||
case _thread_in_vm_trans: | ||
case _thread_in_Java: // From compiled code | ||
case _thread_in_native_trans: | ||
case _thread_blocked_trans: | ||
case _thread_new_trans: | ||
|
||
// We have no idea where the VMThread is, it might even be at next safepoint. | ||
// So we can miss this poll, but stop at next. | ||
|
||
// Load dependent store, it must not pass loading of safepoint_id. | ||
thread->safepoint_state()->set_safepoint_id(safepoint_id); // Release store | ||
// We have no idea where the VMThread is, it might even be at next safepoint. | ||
// So we can miss this poll, but stop at next. | ||
|
||
// This part we can skip if we notice we miss or are in a future safepoint. | ||
OrderAccess::storestore(); | ||
// Load in wait barrier should not float up | ||
thread->set_thread_state_fence(_thread_blocked); | ||
// Load dependent store, it must not pass loading of safepoint_id. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Existing: I struggle to understand what this comment means - we are storing the value of safepoint_id so I don't see how the loading of safepoint_id can be reordered. ??? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll have to check to see who added that comment. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It was added here:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here's the code block:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So I think @robehn was trying to separate these two lines of code:
and
I think the same situation applies in the updated code, but it is harder There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks and apologies for the digression. I'm assuming the intended required order is:
But the store is a release_store, so it is effectively preceded by a LoadStore|SoreStore barrier. So both loads must come before the store. The loads themselves could be reordered AFAICS but with no affect on correctness. So I remain unclear about the "load dependent store" comment actually relates to. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Okay so we're done with this one for this PR. |
||
thread->safepoint_state()->set_safepoint_id(safepoint_id); // Release store | ||
|
||
_wait_barrier->wait(static_cast<int>(safepoint_id)); | ||
assert(_state != _synchronized, "Can't be"); | ||
// This part we can skip if we notice we miss or are in a future safepoint. | ||
OrderAccess::storestore(); | ||
// Load in wait barrier should not float up | ||
thread->set_thread_state_fence(_thread_blocked); | ||
|
||
// If barrier is disarmed stop store from floating above loads in barrier. | ||
OrderAccess::loadstore(); | ||
thread->set_thread_state(state); | ||
_wait_barrier->wait(static_cast<int>(safepoint_id)); | ||
assert(_state != _synchronized, "Can't be"); | ||
|
||
// Then we reset the safepoint id to inactive. | ||
thread->safepoint_state()->reset_safepoint_id(); // Release store | ||
// If barrier is disarmed stop store from floating above loads in barrier. | ||
OrderAccess::loadstore(); | ||
thread->set_thread_state(state); | ||
|
||
OrderAccess::fence(); | ||
// Then we reset the safepoint id to inactive. | ||
thread->safepoint_state()->reset_safepoint_id(); // Release store | ||
|
||
break; | ||
OrderAccess::fence(); | ||
|
||
default: | ||
fatal("Illegal threadstate encountered: %d", state); | ||
} | ||
guarantee(thread->safepoint_state()->get_safepoint_id() == InactiveSafepointCounter, | ||
"The safepoint id should be set only in block path"); | ||
|
||
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -118,6 +118,8 @@ void SafepointMechanism::process(JavaThread *thread, bool allow_suspend) { | ||
// local poll already checked, if used. | ||
bool need_rechecking; | ||
do { | ||
JavaThreadState state = thread->thread_state(); | ||
guarantee(SafepointSynchronize::is_a_block_safe_state(state), "Illegal threadstate encountered: %d", state); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't this be outside the loop? Though we are doubling up given the assert in block(). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the thread's state can change during this loop so having the check in the The assert() in Yes, I specifically wanted a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would expect any state change to restore the original state before we get back into this loop. So this seems a little paranoid, but okay I guess. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No on further thought I'm not sure about this. If we take the path to SS::block() then this guarantee must hold. But what if we don't take that path? What if this is called due to a local poll and the thread is executing code that precludes the possibility of a global poll (e.g. holds Threads_lock) - what are the potential valid states in that case? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Your last comment is exactly why I want this While I've done a lot of Mach5 test runs for this change (plus the fix for JDK-8271251), |
||
if (global_poll()) { | ||
// Any load in ::block() must not pass the global poll load. | ||
// Otherwise we might load an old safepoint counter (for example). | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: you shouldn't need to say SafepointSynchronize:: here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed. Will fix it.