Bump yangtools to 5.0.0-SNAPSHOT
[mdsal.git] / binding / mdsal-binding-dom-codec / src / main / java / org / opendaylight / mdsal / binding / dom / codec / impl / DataContainerCodecPrototype.java
index 9fa864d825305eb60793ff2c5d7832f38ac44a58..73a52cfd12108d6d303b4d518aaeb428b9b90c02 100644 (file)
@@ -8,8 +8,11 @@
 package org.opendaylight.mdsal.binding.dom.codec.impl;
 
 import com.google.common.collect.Iterables;
-import javax.annotation.concurrent.GuardedBy;
-import org.opendaylight.mdsal.binding.dom.codec.api.BindingCodecTreeNode.ChildAddressabilitySummary;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
+import org.checkerframework.checker.lock.qual.Holding;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingDataObjectCodecTreeNode.ChildAddressabilitySummary;
 import org.opendaylight.mdsal.binding.dom.codec.impl.NodeCodecContext.CodecContextFactory;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.DataRoot;
@@ -19,8 +22,8 @@ import org.opendaylight.yangtools.yang.common.QNameModule;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
-import org.opendaylight.yangtools.yang.model.api.AnyDataSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.AnydataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
@@ -38,6 +41,17 @@ import org.slf4j.LoggerFactory;
 final class DataContainerCodecPrototype<T extends WithStatus> implements NodeContextSupplier {
     private static final Logger LOG = LoggerFactory.getLogger(DataContainerCodecPrototype.class);
 
+    private static final VarHandle INSTANCE;
+
+    static {
+        try {
+            INSTANCE = MethodHandles.lookup().findVarHandle(DataContainerCodecPrototype.class,
+                "instance", DataContainerCodecContext.class);
+        } catch (NoSuchFieldException | IllegalAccessException e) {
+            throw new ExceptionInInitializerError(e);
+        }
+    }
+
     private final T schema;
     private final QNameModule namespace;
     private final CodecContextFactory factory;
@@ -45,7 +59,24 @@ final class DataContainerCodecPrototype<T extends WithStatus> implements NodeCon
     private final PathArgument yangArg;
     private final ChildAddressabilitySummary childAddressabilitySummary;
 
-    private volatile DataContainerCodecContext<?, T> instance = null;
+    /*
+     * This field is now utterly in the hot path of CodecDataObject's instantiation of data. It is a volatile support
+     * double-checked locking, as detailed in https://en.wikipedia.org/wiki/Double-checked_locking#Usage_in_Java
+     *
+     * We are opting for the document Java 9 equivalent, forsaking our usual CAS-based approach, where we'd re-assert
+     * concurrent loads. This improves safety a bit (by forcing a synchronized() block), as we expect prototypes to be
+     * long-lived.
+     *
+     * In terms of safe publication, we have two volatile fields in DataObjectCodecContext to worry about, so given
+     * above access trade-off, we opt for the additional safety.
+     *
+     * TODO: all we need is safe publish semantics here, we can most probably tolerate concurrent value loads -- and
+     *       everything except the the above volatiles seems to be ready. Those volatile should disappear once we have
+     *       constant class loader visibility (and thus can discover all valid augmentations and aliases). That would
+     *       mean dropping @Holding from createInstance().
+     */
+    @SuppressWarnings("unused")
+    private volatile DataContainerCodecContext<?, T> instance;
 
     @SuppressWarnings("unchecked")
     private DataContainerCodecPrototype(final Class<?> cls, final PathArgument arg, final T nodeSchema,
@@ -83,7 +114,7 @@ final class DataContainerCodecPrototype<T extends WithStatus> implements NodeCon
                     } else {
                         haveAddressable = true;
                     }
-                } else if (child instanceof AnyDataSchemaNode || child instanceof AnyXmlSchemaNode
+                } else if (child instanceof AnydataSchemaNode || child instanceof AnyxmlSchemaNode
                         || child instanceof TypedDataSchemaNode) {
                     haveUnaddressable = true;
                 } else if (child instanceof ChoiceSchemaNode) {
@@ -115,7 +146,7 @@ final class DataContainerCodecPrototype<T extends WithStatus> implements NodeCon
         } else if (nodeSchema instanceof ChoiceSchemaNode) {
             boolean haveAddressable = false;
             boolean haveUnaddressable = false;
-            for (CaseSchemaNode child : ((ChoiceSchemaNode) nodeSchema).getCases().values()) {
+            for (CaseSchemaNode child : ((ChoiceSchemaNode) nodeSchema).getCases()) {
                 switch (computeChildAddressabilitySummary(child)) {
                     case ADDRESSABLE:
                         haveAddressable = true;
@@ -202,30 +233,36 @@ final class DataContainerCodecPrototype<T extends WithStatus> implements NodeCon
     }
 
     @Override
-    public DataContainerCodecContext<?,T> get() {
-        DataContainerCodecContext<?,T> tmp = instance;
+    public DataContainerCodecContext<?, T> get() {
+        final DataContainerCodecContext<?, T> existing = getInstance();
+        return existing != null ? existing : loadInstance();
+    }
+
+    private synchronized @NonNull DataContainerCodecContext<?, T> loadInstance() {
+        // re-acquire under lock
+        DataContainerCodecContext<?, T> tmp = getInstance();
         if (tmp == null) {
-            synchronized (this) {
-                tmp = instance;
-                if (tmp == null) {
-                    tmp = createInstance();
-                    instance = tmp;
-                }
-            }
+            tmp = createInstance();
+            INSTANCE.setRelease(this, tmp);
         }
-
         return tmp;
     }
 
-    @GuardedBy("this")
+    // Helper for getAcquire access to instance
+    private DataContainerCodecContext<?, T> getInstance() {
+        return (DataContainerCodecContext<?, T>) INSTANCE.getAcquire(this);
+    }
+
+    @Holding("this")
     @SuppressWarnings({ "rawtypes", "unchecked" })
-    private DataContainerCodecContext<?,T> createInstance() {
+    private @NonNull DataContainerCodecContext<?, T> createInstance() {
         // FIXME: make protected abstract
         if (schema instanceof ContainerSchemaNode) {
             return new ContainerNodeCodecContext(this);
         } else if (schema instanceof ListSchemaNode) {
-            return Identifiable.class.isAssignableFrom(getBindingClass()) ? new KeyedListNodeCodecContext(this)
-                    : new ListNodeCodecContext(this);
+            return Identifiable.class.isAssignableFrom(getBindingClass())
+                    ? KeyedListNodeCodecContext.create((DataContainerCodecPrototype<ListSchemaNode>) this)
+                            : new ListNodeCodecContext(this);
         } else if (schema instanceof ChoiceSchemaNode) {
             return new ChoiceNodeCodecContext(this);
         } else if (schema instanceof AugmentationSchemaNode) {