Compute unknown nodes lazily 60/85960/15
authorRobert Varga <robert.varga@pantheon.tech>
Mon, 25 Nov 2019 13:26:29 +0000 (14:26 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Wed, 27 Nov 2019 00:51:35 +0000 (01:51 +0100)
We usually do not access unknown nodes (which are mostly empty
anyway). This patch moves their computation to first access.

JIRA: YANGTOOLS-1041
Change-Id: I85b286b4ce8477c2999aeeb34ee76ed32e597cef
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/AbstractEffectiveDocumentedNode.java

index 6e9608032ffe36e4696c33e2545ac5f0c36b3bd9..ae10073206be71879b2e085b44e4ad19a90e59f0 100644 (file)
@@ -8,6 +8,8 @@
 package org.opendaylight.yangtools.yang.parser.rfc7950.stmt;
 
 import com.google.common.collect.ImmutableList;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
 import java.util.ArrayList;
 import java.util.List;
 import org.eclipse.jdt.annotation.NonNull;
@@ -22,9 +24,22 @@ import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
 // FIXME: 5.0.0: rename to AbstractEffectiveDocumentedNodeWithStatus
 public abstract class AbstractEffectiveDocumentedNode<A, D extends DeclaredStatement<A>>
         extends AbstractEffectiveDocumentedNodeWithoutStatus<A, D> implements DocumentedNode.WithStatus {
-    private final @NonNull ImmutableList<UnknownSchemaNode> unknownNodes;
+    private static final VarHandle UNKNOWN_NODES;
+
+    static {
+        try {
+            UNKNOWN_NODES = MethodHandles.lookup().findVarHandle(AbstractEffectiveDocumentedNode.class,
+                "unknownNodes", ImmutableList.class);
+        } catch (NoSuchFieldException | IllegalAccessException e) {
+            throw new ExceptionInInitializerError(e);
+        }
+    }
+
     private final @NonNull Status status;
 
+    @SuppressWarnings("unused")
+    private volatile ImmutableList<UnknownSchemaNode> unknownNodes;
+
     /**
      * Constructor.
      *
@@ -34,14 +49,6 @@ public abstract class AbstractEffectiveDocumentedNode<A, D extends DeclaredState
     protected AbstractEffectiveDocumentedNode(final StmtContext<A, D, ?> ctx) {
         super(ctx);
         status = findFirstEffectiveSubstatementArgument(StatusEffectiveStatement.class).orElse(Status.CURRENT);
-
-        final List<UnknownSchemaNode> unknownNodesInit = new ArrayList<>();
-        for (final EffectiveStatement<?, ?> stmt : effectiveSubstatements()) {
-            if (stmt instanceof UnknownSchemaNode) {
-                unknownNodesInit.add((UnknownSchemaNode) stmt);
-            }
-        }
-        unknownNodes = ImmutableList.copyOf(unknownNodesInit);
     }
 
     @Override
@@ -51,6 +58,22 @@ public abstract class AbstractEffectiveDocumentedNode<A, D extends DeclaredState
 
     @Override
     public final ImmutableList<UnknownSchemaNode> getUnknownSchemaNodes() {
-        return unknownNodes;
+        final ImmutableList<UnknownSchemaNode> existing =
+                (ImmutableList<UnknownSchemaNode>) UNKNOWN_NODES.getAcquire(this);
+        return existing != null ? existing : loadUnknownSchemaNodes();
+    }
+
+    @SuppressWarnings("unchecked")
+    private @NonNull ImmutableList<UnknownSchemaNode> loadUnknownSchemaNodes() {
+        final List<UnknownSchemaNode> init = new ArrayList<>();
+        for (EffectiveStatement<?, ?> stmt : effectiveSubstatements()) {
+            if (stmt instanceof UnknownSchemaNode) {
+                init.add((UnknownSchemaNode) stmt);
+            }
+        }
+
+        final ImmutableList<UnknownSchemaNode> computed = ImmutableList.copyOf(init);
+        final Object witness = UNKNOWN_NODES.compareAndExchangeRelease(this, null, computed);
+        return witness == null ? computed : (ImmutableList<UnknownSchemaNode>) witness;
     }
 }