RFR 8191890: Biased locking still uses the inferior stop the world safepoint for revocation
coleen.phillimore at oracle.com
coleen.phillimore at oracle.com
Mon Jun 17 20:55:47 UTC 2019
On 6/17/19 2:14 PM, Patricio Chilano wrote:
> Hi Coleen,
>
> On 6/14/19 7:08 PM, coleen.phillimore at oracle.com wrote:
>>
>> Sorry for being late to the party.
>>
>> http://cr.openjdk.java.net/~pchilanomate/8191890/v02/webrev/src/hotspot/share/runtime/biasedLocking.cpp.frames.html
>>
>>
>> 586 if (_biased_locker == mark->biased_locker()) {
>> 587 if (mark->bias_epoch() == prototype->bias_epoch()) {
>>
>> Can you add a comment what this means? The object's biased locker
>> matches what we thought it was, and the epoch being the same means?
>> The epoch being equal means that this biaser actually might have this
>> lock? A comment would be good here.
> Yes, if the epoch is still valid it means the biaser could be
> currently synchronized on this object. If that's the case then we must
> walk its stack and change those monitor records into thin locks. Added
> comment.
>
>
>> 785 mark = res_mark; // Refresh mark with the latest value.
>>
>>
>> I don't see what this does either. I had to download your patch.
>> 'mark' isn't used outside the loop and it is reloaded at the top of
>> the loop.
> If the CAS fails, the mark needs to be updated with the new value so
> that when we get the current biaser (HR_SINGLE_REVOKE case) we
> actually get the updated biaser and not the old one. If we don't do
> that we could be handshaking the wrong thread, or worst we could hit
> an assert in walk_stack_and_revoke() for the "blt == THREAD", since
> the old thread could be ourselves.
I found where it's used below now. Maybe once this function is
refactored a bit, it'll be easier to see for next time. Looks good!
>
>
>> 796 obj->cas_set_mark(prototype_header->set_age(mark->age()), mark);
>>
>>
>> As an later enhancement, there should be some inline function in
>> markOop that returns the prototype header preserving the age of the
>> object, but I'll leave it to you to name.
> Ok, sounds good. That particular line was preexistent but I did added
> in some places "markOopDesc::prototype()->set_age(mark->age())", which
> is doing the same thing.
>
>
>> 555 RevokeOneBias(Handle* obj, JavaThread* requesting_thread,
>> JavaThread* biased_locker) ... 565 oop o = (*_obj)();
>>
>>
>> This was pre-existing your change, but passing Handle* is not
>> generally done, and is suspicious when it is because it must be
>> allocated with the thread calling the function. Can you change this
>> to Handle (not pointer)? I can't think why this would be done this way.
> Fixed.
>
>
Great. I was afraid there was some subtlety I didn't see. Please
retest with this though just in case. Sometimes bits of wierdness have
a strange reason that isn't documented.
>> 870 // All objects in objs should be locked by biaser
>> 871 void BiasedLocking::revoke(GrowableArray<Handle>* objs,
>> JavaThread *biaser) {
>>
>>
>> I don't see why BiasedLocking::revoke() and
>> BiasedLocking::revoke_at_safepoint() are so different.
>>
>> The name "revoke" should be something more descriptive of the
>> situation though, like revoke_for_current_thread() or something like
>> that (revoke_at_safepoint's objects are from the stack too for the
>> current thread...) I keep thinking "revoke" should be a leaf
>> function in biasedLocking.
> Yes, nice observation. Method
> revoke_at_safepoint(GrowableArray<Handle>* objs) could be removed and
> we could just use BiasedLocking::revoke(GrowableArray<Handle>* objs
> ...) instead, since it's called from deoptimization.cpp where all the
> objects in the array belong to the same JavaThread. The difference is
> that we don't do update_heuristics() for the non-safepoint case since
> it might trigger a bulk operation. For the safepoint case it doesn't
> matter because we are already at one, we don't have the overhead of
> requesting it. But I could combine them into one method and do the
> update_heuristics() only if we are at a safepoint, what do you think?
You could file a follow-up RFE for this if you want, since the current
version has gone through all the testing.
The change looks great!
Coleen
>
>> The change looks really good to me and I look forward to further
>> cleanups so maybe it'll make sense someday!
> Thanks for looking at this Coleen!
>
>
> Patricio
>> Thanks!
>> Coleen
>>
>> On 6/7/19 12:56 AM, Patricio Chilano wrote:
>>> Hi all,
>>>
>>> Here is v02 addressing comments made by Dan and David.
>>>
>>> Full webrev:
>>> http://cr.openjdk.java.net/~pchilanomate/8191890/v02/webrev/
>>> <http://cr.openjdk.java.net/%7Epchilanomate/8191890/v02/webrev/>
>>> Inc webrev:
>>> http://cr.openjdk.java.net/~pchilanomate/8191890/v02/inc/webrev/
>>> <http://cr.openjdk.java.net/%7Epchilanomate/8191890/v02/inc/>
>>>
>>> Thanks!
>>>
>>> Patricio
>>>
>>> On 6/6/19 7:37 PM, David Holmes wrote:
>>>> Hi Patricio,
>>>>
>>>> On 7/06/2019 9:19 am, Patricio Chilano wrote:
>>>>> Hi David,
>>>>>
>>>>>
>>>>> On 6/6/19 3:37 AM, David Holmes wrote:
>>>>>> Hi Patricio,
>>>>>>
>>>>>> First thanks for taking this on!
>>>>>>
>>>>>> I have some higher-level general discussion around this before
>>>>>> deep diving into the actual code review (not that I have much
>>>>>> there either :)).
>>>>>>
>>>>>> First to clarify how biased-locking works. I'm unclear when an
>>>>>> object can be rebiased after having its bias revoked? This
>>>>>> particularly relates to some of your assertions (as Markus has
>>>>>> queried) after the CAS to update the mark so that the bias is
>>>>>> revoked, and you then re-read the mark and assert the bias has
>>>>>> been revoked - what stops another thread from rebiasing the
>>>>>> object in between those two statements? Is it that rebiasing
>>>>>> cannot happen, or that it could only happen if there were an
>>>>>> intervening safepoint which in turn cannot happen?
>>>>> Once the bias of the object is revoked it will stay like that
>>>>> forever, it cannot happen that it goes back to having the 0x5
>>>>> pattern.
>>>>> Also, once the bias pattern in the prototype header for a class is
>>>>> revoked during a bulk revocation operation, if there is an object
>>>>> of that class that still has the bias pattern, a JavaThread that
>>>>> wants to synchronize on that object will always revoke the bias
>>>>> first. This is why I don't check if the CAS succeeded if the
>>>>> prototype of the class does not has the bias pattern, I just
>>>>> assert that the object is not biased anymore.
>>>>>
>>>>> Below I describe the cases where an object can be rebiased.
>>>>>
>>>>> Once a JavaThread biases an object for the first time, there are
>>>>> two cases that allows for that object to be rebiased:
>>>>> 1) If the epoch in the markword becomes invalid. For this to
>>>>> happen a bulk rebias operation is needed. This is why I do check
>>>>> if the CAS succeeded or not for these cases, since some other
>>>>> JavaThread could have rebiased it.
>>>>> 2) During a full GC, objects that are biased ( some JavaThread is
>>>>> set in the biaser bits) could have their markword be reset to 0x5.
>>>>> This means they will become anonymously biased again and so will
>>>>> look as if they were not biased yet. As to how this logic works:
>>>>> At the beginning of the full GC, BiasedLocking::preserve_marks()
>>>>> saves all the markwords for those objects that are currently
>>>>> locked and have a bias pattern. After that,
>>>>> markOopDesc::must_be_preserved_with_bias() will be called to
>>>>> decide if the markword of an object should be preserved or not. If
>>>>> the markword contains the bias pattern it is never preserved. At
>>>>> the end BiasedLocking::restore_marks() is called to restore the
>>>>> marks for those objects that we saved before. So this means that
>>>>> even if an object has a valid biaser, with valid epoch, if the
>>>>> object is not currently locked it could be reset during the GC.
>>>>> I'm not sure though if whenever
>>>>> markOopDesc::must_be_preserved_with_bias() returns false the
>>>>> garbage collector always does the reset or it just means it could
>>>>> reset it if it wants to. In any case I've seen that reset
>>>>> happening when doing handshakes. In fact, this is one of the
>>>>> reasons why the handshake could return that the bias was not
>>>>> revoked, since I don't check for the anonymously biased case in
>>>>> RevokeOneBias.
>>>>
>>>> Thanks for that very detailed set of descriptions. I won't pretend
>>>> to fully grok all the details as I'm not completely clear on the
>>>> role of the "epoch" or being anonymously biased, but I'm convinced
>>>> you have a full understanding of such things. :) In
>>>> revoke_and_rebias it was always a struggle for me to figure out
>>>> exactly when the "rebias" part could come into play.
>>>>
>>>>>
>>>>>> The main concern with a change like this (as with all the
>>>>>> handshake changes) is what new races this may allow and whether
>>>>>> they have all been accounted for. IIUC the handshake will still
>>>>>> be conducted by the VMThread so that still ensures serialization
>>>>>> wrt. safepoints (which makes it simpler to reason about things).
>>>>>> I've looked at some of the races you anticipated (like the
>>>>>> "resurrected" thread) and they seem to be handled correctly. I'm
>>>>>> unable to construct other races that might be problematic (but
>>>>>> that isn't saying a lot :) ).
>>>>> I agree that since we are now doing the revocation outside
>>>>> safepoints there is potential for additional races. But also one
>>>>> thing to note is that RevokeOneBias, which contains the logic of
>>>>> the handshake and is now replacing what we used to do at a
>>>>> safepoint, is not really different from the initial code in
>>>>> revoke_and_rebias() which is done outside safepoints. The
>>>>> handshake logic is like executing that initial part but with the
>>>>> right JavaThread so that if the object has a valid biaser, then
>>>>> that biaser is either ourselves or we are the VMThread while the
>>>>> biaser is blocked, so that we can execute revoke_own_lock(). In
>>>>> fact I was thinking at some point to combine them in some method
>>>>> (maybe try_fast_revoke()). The attempt_rebias flag and the
>>>>> update_heuristics() in revoke_and_rebias() complicated things so I
>>>>> kept them separate.
>>>>> I have also tried to think on all possible racy scenarios and
>>>>> couldn't find additional problems beside the "resurrected thread"
>>>>> one (although it's also not a guarantee of anything). But that's
>>>>> why I was thinking to check this in 14, so that if there are any
>>>>> problems we have plenty of testing time to detect them.
>>>>
>>>> Yes that is a good idea. No need to rush this into 13.
>>>>
>>>>>
>>>>>> src/hotspot/share/jfr/metadata/metadata.xml
>>>>>>
>>>>>> Is it the case that this event is now never generated from a
>>>>>> safepoint? Or have you just deleted the safepoint_id from the
>>>>>> event because it might not be at a safepoint? If the latter can't
>>>>>> we keep it and use 0 to indicate "not at a safepoint"? I think
>>>>>> the JFR folk need to comment on this part of the change anyway.
>>>>> This event will be created and commited only from
>>>>> BiasedLocking::single_revoke_with_handshake(). Now, the actual
>>>>> handshake that revoked the bias could be executed at a safepoint
>>>>> only if ThreadLocalHandshakes is false. But I understand that this
>>>>> is true for all platforms so the handshake should always be
>>>>> executed outside safepoints.
>>>>
>>>> Ok.
>>>>
>>>>> It would be great if JFR folks review this part.
>>>>
>>>> Try to grab Markus :)
>>>>
>>>>>
>>>>>> src/hotspot/share/runtime/biasedLocking.cpp
>>>>>>
>>>>>> I second Dan's comment about combining cleanup and code motion in
>>>>>> a big change like this - it does make it much harder to spot the
>>>>>> real difference.
>>>>> Ok, already two objections on this so I'll revert moving the
>>>>> heuristics part. I think I also moved
>>>>> clean_up_cached_monitor_info() and I will double check any other
>>>>> movements.
>>>>>
>>>>>
>>>>>> I note Dan picked up on the lack of p2i and other stuff related
>>>>>> to the logging statements, and that you indicated they were
>>>>>> fixed. I note that all that stuff is pre-existing so I'm unclear
>>>>>> now whether you have fixed all the logging in the file or only
>>>>>> the statements in the code you have changed or added? Again such
>>>>>> cleanup may be best done separately.
>>>>> I haven't fixed the existing log statements, only the ones Dan
>>>>> mentioned which are in single_revoke_with_handshake(),
>>>>> revoke_own_lock(), and in VM_HandshakeOneThread(). Ok, I can fix
>>>>> the other ones in a cleanup later along with code movement and the
>>>>> removal of the attemp_rebias flag which we are not using.
>>>>
>>>> Okay. To be clear I don't expect you to fix all the existing uses I
>>>> just wanted to clarify which ones you had fixed.
>>>>
>>>>>
>>>>>> 640 void BiasedLocking::revoke_own_lock(oop obj, JavaThread*
>>>>>> biased_locker) {
>>>>>> 641 assert(!SafepointSynchronize::is_at_safepoint() ||
>>>>>> !ThreadLocalHandshakes,
>>>>>> 642 "if ThreadLocalHandshakes is enabled this should
>>>>>> always be executed outside safepoints");
>>>>>> 643 assert(Thread::current() == biased_locker ||
>>>>>> Thread::current()->is_VM_thread(), "wrong thread");
>>>>>> 644
>>>>>>
>>>>>> This is called "revoke_own_lock" but it can also be executed by
>>>>>> the VMThread - so its not its own lock. Also we don't revoke
>>>>>> anything related to a "lock" - we revoke a bias from the markword
>>>>>> of an oop. I think a better name is needed.
>>>>> Yes, I didn't really like it either. How about
>>>>> walk_stack_and_revoke() ?
>>>>
>>>> That sounds good to me. Roll on v2 :)
>>>>
>>>> Thanks,
>>>> David
>>>> -----
>>>>
>>>>>
>>>>>> src/hotspot/share/runtime/handshake.cpp
>>>>>>
>>>>>> 125 log_trace(handshake)("JavaThread " INTPTR_FORMAT " is
>>>>>> not alive", (intptr_t)_target);
>>>>>>
>>>>>> Use p2i(_target) rather than cast to intptr_t.
>>>>> Fixed.
>>>>>
>>>>>
>>>>>> That's all from me.
>>>>> Thanks for looking into this David! If you are okay with the
>>>>> "walk_stack_and_revoke()" name then I can send v2.
>>>>>
>>>>>
>>>>> Thanks!
>>>>>
>>>>>
>>>>> Patricio
>>>>>
>>>>>> Thanks,
>>>>>> David
>>>>>> -----
>>>>>>
>>>>>>
>>>>>> On 30/05/2019 2:29 am, Patricio Chilano wrote:
>>>>>>> Hi all,
>>>>>>>
>>>>>>> Could you review this patch that uses thread local handshakes
>>>>>>> instead of safepoints to revoke the biases of locked objects?
>>>>>>>
>>>>>>> Webrev:
>>>>>>> http://cr.openjdk.java.net/~pchilanomate/8191890/v01/webrev/
>>>>>>> Bug:
>>>>>>> https://bugs.openjdk.java.net/browse/JDK-8191890
>>>>>>>
>>>>>>>
>>>>>>> Today whenever a JavaThread needs to revoke the bias of an
>>>>>>> object that has been biased by another JavaThread (and where the
>>>>>>> epoch is still valid and the prototype header of the class still
>>>>>>> has the bias pattern) it needs to request a safepoint operation.
>>>>>>> The VMThread inside the safepoint walks the stack of the biaser
>>>>>>> looking for lock records associated with the biased object, and
>>>>>>> converts them to thin locks if any are found.
>>>>>>> This patch uses thread local handshakes instead, since we
>>>>>>> actually only need to be able to safely walk the stack of the
>>>>>>> JavaThread that biased the object and not other JavaThreads.
>>>>>>>
>>>>>>> Some notes about the patch:
>>>>>>>
>>>>>>> - Thanks to Robbin for initial work on this patch and for advice
>>>>>>> and feedback!
>>>>>>> - We still execute bulk rebias and bulk revoke operations inside
>>>>>>> safepoints, since in those cases all the JavaThread's stacks
>>>>>>> need to be walked to potentially update lock records.
>>>>>>> - The method revoke_bias() was renamed to
>>>>>>> single_revoke_at_safepoint(). This method is still kept because
>>>>>>> there are places where we check whether we are already at
>>>>>>> safepoint when trying to revoke. In those cases, if we are
>>>>>>> already at a safepoint we simply end up calling this method.
>>>>>>> - Handshakes are executed as VMOperations so the VMThread is
>>>>>>> still involved in the revocation. This means we cannot have
>>>>>>> different revocations being executed in parallel (same as with
>>>>>>> safepoints). Ideally we would like to execute thread local
>>>>>>> handshakes without needing for the VMThread to participate.
>>>>>>> However, now other JavaThreads that do not participate in the
>>>>>>> revocation are allow to continue making progress.
>>>>>>>
>>>>>>> Run several benchmarks and mostly performance seems unaffected.
>>>>>>> Measured the average time it takes for revoking bias with a
>>>>>>> handshake and with a safepoint and numbers are pretty similar
>>>>>>> varying between benchmarks. Some numbers are shown below:
>>>>>>>
>>>>>>> specjbb2015
>>>>>>> Handshakes Safepoints
>>>>>>> Linux 4ms 4.6ms
>>>>>>> Windows 11ms 19ms
>>>>>>>
>>>>>>> startup benchmarks
>>>>>>> Handshakes Safepoints
>>>>>>> Linux 159us 248us
>>>>>>> Windows 150us 111us
>>>>>>>
>>>>>>> Overall the variation is not enough to show significant
>>>>>>> difference in performance, considering also that revocations of
>>>>>>> a valid biaser are usually a fraction of the overall running
>>>>>>> time of a benchmark (specially jbb2015). In any case using
>>>>>>> handshakes allows other JavaThreads to make progress during that
>>>>>>> time, minimizing STW operations.
>>>>>>>
>>>>>>> In terms of testing, the patch passed several runs of tiers1-6
>>>>>>> in mach5 on Windows, Linux, MacOS and Solaris.
>>>>>>>
>>>>>>> Thanks!
>>>>>>>
>>>>>>> Patricio
>>>>>>>
>>>>>
>>>
>>
>
More information about the hotspot-runtime-dev
mailing list