X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=binding%2Fmdsal-binding-dom-codec%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fmdsal%2Fbinding%2Fdom%2Fcodec%2Fimpl%2FCodecDataObject.java;h=a32e7ea3094eb8ad498349f388231385df1f1729;hb=74ef67db283874e6024413355267120c77a6095c;hp=b48bde535a3943eaa37bfb3e92921ee72f902477;hpb=f4008494701891f9231562984554f6ec2358824c;p=mdsal.git diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CodecDataObject.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CodecDataObject.java index b48bde535a..a32e7ea309 100644 --- a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CodecDataObject.java +++ b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CodecDataObject.java @@ -7,19 +7,17 @@ */ package org.opendaylight.mdsal.binding.dom.codec.impl; -import static com.google.common.base.Verify.verify; import static java.util.Objects.requireNonNull; -import com.google.common.base.MoreObjects; -import com.google.common.base.MoreObjects.ToStringHelper; -import java.util.Optional; -import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; +import com.google.common.base.VerifyException; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode; import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; -import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; -import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer; /** * A base class for {@link DataObject}s backed by {@link DataObjectCodecContext}. While this class is public, it not @@ -29,116 +27,125 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer; * @param DataObject type */ public abstract class CodecDataObject implements DataObject { + // An object representing a null value in a member field. private static final @NonNull Object NULL_VALUE = new Object(); - @SuppressWarnings("rawtypes") - private final @NonNull NormalizedNodeContainer data; + private static final VarHandle CACHED_HASH_CODE; - private volatile Integer cachedHashcode = null; + static { + try { + CACHED_HASH_CODE = MethodHandles.lookup().findVarHandle(CodecDataObject.class, "cachedHashcode", + Integer.class); + } catch (NoSuchFieldException | IllegalAccessException e) { + throw new ExceptionInInitializerError(e); + } + } + + private final @NonNull AbstractDataObjectCodecContext context; + private final @NonNull DataContainerNode data; - protected CodecDataObject(final DataObjectCodecContext context, final NormalizedNodeContainer data) { + // Accessed via a VarHandle + // FIXME: consider using a primitive int-based cache (with 0 being uninit) + @SuppressWarnings("unused") + @SuppressFBWarnings(value = "UUF_UNUSED_FIELD", justification = "https://github.com/spotbugs/spotbugs/issues/2749") + private volatile Integer cachedHashcode; + + protected CodecDataObject(final AbstractDataObjectCodecContext context, final DataContainerNode data) { this.data = requireNonNull(data, "Data must not be null"); + this.context = requireNonNull(context, "Context must not be null"); } @Override public final int hashCode() { - final Integer cached = cachedHashcode; - if (cached != null) { - return cached; - } - - final int result = codecAugmentedHashCode(); - cachedHashcode = result; - return result; + final var cached = (Integer) CACHED_HASH_CODE.getAcquire(this); + return cached != null ? cached : loadHashCode(); } @Override public final boolean equals(final Object obj) { - if (obj == this) { - return true; - } - final Class iface = implementedInterface(); - if (!iface.isInstance(obj)) { - return false; - } - @SuppressWarnings("unchecked") - final T other = (T) iface.cast(obj); - if (other instanceof CodecDataObject) { - return data.equals(((CodecDataObject) obj).data); - } - return codecAugmentedEquals(other); + // Indirection to keep checkstyle happy + return codecEquals(obj); } @Override - public final String toString() { - return codecAugmentedFillToString(MoreObjects.toStringHelper(implementedInterface()).omitNullValues()) - .toString(); - } + public abstract String toString(); - // TODO: consider switching to VarHandles for Java 9+, as that would disconnect us from the need to use a volatile - // field and use acquire/release mechanics -- see http://gee.cs.oswego.edu/dl/html/j9mm.html for details. - protected final Object codecMember(final AtomicReferenceFieldUpdater, Object> updater, - final NodeContextSupplier supplier) { - final Object cached = updater.get(this); - return cached != null ? unmaskNull(cached) : loadMember(updater, supplier); + protected final Object codecMember(final VarHandle handle, final String localName) { + final Object cached = handle.getAcquire(this); + return cached != null ? unmaskNull(cached) : loadMember(handle, context.getLeafChild(localName)); } - protected final Object codecMember(final AtomicReferenceFieldUpdater, Object> updater, - final IdentifiableItemCodec codec) { - final Object cached = updater.get(this); - return cached != null ? unmaskNull(cached) : loadKey(updater, codec); + protected final Object codecMember(final VarHandle handle, final Class bindingClass) { + final Object cached = handle.getAcquire(this); + return cached != null ? unmaskNull(cached) : loadMember(handle, context.getStreamChild(bindingClass)); } - protected abstract int codecHashCode(); - - protected abstract boolean codecEquals(T other); - - protected abstract ToStringHelper codecFillToString(ToStringHelper helper); + protected final Object codecMember(final VarHandle handle, final CodecContextSupplier supplier) { + final Object cached = handle.getAcquire(this); + return cached != null ? unmaskNull(cached) : loadMember(handle, supplier.getCodecContext()); + } - @SuppressWarnings("rawtypes") - final @NonNull NormalizedNodeContainer codecData() { - return data; + protected final @NonNull Object codecMemberOrEmpty(final @Nullable Object value, + final @NonNull Class bindingClass) { + return value != null ? value : emptyObject(bindingClass); } - // Non-final to allow specialization in AugmentableCodecDataObject - int codecAugmentedHashCode() { - return codecHashCode(); + private @NonNull Object emptyObject(final @NonNull Class bindingClass) { + final var childContext = context.getStreamChild(bindingClass); + if (childContext instanceof StructuralContainerCodecContext structural) { + return structural.emptyObject(); + } + throw new VerifyException("Unexpected context " + childContext); } - // Non-final to allow specialization in AugmentableCodecDataObject - boolean codecAugmentedEquals(final T other) { - return codecEquals(other); + protected final @NonNull Object codecKey(final VarHandle handle) { + final Object cached = handle.getAcquire(this); + return cached != null ? cached : loadKey(handle); } - // Non-final to allow specialization in AugmentableCodecDataObject - ToStringHelper codecAugmentedFillToString(final ToStringHelper helper) { - return codecFillToString(helper); + protected abstract int codecHashCode(); + + protected abstract boolean codecEquals(Object obj); + + final @NonNull AbstractDataObjectCodecContext codecContext() { + return context; } - // Helpers split out of codecMember to aid its inlining - private Object loadMember(final AtomicReferenceFieldUpdater, Object> updater, - final NodeContextSupplier supplier) { - final NodeCodecContext context = supplier.get(); + final @NonNull DataContainerNode codecData() { + return data; + } - @SuppressWarnings("unchecked") - final Optional> child = data.getChild(context.getDomPathArgument()); + // Helper split out of codecMember to aid its inlining + private Object loadMember(final VarHandle handle, final CodecContext childCtx) { + final var child = data.childByArg(childCtx.getDomPathArgument()); // We do not want to use Optional.map() here because we do not want to invoke defaultObject() when we have // normal value because defaultObject() may end up throwing an exception intentionally. - return updateCache(updater, child.isPresent() ? context.deserializeObject(child.get()) - : context.defaultObject()); + final Object obj = child != null ? childCtx.deserializeObject(child) : childCtx.defaultObject(); + final Object witness = handle.compareAndExchangeRelease(this, null, maskNull(obj)); + return witness == null ? obj : unmaskNull(witness); } - // Helpers split out of codecMember to aid its inlining - private Object loadKey(final AtomicReferenceFieldUpdater, Object> updater, - final IdentifiableItemCodec codec) { - verify(data instanceof MapEntryNode, "Unsupported value %s", data); - return updateCache(updater, codec.deserialize(((MapEntryNode) data).getIdentifier()).getKey()); + // Helper split out of codecKey to aid its inlining + private @NonNull Object loadKey(final VarHandle handle) { + if (!(data instanceof MapEntryNode mapEntry)) { + throw new VerifyException("Unsupported value " + data); + } + if (!(context instanceof MapCodecContext listContext)) { + throw new VerifyException("Unexpected context " + context); + } + + final Object obj = listContext.deserialize(mapEntry.name()); + // key is known to be non-null, no need to mask it + final Object witness = handle.compareAndExchangeRelease(this, null, obj); + return witness == null ? obj : witness; } - private Object updateCache(final AtomicReferenceFieldUpdater, Object> updater, - final Object obj) { - return updater.compareAndSet(this, null, maskNull(obj)) ? obj : unmaskNull(updater.get(this)); + // Helper split out of hashCode() to aid its inlining + private int loadHashCode() { + final int result = codecHashCode(); + final Object witness = CACHED_HASH_CODE.compareAndExchangeRelease(this, null, result); + return witness == null ? result : (Integer) witness; } private static @NonNull Object maskNull(final @Nullable Object unmasked) {