RFR (S): 8182703: Correct G1 barrier queue lock orderings
Mikael Gerdin
mikael.gerdin at oracle.com
Mon Jul 3 11:57:48 UTC 2017
Hi Erik,
On 2017-06-26 15:34, Erik Österlund wrote:
> Hi,
>
> Webrev: http://cr.openjdk.java.net/~eosterlund/8182703/webrev.02/
I think this change makes sense and I agree with your reasoning below.
I'm leaning towards suggesting creating a named enum value for
"access+1" to begin a move towards getting rid of adding and subtracting
values from enums in this code. I don't have a good name for it, though.
/Mikael
> Bug: https://bugs.openjdk.java.net/browse/JDK-8182703
>
> The G1 barrier queues have very awkward lock orderings for the following
> reasons:
>
> 1) These queues may queue up things when performing a reference write or
> resolving a jweak (intentionally or just happened to be jweak, even
> though it looks like a jobject), which can happen in a lot of places in
> the code. We resolve JNIHandles while holding special locks in many
> places. We perform reference writes also in many places. Now the
> unsuspecting hotspot developer might think that it is okay to resolve a
> JNIHandle or perform a reference write while possibly holding a special
> lock. But no. In some cases, object writes have been moved out of locks
> and replaced with lock-free CAS, only to dodge the G1 write barrier
> locks. I don't think the G1 lock ordering issues should shape the shared
> code rather than the other way around.
> 2) There is an issue that the shared queue locks have a "special" rank,
> which is below the lock ranks used by the cbl monitor and free list
> monitor. This leads to an issue when these locks have to be taken while
> holding the shared queue locks. The current solution is to drop the
> shared queue locks temporarily, introducing nasty data races. These
> races are guarded, but the whole race seems very unnecessary.
>
> I argue that if the G1 write barrier queue locks were simply set
> appropriately in the first place by analyzing what ranks they should
> have, none of the above issues would exist. Therefore I propose this new
> ordering.
>
> Specifically, I recognize that locks required for performing memory
> accesses and resolving JNIHandles are more special than the "special"
> rank. Therefore, this change introduces a new lock ordering category
> called "access", which is to be used by barriers required to perform
> memory accesses. In other words, by recognizing the rank is more special
> than "special", we can remove "special" code to walk around making its
> rank more "special". That seems desirable to me. The access locks need
> to comply to the same constraints as the special locks: they may not
> perform safepoint checks.
>
> The old lock ranks were:
>
> SATB_Q_FL_lock: special
> SATB_Q_CBL_mon: leaf - 1
> Shared_SATB_Q_lock: leaf - 1
>
> DirtyCardQ_FL_lock: special
> DirtyCardQ_CBL_mon: leaf - 1
> Shared_DirtyCardQ_lock: leaf - 1
>
> The new lock ranks are:
>
> SATB_Q_FL_lock: access (special - 2)
> SATB_Q_CBL_mon: access (special - 2)
> Shared_SATB_Q_lock: access + 1 (special - 1)
>
> DirtyCardQ_FL_lock: access (special - 2)
> DirtyCardQ_CBL_mon: access (special - 2)
> Shared_DirtyCardQ_lock: access + 1 (special - 1)
>
> Analysis:
>
> Each PtrQueue and PtrQueueSet group, SATB or DirtyCardQ have the same
> group of locks. The free list lock, the completed buffer list monitor
> and the shared queue lock.
>
> Observations:
> 1) The free list lock and completed buffer list monitors (members of
> PtrQueueSet) are disjoint. We never hold both of them at the same time.
> Rationale: The free list lock is only used from
> PtrQueueSet::allocate_buffer, PtrQueueSet::deallocate_buffer and
> PtrQueueSet::reduce_free_list, and no callsite from there can be
> expanded where the cbl monitor is acquired. So therefore it is
> impossible to acquire the cbl monitor while holding the free list lock.
> The opposite case of acquiring the free list lock while holding the cbl
> monitor is also not possible; only the following places acquire the cbl
> monitor: PtrQueueSet::enqueue_complete_buffer,
> PtrQueueSet::merge_bufferlists,
> PtrQueueSet::assert_completed_buffer_list_len_correct,
> PtrQueueSet::notify_if_necessary, FreeIdSet::claim_par_id,
> FreeIdSet::release_par_id, DirtyCardQueueSet::get_completed_buffer,
> DirtyCardQueueSet::clear,
> SATBMarkQueueSet::apply_closure_to_completed_buffer and
> SATBMarkQueueSet::abandon_partial_marking. Again, neither of these paths
> where the cbl monitor is held can expand callsites to a place where the
> free list locks are held. Therefore it holds that the cbl monitor can
> not be held while the free list lock is held, and the free list lock can
> not be held while the cbl monitor is held. Therefore they are held
> disjointly.
> 2) We might hold the shared queue locks before acquiring the completed
> buffer list monitor. (today we drop the shared queue lock then and
> reacquire it later as a hack as already described)
> 3) We do not acquire a shared queue lock while holding the free list
> lock or completed buffer list monitor, as there is no reference from a
> PtrQueueSet to its shared queue, so those code paths do not know how to
> reach the shared PtrQueue to acquire its lock. The derived classes are
> exceptions but they never use the shared queue lock while holding the
> completed buffer list monitor or free list lock. DirtyCardQueueSet uses
> the shared queue for concatenating logs (in a safepoint without holding
> those locks). The SATBMarkQueueSet uses the shared queue for filtering
> the buffers, fiddling with activeness, printing and resetting, all
> without grabbing any locks.
> 4) We do not acquire any other lock (above event) while holding the free
> list lock or completed buffer list monitors. This was discovered by
> manually expanding the call graphs from where these two locks are held.
>
> Derived constraints:
> a) Because of observation 1, the free list lock and completed buffer
> list monitors can have the same rank.
> b) Because of observations 1 and 2, the shared queue lock ought to have
> a rank higher than the ranks of the free list lock and the completed
> buffer list monitors (not the case today).
> c) Because of of observation 3 and 2, the free list lock and completed
> buffer list monitors ought to have a rank lower than the rank of the
> shared queue lock.
> d) Because of observation 4 (and constraints a-c), all the barrier locks
> should be below the "special" rank without violating any existing ranks.
>
> The proposed new lock ranks conform to the constraints derived from my
> observations. It is worth noting that the potential relationship that
> could break (and why they do not) are:
> 1) If a lock is acquired from within the barriers that does not involve
> the shared queue lock, the free list lock or the completed buffer list
> monitor, we have now inverted their relationship as that other lock
> would probably have a rank higher than or equal to "special". But due to
> observation 4, there are no such cases.
> 2) The relationship between the shared queue lock and the completed
> buffer list monitor has been changed so both can be held at the same
> time if the shared queue lock is acquired first (which it is). This is
> arguably the way it should have been from the first place, and the old
> solution had ugly hacks where we would drop the shared queue lock to not
> run into the lock order assert (and only not to run into the lock order
> assert, i.e. not to avoid potential deadlock) to ensure the locks are
> not held at the same time. That code has now been removed, so that the
> shared queue lock is still held when enqueueing completed buffers (no
> dodgy dropping and reclaiming), and the code for handling the races due
> to multiple concurrent enqueuers has also been removed and replaced with
> an assertion that there simply should not be multiple concurrent
> enqueuers. Since the shared queue lock is now held throughout the whole
> operation, there will be no concurrent enqueuers.
> 3) The completed buffer list monitor used to have a higher rank than the
> free list lock. Now they have the same. Therefore, they could previously
> allow them to be held at the same time if the cbl monitor was acquired
> first. However, as discussed, there is no such case, and they ought to
> have the same rank not to confuse their true disjointness. If anyone
> insists we do not break this relationship despite the true disjointness,
> I could consent to adding another access lock rank, like this:
> http://cr.openjdk.java.net/~eosterlund/8182703/webrev.01/ but I think it
> seems better to have the same rank since they are actually truly
> disjoint and should remain disjoint.
>
> I do recognize that long term, we *might* want a lock-free solution or
> something (not saying we do or do not). But until then, the ranks ought
> to be corrected so that they do not cause these problems causing
> everyone to bash their head against the awkward G1 lock ranks throughout
> the code and make hacks around it.
>
> Testing: JPRT with hotspot all and lots of local testing.
>
> Thanks,
> /Erik
More information about the hotspot-gc-dev
mailing list