Relax AbstractDataObjectModification.childNodesCache
[mdsal.git] / binding / mdsal-binding-dom-adapter / src / main / java / org / opendaylight / mdsal / binding / dom / adapter / AbstractDataObjectModification.java
index 9346e9a617c80011d37403ddd53e06e075bd38b4..023ed75e5dbc7eaec540b50094ae2465bc4dc5d5 100644 (file)
@@ -61,6 +61,7 @@ abstract sealed class AbstractDataObjectModification<T extends DataObject, N ext
         permits LazyAugmentationModification, LazyDataObjectModification {
     private static final Logger LOG = LoggerFactory.getLogger(AbstractDataObjectModification.class);
     private static final VarHandle MODIFICATION_TYPE;
+    private static final VarHandle MODIFIED_CHILDREN;
 
     static {
         final var lookup = MethodHandles.lookup();
@@ -68,6 +69,8 @@ abstract sealed class AbstractDataObjectModification<T extends DataObject, N ext
         try {
             MODIFICATION_TYPE = lookup.findVarHandle(AbstractDataObjectModification.class, "modificationType",
                 ModificationType.class);
+            MODIFIED_CHILDREN = lookup.findVarHandle(AbstractDataObjectModification.class, "modifiedChildren",
+                ImmutableList.class);
         } catch (NoSuchFieldException | IllegalAccessException e) {
             throw new ExceptionInInitializerError(e);
         }
@@ -77,7 +80,8 @@ abstract sealed class AbstractDataObjectModification<T extends DataObject, N ext
     final @NonNull PathArgument identifier;
     final @NonNull N codec;
 
-    private volatile ImmutableList<AbstractDataObjectModification<?, ?>> childNodesCache;
+    @SuppressWarnings("unused")
+    private volatile ImmutableList<AbstractDataObjectModification<?, ?>> modifiedChildren;
     @SuppressWarnings("unused")
     private volatile ModificationType modificationType;
     private volatile @Nullable T dataBefore;
@@ -178,11 +182,8 @@ abstract sealed class AbstractDataObjectModification<T extends DataObject, N ext
 
     @Override
     public final ImmutableList<AbstractDataObjectModification<?, ?>> getModifiedChildren() {
-        var local = childNodesCache;
-        if (local == null) {
-            childNodesCache = local = createModifiedChilden(codec, domData, domChildNodes());
-        }
-        return local;
+        final var local = (ImmutableList<AbstractDataObjectModification<?, ?>>) MODIFIED_CHILDREN.getAcquire(this);
+        return local != null ? local : loadModifiedChilden();
     }
 
     @Override
@@ -199,6 +200,16 @@ abstract sealed class AbstractDataObjectModification<T extends DataObject, N ext
             .collect(Collectors.toList());
     }
 
+    @SuppressWarnings("unchecked")
+    private @NonNull ImmutableList<AbstractDataObjectModification<?, ?>> loadModifiedChilden() {
+        final var builder = ImmutableList.<AbstractDataObjectModification<?, ?>>builder();
+        populateList(builder, codec, domData, domChildNodes());
+        final var computed = builder.build();
+        // Non-trivial return: use CAS to ensure we reuse concurrent loads
+        final var witness = MODIFIED_CHILDREN.compareAndExchangeRelease(this, null, computed);
+        return witness == null ? computed : (ImmutableList<AbstractDataObjectModification<?, ?>>) witness;
+    }
+
     @SuppressWarnings("unchecked")
     private <C extends DataObject> Stream<LazyDataObjectModification<C>> streamModifiedChildren(
             final Class<C> childType) {
@@ -282,14 +293,6 @@ abstract sealed class AbstractDataObjectModification<T extends DataObject, N ext
         };
     }
 
-    private static @NonNull ImmutableList<AbstractDataObjectModification<?, ?>> createModifiedChilden(
-            final CommonDataObjectCodecTreeNode<?> parentCodec, final DataTreeCandidateNode parent,
-            final Collection<DataTreeCandidateNode> children) {
-        final var result = ImmutableList.<AbstractDataObjectModification<?, ?>>builder();
-        populateList(result, parentCodec, parent, children);
-        return result.build();
-    }
-
     private static void populateList(final ImmutableList.Builder<AbstractDataObjectModification<?, ?>> result,
             final CommonDataObjectCodecTreeNode<?> parentCodec, final DataTreeCandidateNode parent,
             final Collection<DataTreeCandidateNode> children) {