RFR 8191890: Biased locking still uses the inferior stop the world safepoint for revocation
Patricio Chilano
patricio.chilano.mateo at oracle.com
Thu May 30 22:16:12 UTC 2019
Hi Dan,
On 5/30/19 3:28 PM, Daniel D. Daugherty wrote:
> On 5/29/19 12:29 PM, 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/
>
> src/hotspot/share/runtime/biasedLocking.hpp
> No comments.
>
> src/hotspot/share/runtime/vmOperations.hpp
> No comments.
>
> src/hotspot/share/runtime/biasedLocking.cpp
> L170: assert(Thread::current()->is_VM_thread(), "should be
> VMThread");
> L334: assert(Thread::current()->is_VM_thread(), "should be
> VMThread");
> Since you are assert()'ing the condition: s/should/must/
>
> L463: virtual bool doit_prologue() { return true; }
> You get a doit_prologue() that simply return 'true' for
> free from the VM_Operation super class.
>
> L592: log_info(biasedlocking, handshake)("JavaThread "
> INTPTR_FORMAT " handshaking JavaThread "
> L593: INTPTR_FORMAT " to
> revoke object " INTPTR_FORMAT, (intptr_t)requester,
> L594: (intptr_t)biaser,
> p2i((void *)obj()));
> Use p2i(requester) and p2i(biaser) instead of the casts.
> p2i(obj()) should also work without the cast.
>
> L602: log_info(biasedlocking, handshake)("Handshake revocation
> for object " INTPTR_FORMAT " succeded. Bias was %srevoked",
> Typo - s/succeded/succeeded/
>
> L603: p2i((void *)obj()),
> (revoke.status_code() == BIAS_REVOKED ? "" : "already "));
> p2i(obj()) should also work without the cast.
>
> L609: }
> L610: else {
> nit - generally HotSpot style is '} else {'
>
> L654: (intptr_t)Thread::current(),
> L655: p2i((void *)obj),
> L656: (intptr_t) mark,
> L658: (intptr_t)
> obj->klass()->prototype_header(),
> L659: (intptr_t) biased_locker,
> Use p2i(foo) instead of (intptr_t)foo.
> p2i(obj()) should also work without the cast.
>
> L671: p2i((void *) obj));
> L679: p2i((void *) obj));
> p2i(obj()) should also work without the cast.
>
> L672: // Assume recursive case and fix up highest lock later
> Perhaps:
> // Assume recursive case and fix up the actual highest
> lock below.
>
> L687: // Must release storing the lock address for platforms
> without TSO
> Typo - s/storing/store/
>
> L699: assert(!obj->mark()->has_bias_pattern(), "why not?");
> Perhaps: "must not be biased"
Fixed all above.
> old L313: enum HeuristicsResult {
> old L321: static HeuristicsResult update_heuristics(oop o, bool
> allow_rebias) {
> new L703: enum HeuristicsResult {
> new L711: static HeuristicsResult update_heuristics(oop o) {
> This is all code motion with the exception of removing the
> unused 'allow_rebias' parameter. That makes the code harder
> to review.
Yes, I wanted to moved it above revoke_and_rebias() where it is actually
used. Also I wanted to put single_revoke_at_safepoint() and
bulk_revoke_or_rebias_at_safepoint() together. But I can move it back
where it was if you want.
> L767: while(true) {
> nit - need a space before '('
>
> L786: // Read the updated mark
> L787: mark = res_mark;
> L826: // Read the updated mark
> L827: mark = res_mark;
> Perhaps:
> mark = res_mark; // Refresh mark with the latest value.
Fixed both.
> But what are you trying to do with these updates to 'mark'?
If the CAS failed in those cases then it means that something changed in
the markword. Either is not biased anymore, or somebody biased the
object. If it's the first case then update_heuristics() will return
HR_NOT_BIASED, so the update doesn't matter. Otherwise somebody biased
the object and update_heuristics() will either return HR_SINGLE_REVOKE
or HR_BULK_REVOKE. For the latter case that update does not matter
either. But for the HR_SINGLE_REVOKE case we will have to read the
biaser (JavaThread *blt = mark->biased_locker()) to see which JavaThread
to handshake. If we use the old mark then we would be handshaking the
old thread (it could even be ourselves in which case we would hit an
assert in revoke_own_lock()).
We could just try to avoid the update and do the following when reading
the biaser:
JavaThread *blt = obj->mark()->biased_locker();
but we could also hit an assert this time in biased_locker(), since by
the time update_heuristics() returned and before we execute that line,
the biaser could have chosen to revoke the bias. We could do an
additional check for the bias pattern (once more) before trying to read
the biaser. But I think this would look weird since we are already in a
branch where the mark should be biased.
Alternatively, two other options could be:
1) update_heuristics() could be passed a (JavaThread*) which it would
set to the biaser.
2) use the old check (mark->biased_locker() == THREAD &&
prototype_header->bias_epoch() == mark->bias_epoch()). But I don't like
this approach because it involves reading more values, which are sort of
redundant since we can already tell by updating the markword above which
condition we are in.
> This line at the top of the loop obviates those assignments.
> L772: markOop mark = obj->mark();
>
> Perhaps you meant for L772 to be before the 'while (true)'?
The loop will only be executed again if we do not succeed in revoking
from single_revoke_with_handshake() (the biaser could have change and
that prevents us from revoking). In that case we would have to execute
revoke_and_rebias() as if it was the first time.
> src/hotspot/share/runtime/handshake.cpp
> L125: log_trace(handshake)("JavaThread " INTPTR_FORMAT " is
> not alive", (intptr_t)_target);
> L129: log_trace(handshake)("JavaThread " INTPTR_FORMAT "
> signaled, begin attempt to process by VMThtread", (intptr_t)_target);
> Use 'p2i(_target)' instead of cast.
Fixed.
> src/hotspot/share/runtime/deoptimization.cpp
> No comments.
>
> src/hotspot/share/interpreter/interpreterRuntime.cpp
> No comments.
>
> src/hotspot/share/jfr/metadata/metadata.xml
> No comments.
>
> src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/VMOps.java
> No comments.
>
> test/jdk/jdk/jfr/event/runtime/TestBiasedLockRevocationEvents.java
> Please update copyright year.
Fixed.
> This looks great. As with anything to do with locking, I'll have to
> let this percolate in the background and I may have more comments or
> questions. None of my comments are critical/must-fix right now type
> stuff.
Great, I'll wait then to send v02 in case you have more comments.
> Outstanding job on a very arcane and complicated part of the system.
Thanks Dan! : ) And thanks for reviewing this!
Patricio
> Dan
>
>
>> 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