RFR: JDK-8211279: Verify missing object equals barriers
Per Liden
per.liden at oracle.com
Wed Oct 3 10:21:01 UTC 2018
Hi,
On 2018-10-03 11:23, Erik Österlund wrote:
> Hi Per and Roman,
>
> My favourite solution to this problem would be to in the oop::operator()
> definition assert that RawAccess<>::equals(o1, o2) ==
> Access<>::equals(o1, o2).
> This verification could have false positives where it considers things
> safe that are in fact unsafe at runtime. On the other hand, the already
> proposed verification could have false negatives, where a completely
> valid == is considered not safe, despite being safe. For example if the
> operands have already been Access<>::resolved, then it is safe to use ==
> to compare them.
>
> In practice, I would be surprised if my proposed solution did not
> immediately hit the assert during testing when the usage is wrong. And
> every time it hits the assert, it would be due to provably wrong usage
> of ==.
>
> As a benefit, we do not have to clutter the shared barrier set with a
> function that essentially says if (UseShenandoah) ShouldNotReachHere();
>
> What do you think?
That would be a nicely GC agnostic and much less intrusive approach,
which turns all this into a one-liner fix. I like it.
cheers,
Per
>
> Thanks,
> /Erik
>
> On 2018-10-03 10:42, Per Liden wrote:
>> Hi Roman,
>>
>> On 10/01/2018 02:48 PM, Roman Kennke wrote:
>>> Hi Per,
>>>
>>>
>>>>> GCs like Shenandoah require an extra barrier for comparing objects
>>>>> (oop==oop). It is easy to forget or overlook this. It would be very
>>>>> useful to have a flag to turn on extra verification that catches
>>>>> missing
>>>>> object equality barriers.
>>>>>
>>>>> This change inserts an assert into == and != operators for the oop
>>>>> class
>>>>> in oopsHierarchy.hpp. This only gets compiled in fastdebug builds
>>>>> (when
>>>>> CHECK_UNHANDLED_OOPS is on).
>>>>>
>>>>> It also adds a develop flag VerifyObjectEquals that is used to turn on
>>>>> this verification.
>>>>>
>>>>> It also adds a method oopDesc::unsafe_equals(..) to be used in cases
>>>>> where you know what what you are doing, and really want to use
>>>>> direct ==
>>>>> comparison without using barriers. This is used in e.g.
>>>>> ReferenceProcessor or all over the place in ShenandoahGC.
>>>>>
>>>>> The change also fixes a couple of places where oops are compared to
>>>>> non-oops like Universe::non_oop_word() to use the oop==void* operator
>>>>> instead, so those don't falsely trip the verification.
>>>>>
>>>>> It doesn't make sense to turn this check on if you're not running a GC
>>>>> that needs it, unless you want to go fix all the oop==oop in the GC
>>>>> itself.
>>>>>
>>>>> Bug:
>>>>> https://bugs.openjdk.java.net/browse/JDK-8211279
>>>>> Webrev:
>>>>> http://cr.openjdk.java.net/~rkennke/JDK-8211279/webrev.00/
>>>>>
>>>>> What do you think?
>>>>
>>>> So this means we would have a verification option that, when enabled,
>>>> always crashes the VM unless you run Shenandoah? That doesn't sound
>>>> quite right to me. This should just be a noop when not using
>>>> Shenandoah,
>>>> don't you think?
>>>
>>>
>>> Hmm, right. Let's add some BarrierSet-infrastructure to handle this, and
>>> remove the option (it would be a GC-'private' option). It would probably
>>> have looked slightly better to do this in BarrierSet::Access, next to
>>> the Access::equals() API, but I don't feel like adding tons of
>>> boilerplate just to add this. (Infact, this is a big red warning signal
>>> regarding the Access API...)
>>>
>>> http://cr.openjdk.java.net/~rkennke/JDK-8211279/webrev.01/
>>>
>>> How does this look now?
>>
>>
>> src/hotspot/share/oops/oop.hpp
>> ------------------------------
>>
>> 157 inline static bool unsafe_equals(oop o1, oop o2) {
>> 158 return (void*) o1 == (void*) o2;
>> 159 }
>>
>> I think this should be called oopDesc::equals_raw() to follow the
>> naming convention we have for these types of functions. Also, it
>> should do:
>>
>> return RawAccess<>::equals(o1, o2);
>>
>> Also, please make it a one-liner to better match the look of
>> oopDesc::equals().
>>
>>
>> src/hotspot/share/gc/shared/referenceProcessor.cpp
>> --------------------------------------------------
>>
>> 477 while (! oopDesc::unsafe_equals(next, obj)) {
>>
>> Stray white-space, please remove.
>>
>>
>> src/hotspot/share/gc/shared/referenceProcessor.hpp
>> --------------------------------------------------
>>
>> 152 assert(! oopDesc::unsafe_equals(_current_discovered,
>> _first_seen), "cyclic ref_list found");
>>
>> Stray white-space, please remove.
>>
>>
>> src/hotspot/share/oops/accessBackend.hpp
>> ----------------------------------------
>>
>> 413 static bool equals(oop o1, oop o2) { return (void*) o1 ==
>> (void*) o2; }
>>
>> Stray white-spaces, please make that "(void*)o1 == (void*)o2".
>>
>>
>> src/hotspot/share/gc/shared/barrierSet.hpp
>> ------------------------------------------
>>
>> 134 virtual void verify_equals(oop o1, oop o2) { }
>>
>> I'm thinking this should be:
>>
>> virtual bool oop_equals_operator_allowed() { return true; }
>>
>> And let oop::operator==(...) do:
>>
>> assert(BarrierSet::barrier_set()->oop_equals_operator_allowed(), "Not
>> allowed");
>>
>>
>> Erik, can you live with this, or do you have any better ideas here?
>> I'm not ecstatic about having a new function on BarrierSet just for
>> this. Should we just make oop::operator==() private and fix all the
>> places where it's used? One could also argue the oop::operator==()
>> _is_ the raw equals and that we should be allowed to use it. Any other
>> ideas?
>>
>>
>> cheers,
>> Per
>>
>>>
>>> It still passes hotspot/jtreg:tier1 here.
>>>
>>> Thanks for looking at this!
>>> Roman
>>>
>
More information about the hotspot-gc-dev
mailing list