import com.google.common.collect.Iterables;
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.model.api.AugmentationSchemaNode;
import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerLike;
import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
import org.opendaylight.yangtools.yang.model.api.DocumentedNode.WithStatus;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
private final PathArgument yangArg;
private final ChildAddressabilitySummary childAddressabilitySummary;
- /*
- * 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().
- */
+ // Accessed via INSTANCE
@SuppressWarnings("unused")
private volatile DataContainerCodecContext<?, T> instance;
return ChildAddressabilitySummary.UNADDRESSABLE;
}
- static DataContainerCodecPrototype<SchemaContext> rootPrototype(final CodecContextFactory factory) {
- final SchemaContext schema = factory.getRuntimeContext().getEffectiveModelContext();
- final NodeIdentifier arg = NodeIdentifier.create(schema.getQName());
+ static DataContainerCodecPrototype<EffectiveModelContext> rootPrototype(final CodecContextFactory factory) {
+ final EffectiveModelContext schema = factory.getRuntimeContext().getEffectiveModelContext();
+ final NodeIdentifier arg = NodeIdentifier.create(SchemaContext.NAME);
return new DataContainerCodecPrototype<>(DataRoot.class, arg, schema, factory);
}
- @SuppressWarnings({ "unchecked", "rawtypes" })
static <T extends DataSchemaNode> DataContainerCodecPrototype<T> from(final Class<?> cls, final T schema,
final CodecContextFactory factory) {
- return new DataContainerCodecPrototype(cls, NodeIdentifier.create(schema.getQName()), schema, factory);
+ return new DataContainerCodecPrototype<>(cls, NodeIdentifier.create(schema.getQName()), schema, factory);
}
- @SuppressWarnings({ "unchecked", "rawtypes" })
static <T extends DataSchemaNode> DataContainerCodecPrototype<T> from(final Item<?> bindingArg, final T schema,
final CodecContextFactory factory) {
- return new DataContainerCodecPrototype(bindingArg, NodeIdentifier.create(schema.getQName()), schema, factory);
+ return new DataContainerCodecPrototype<>(bindingArg, NodeIdentifier.create(schema.getQName()), schema, factory);
}
- @SuppressWarnings({ "rawtypes", "unchecked" })
- static DataContainerCodecPrototype<?> from(final Class<?> augClass, final AugmentationIdentifier arg,
- final AugmentationSchemaNode schema, final CodecContextFactory factory) {
- return new DataContainerCodecPrototype(augClass, arg, schema, factory);
+ static DataContainerCodecPrototype<AugmentationSchemaNode> from(final Class<?> augClass,
+ final AugmentationIdentifier arg, final AugmentationSchemaNode schema, final CodecContextFactory factory) {
+ return new DataContainerCodecPrototype<>(augClass, arg, schema, factory);
}
static DataContainerCodecPrototype<NotificationDefinition> from(final Class<?> augClass,
return new DataContainerCodecPrototype<>(augClass,arg, schema, factory);
}
- protected T getSchema() {
+ T getSchema() {
return schema;
}
return childAddressabilitySummary;
}
- protected QNameModule getNamespace() {
+ QNameModule getNamespace() {
return namespace;
}
- protected CodecContextFactory getFactory() {
+ CodecContextFactory getFactory() {
return factory;
}
- protected Class<?> getBindingClass() {
+ Class<?> getBindingClass() {
return bindingArg.getType();
}
- protected Item<?> getBindingArg() {
+ Item<?> getBindingArg() {
return bindingArg;
}
- protected PathArgument getYangArg() {
+ PathArgument getYangArg() {
return yangArg;
}
@Override
public DataContainerCodecContext<?, T> get() {
- final DataContainerCodecContext<?, T> existing = getInstance();
+ final DataContainerCodecContext<?, T> existing = (DataContainerCodecContext<?, T>) INSTANCE.getAcquire(this);
return existing != null ? existing : loadInstance();
}
- private synchronized @NonNull DataContainerCodecContext<?, T> loadInstance() {
- // re-acquire under lock
- DataContainerCodecContext<?, T> tmp = getInstance();
- if (tmp == null) {
- tmp = createInstance();
- INSTANCE.setRelease(this, tmp);
- }
- return tmp;
- }
-
- // Helper for getAcquire access to instance
- private DataContainerCodecContext<?, T> getInstance() {
- return (DataContainerCodecContext<?, T>) INSTANCE.getAcquire(this);
+ private @NonNull DataContainerCodecContext<?, T> loadInstance() {
+ final var tmp = createInstance();
+ final var witness = (DataContainerCodecContext<?, T>) INSTANCE.compareAndExchangeRelease(this, null, tmp);
+ return witness == null ? tmp : witness;
}
- @Holding("this")
@SuppressWarnings({ "rawtypes", "unchecked" })
+ // This method must allow concurrent loading, i.e. nothing in it may have effects outside of the loaded object
private @NonNull DataContainerCodecContext<?, T> createInstance() {
// FIXME: make protected abstract
- if (schema instanceof ContainerSchemaNode) {
+ if (schema instanceof ContainerLike) {
return new ContainerNodeCodecContext(this);
} else if (schema instanceof ListSchemaNode) {
return Identifiable.class.isAssignableFrom(getBindingClass())
throw new IllegalArgumentException("Unsupported type " + getBindingClass() + " " + schema);
}
+ // FIXME: eliminate with above createInstance() item
boolean isChoice() {
return schema instanceof ChoiceSchemaNode;
}