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;
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;
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;
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,
} 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) {
} 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;
}
@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) {