RFR: 8247213: G1: Reduce usage of volatile in favour of Atomic operations

Kim Barrett kim.barrett at oracle.com
Mon Jun 22 07:52:17 UTC 2020


> On Jun 14, 2020, at 4:51 AM, Kim Barrett <kim.barrett at oracle.com> wrote:
> 
>> On Jun 10, 2020, at 8:46 AM, Leo Korinth <leo.korinth at oracle.com> wrote:
>> 
>> […]
>> Enhancement:
>> https://bugs.openjdk.java.net/browse/JDK-8247213
>> 
>> Webrev:
>> http://cr.openjdk.java.net/~lkorinth/8247213/0/part1
>> http://cr.openjdk.java.net/~lkorinth/8247213/0/part2
>> http://cr.openjdk.java.net/~lkorinth/8247213/0/full
> 
> ------------------------------------------------------------------------------
> src/hotspot/share/runtime/atomic.hpp 
> 1011 template<typename T>
> 1012 static AtomicValue<T>* cast_to_atomic_ptr(T* nonatomic) {
> 
> I would really like to not have this.  I'd like to see how far we can
> get without it.
> 
> ------------------------------------------------------------------------------
> src/hotspot/share/runtime/atomic.hpp 
> 940 class AtomicValue {
> 
> Should it be default constructible? If so, should the default
> constructor guarantee value initialization? I think having a trivial
> default constructor is needed to avoid UB in some uses. See discussion
> of G1BOT's _offset_array below.
> 
> ------------------------------------------------------------------------------
> src/hotspot/share/gc/g1/g1BlockOffsetTable.cpp
> 45   _offset_array = cast_to_atomic_ptr((u_char*) bot_reserved.start());
> 
> This is currently the only use of cast_to_atomic_ptr. It leads to UB
> since the (non-trivial) constructor for AtomicValue is never called
> for any of the objects in the array. I think the call to
> memset_with_concurrent_readers is also UB. I *think* having a trivial
> default constructor solves the UB problem, but that isn't possible
> until C++11.
> 
> Rather than just casting to a pointer to AtomicValue<>, this could
> iterate over the array memory, doing placement new construction of the
> elements.  If we gave AtomicValue<> a non-trivial but empty default
> constructor (so not initializing the value), then such an iteration
> should be optimized away.  We'd still have the UB from memset_with_cr,
> though that could use an iteration with the one-arg constructor instead.
> Or ignore the technical UB and just keep using memset_with_cr.
> 
> And that gets rid of the one use (so far) of cast_to_atomic_ptr.
> 
> The problem, of course, is that having a trivial/empty default
> constructor means one could unintentionally construct without
> initialization. I think a private trivial default constructor would
> deal with that, though again that requires C++11.

Responding to myself about the above, here’s a way to get value
initialization by the default constructor but have another constructor that
does no initialization, and should be compiled to nothing.

// Put this somewhere convenient (not! in AtomicValue class).
struct NoInitFlag {};
const NoInitFlag NO_INIT;

Add these constructors:
AtomicValue() : _val() {}   // Value initialization.
AtomicValue(NoInitFlag) {}  // Intentionally implicit.

> src/hotspot/share/gc/g1/heapRegionManager.cpp
> 663   for (uint i = 0; i < _n_regions; ++i) {
> 664     new (&_claims[i]) AtomicValue<uint>(Unclaimed);
> 665   }
> 
> Even if AtomicValue has a trivial default destructor (as discussed
> above) to avoid UB when using memset, this change is an improvement.
> The old memset code only works because Unclaimed == 0.

Also related, s/destructor/constructor/



More information about the hotspot-dev mailing list