-
Notifications
You must be signed in to change notification settings - Fork 5.8k
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
8263107: PSPromotionManager::copy_and_push_safe_barrier needs acquire memory barrier #4371
Conversation
👋 Welcome back kbarrett! A progress list of the required criteria for merging this PR into |
@kimbarrett The following label will be automatically applied to this pull request:
When this pull request is ready to be reviewed, an "RFR" email will be sent to the corresponding mailing list. If you would like to change these labels, use the /label pull request command. |
Webrevs
|
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.
lgtm!
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.
Lgtm.
// Ensure any loads from the forwardee follow all changes that precede | ||
// the release-cmpxchg that performed the forwarding, possibly in some | ||
// other thread. | ||
OrderAccess::acquire(); |
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.
Maybe add a comment here that copy_unmarked_to_survivor_space
also guarantees this for all paths (i.e. both successful as well as failing CAS). Maybe this is not the correct place here, maybe in a comment for copy_unmarked_to_survivor_space
; or something like "copy_to_survivor_space
acts as an acquire" or something, so that the returned object can be used "safely" - idk.
@kimbarrett This change now passes all automated pre-integration checks. ℹ️ This project also has non-automated pre-integration requirements. Please see the file CONTRIBUTING.md for details. After integration, the commit message for the final commit will be:
You can use pull request commands such as /summary, /contributor and /issue to adjust it as needed. At the time when this comment was updated there had been 2 new commits pushed to the
Please see this link for an up-to-date comparison between the source branch of this pull request and the ➡️ To integrate this PR with the above commit message to the |
I have trouble understanding (2). I have no idea how it can happen that reading the same volatile memory location a second time can retrieve an older value. Regarding JDK-8229169, age() and age_top() read different sizes. The ordering issue was related to different Bytes which weren't read before AFAICS. |
log_develop_trace(gc, scavenge)("{%s %s " PTR_FORMAT " -> " PTR_FORMAT " (%d)}", | ||
should_scavenge(&new_obj) ? "copying" : "tenuring", | ||
new_obj->klass()->internal_name(), p2i((void *)o), p2i((void *)new_obj), new_obj->size()); | ||
// don't update this before the unallocation! |
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.
This comment gives me the impression that there is some racy going on here, and deallocation and update to new_obj
must be ordered this way. However, the actual reason is that the old value ofnew_obj
is used for deallocation. I think it's best to remove this comment; it doesn't really say anything interesting.
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.
That comment went away as part of latest change to avoid reloading the forwardee.
Dredging through the Standard, the C++ memory model does require read-read It's unclear to me what the Standard says about a case like JDK-8229169. The Be that as it may, I've found a way to entirely bypass the whole question. Mostly done re-running tests. |
Thanks a lot for checking! That makes sense. |
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.
Still good.
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.
Looks good. + Nice cleanup!
Thanks @TheRealMDoerr , @tschatzl , @walulyai for reviews. |
/integrate |
@kimbarrett Since your change was applied there have been 2 commits pushed to the
Your commit was automatically rebased without conflicts. Pushed as commit 5a66628. 💡 You may see a message that your pull request was closed with unmerged commits. This can be safely ignored. |
Please review this change to PSPromotionManager::copy_to_survivor_space
(ParallelGC) to remove some redundant work, and to add some missing memory
barriers.
There are two callers of copy_to_survivor_space, both of which wrap that
call with the idiom
if obj->is_forwarded() then
new_obj = obj->forwardee()
else
new_obj = copy_to_survivor_space(obj)
endif
There are problems with this.
(1) The first thing copy_to_survivor_space does is check whether the object
is already forwarded, and if so then return obj->forwardee_acquire(). The
idiom used by the callers is a redundant check, and the redundancy can't be
optimized away. It is also missing the acquire barrier that was added by
JDK-8154736 after long discussion.
(2) It turns out the forwardee_acquire() from JDK-8154736 isn't sufficient
after all. The "if is_forwarded() then use forwardee()" idiom is hiding
under the abstractions that we're doing two relaxed atomic loads of the mark
word, and there is nothing here to prevent the second from reading a value
older than that read by the first, with bad consequences. This possibility
came up in the discussion of JDK-8154736, but seems to have been either lost
or discounted. If you think loads from the same location can't do that, see
JDK-8229169 for a counter example.
Part of this change involves removing the conditionalization of the calls to
copy_to_survivor_space; just call it directly. However, it turns out that
some compilers don't inline copy_to_survivor_space because of its size. So
we refactored it into two functions, one doing the already marked check and
then calling the other to do most of the work. This is enough for the check
to be inlined into callers, so we've effectively removed the redundant inner
check. Note: This part of the change introduces a large block of whitespace
differences due to removal of an if-else and outdenting the body; I recommend
using a view that suppresses those when reviewing.
The second part of the change involves adding or moving some acquire barriers.
(a) For the initial check whether the object is already marked, if it is
then add an acquire fence before returning the forwardee. We could instead
use a load-acquire to obtain the mark word, but that would be an unneeded
acquire barrier on the much more common unmarked case. Also removed
forwardee_acquire(), which is no longer used.
(b) If the cmpxchg race is lost, add an acquire fence before fetching and
returning the forwardee. The failed release-cmpxchg effectively behaves
like a relaxed-load, which must preceed the forwardee access and any reads
from it.
I've also changed to only log copying when actually copied, not when already
copied and forwarded. Also changed a guarantee to an assert.
I looked at all uses of forwardee() in light of problem (2), and did not
find any additional problems. (That doesn't mean there aren't any, just
that I didn't spot any. This is low-level atomics, after all.)
Testing:
mach5 tier1-3,5,7 (tier3,5,7 are where a lot of ParallelGC testing is done).
Performance testing showed no significant change.
Progress
Issue
Reviewers
Reviewing
Using
git
Checkout this PR locally:
$ git fetch https://git.openjdk.java.net/jdk pull/4371/head:pull/4371
$ git checkout pull/4371
Update a local copy of the PR:
$ git checkout pull/4371
$ git pull https://git.openjdk.java.net/jdk pull/4371/head
Using Skara CLI tools
Checkout this PR locally:
$ git pr checkout 4371
View PR using the GUI difftool:
$ git pr show -t 4371
Using diff file
Download this PR as a diff file:
https://git.openjdk.java.net/jdk/pull/4371.diff