RFR: 8263107: PSPromotionManager::copy_and_push_safe_barrier needs acquire memory barrier

Albert Mingkun Yang ayang at openjdk.java.net
Mon Jun 7 14:51:13 UTC 2021


On Sat, 5 Jun 2021 03:30:42 GMT, Kim Barrett <kbarrett at openjdk.org> wrote:

> 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.

src/hotspot/share/gc/parallel/psPromotionManager.inline.hpp line 308:

> 306:     }
> 307: 
> 308:     // don't update this before the unallocation!

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 of`new_obj` is used for deallocation. I think it's best to remove this comment; it doesn't really say anything interesting.

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

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


More information about the hotspot-dev mailing list