Relax LazyDataObject.augmentations() implementation 92/76992/1
authorRobert Varga <robert.varga@pantheon.tech>
Sat, 13 Oct 2018 09:35:56 +0000 (11:35 +0200)
committerRobert Varga <nite@hq.sk>
Mon, 15 Oct 2018 13:14:18 +0000 (13:14 +0000)
We use double-checked locking to guard against concurrent
initialization of cachedAugmentations. While this ensures we do
not do the same work twice, it is a bit costly for the typical
case when the augmentations field is accessed once.

During the first call, we perform:
- a volatile read
- a lock acquire
- a volatile read
- compute the value (exclusive)
- a volatile write
- a lock release

Rather than doing that, allow the value to be computed concurrently
and use compare-and-swap to initialize it. Hence for the first call
we perform:
- a volatile read
- compute the value (concurrent)
- a compare-and-set

In case of concurrent initialization, all threads will compute
the value, but all except the first one will issue another volatile
read and throw their work away.

JIRA: MDSAL-377
Change-Id: I1dbf9c4e60cf9800720eea31bd9de936b1dae5aa
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
(cherry picked from commit 6379e438ee4c8496c7d71865b1a29409dcd06d7b)

binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/LazyDataObject.java

index 587fb8d24260169878ed45a746c3001eec290685..6523a8adee8159e666fd53e44728aa70b07616ac 100644 (file)
@@ -20,6 +20,7 @@ import java.util.Arrays;
 import java.util.Map;
 import java.util.Objects;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
 import org.opendaylight.mdsal.binding.dom.codec.util.AugmentationReader;
 import org.opendaylight.mdsal.binding.spec.naming.BindingMapping;
 import org.opendaylight.mdsal.binding.spec.reflect.BindingReflections;
@@ -46,6 +47,9 @@ class LazyDataObject<D extends DataObject> implements InvocationHandler, Augment
     private final NormalizedNodeContainer<?, PathArgument, NormalizedNode<?, ?>> data;
     private final DataObjectCodecContext<D,?> context;
 
+    @SuppressWarnings("rawtypes")
+    private static final AtomicReferenceFieldUpdater<LazyDataObject, ImmutableMap> CACHED_AUGMENTATIONS_UPDATER =
+            AtomicReferenceFieldUpdater.newUpdater(LazyDataObject.class, ImmutableMap.class, "cachedAugmentations");
     private volatile ImmutableMap<Class<? extends Augmentation<?>>, Augmentation<?>> cachedAugmentations = null;
     private volatile Integer cachedHashcode = null;
 
@@ -158,18 +162,13 @@ class LazyDataObject<D extends DataObject> implements InvocationHandler, Augment
     }
 
     private Map<Class<? extends Augmentation<?>>, Augmentation<?>> getAugmentationsImpl() {
-        ImmutableMap<Class<? extends Augmentation<?>>, Augmentation<?>> ret = cachedAugmentations;
-        if (ret == null) {
-            synchronized (this) {
-                ret = cachedAugmentations;
-                if (ret == null) {
-                    ret = ImmutableMap.copyOf(context.getAllAugmentationsFrom(data));
-                    cachedAugmentations = ret;
-                }
-            }
+        ImmutableMap<Class<? extends Augmentation<?>>, Augmentation<?>> local = cachedAugmentations;
+        if (local != null) {
+            return local;
         }
 
-        return ret;
+        local = ImmutableMap.copyOf(context.getAllAugmentationsFrom(data));
+        return CACHED_AUGMENTATIONS_UPDATER.compareAndSet(this, null, local) ? local : cachedAugmentations;
     }
 
     @Override