- split ListenerTree two static inner classes to ListenerWalker and ListenerNode
- fix refences [ResolvedDataChangeState]
- improve performance of ListenerWalker.close()
Change-Id: Ide82bc6caeaca1aa5c9080f134d570cf7a1571d6
Signed-off-by: Vaclav Demcak <vdemcak@cisco.com>
Signed-off-by: Robert Varga <rovarga@cisco.com>
import org.opendaylight.controller.md.sal.dom.store.impl.DOMImmutableDataChangeEvent.Builder;
import org.opendaylight.controller.md.sal.dom.store.impl.DOMImmutableDataChangeEvent.SimpleEventFactory;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.ListenerTree;
-import org.opendaylight.controller.md.sal.dom.store.impl.tree.ListenerTree.Walker;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.ListenerWalker;
import org.opendaylight.yangtools.util.concurrent.NotificationManager;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
* Resolves and submits notification tasks to the specified manager.
*/
public synchronized void resolve(final NotificationManager<DataChangeListenerRegistration<?>, DOMImmutableDataChangeEvent> manager) {
- try (final Walker w = listenerRoot.getWalker()) {
+ try (final ListenerWalker w = listenerRoot.getWalker()) {
// Defensive: reset internal state
collectedEvents = ArrayListMultimap.create();
*/
package org.opendaylight.controller.md.sal.dom.store.impl;
-import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Multimap;
-
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
import org.opendaylight.controller.md.sal.dom.store.impl.DOMImmutableDataChangeEvent.Builder;
-import org.opendaylight.controller.md.sal.dom.store.impl.tree.ListenerTree.Node;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.ListenerNode;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Multimap;
+
/**
* Recursion state used in {@link ResolveDataChangeEventsTask}. Instances of this
* method track which listeners are affected by a particular change node. It takes
*/
private final Collection<Builder> inheritedOne;
private final YangInstanceIdentifier nodeId;
- private final Collection<Node> nodes;
+ private final Collection<ListenerNode> nodes;
private final Map<DataChangeListenerRegistration<?>, Builder> subBuilders;
private final Map<DataChangeListenerRegistration<?>, Builder> oneBuilders;
private ResolveDataChangeState(final YangInstanceIdentifier nodeId,
final Iterable<Builder> inheritedSub, final Collection<Builder> inheritedOne,
- final Collection<Node> nodes) {
+ final Collection<ListenerNode> nodes) {
this.nodeId = Preconditions.checkNotNull(nodeId);
this.nodes = Preconditions.checkNotNull(nodes);
this.inheritedSub = Preconditions.checkNotNull(inheritedSub);
final Map<DataChangeListenerRegistration<?>, Builder> sub = new HashMap<>();
final Map<DataChangeListenerRegistration<?>, Builder> one = new HashMap<>();
final Map<DataChangeListenerRegistration<?>, Builder> base = new HashMap<>();
- for (Node n : nodes) {
+ for (ListenerNode n : nodes) {
for (DataChangeListenerRegistration<?> l : n.getListeners()) {
final Builder b = DOMImmutableDataChangeEvent.builder(DataChangeScope.BASE);
switch (l.getScope()) {
* @param root root node
* @return
*/
- public static ResolveDataChangeState initial(final YangInstanceIdentifier rootId, final Node root) {
+ public static ResolveDataChangeState initial(final YangInstanceIdentifier rootId, final ListenerNode root) {
return new ResolveDataChangeState(rootId, Collections.<Builder>emptyList(),
Collections.<Builder>emptyList(), Collections.singletonList(root));
}
LOG.trace("Collected events {}", map);
}
- private static Collection<Node> getListenerChildrenWildcarded(final Collection<Node> parentNodes,
+ private static Collection<ListenerNode> getListenerChildrenWildcarded(final Collection<ListenerNode> parentNodes,
final PathArgument child) {
if (parentNodes.isEmpty()) {
return Collections.emptyList();
}
- final List<Node> result = new ArrayList<>();
+ final List<ListenerNode> result = new ArrayList<>();
if (child instanceof NodeWithValue || child instanceof NodeIdentifierWithPredicates) {
NodeIdentifier wildcardedIdentifier = new NodeIdentifier(child.getNodeType());
addChildNodes(result, parentNodes, wildcardedIdentifier);
return result;
}
- private static void addChildNodes(final List<Node> result, final Collection<Node> parentNodes, final PathArgument childIdentifier) {
- for (Node node : parentNodes) {
- Optional<Node> child = node.getChild(childIdentifier);
+ private static void addChildNodes(final List<ListenerNode> result, final Collection<ListenerNode> parentNodes, final PathArgument childIdentifier) {
+ for (ListenerNode node : parentNodes) {
+ Optional<ListenerNode> child = node.getChild(childIdentifier);
if (child.isPresent()) {
result.add(child.get());
}
--- /dev/null
+/**
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl.tree;
+
+import com.google.common.base.Optional;
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import org.opendaylight.controller.md.sal.dom.store.impl.DataChangeListenerRegistration;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.ListenerTree.DataChangeListenerRegistrationImpl;
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.StoreTreeNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This is a single node within the listener tree. Note that the data returned from
+ * and instance of this class is guaranteed to have any relevance or consistency
+ * only as long as the {@link ListenerWalker} instance through which it is reached remains
+ * unclosed.
+ *
+ * @author Robert Varga
+ */
+public class ListenerNode implements StoreTreeNode<ListenerNode>, Identifiable<PathArgument> {
+
+ private static final Logger LOG = LoggerFactory.getLogger(ListenerNode.class);
+
+ private final Collection<DataChangeListenerRegistration<?>> listeners = new ArrayList<>();
+ private final Map<PathArgument, ListenerNode> children = new HashMap<>();
+ private final PathArgument identifier;
+ private final Reference<ListenerNode> parent;
+
+ ListenerNode(final ListenerNode parent, final PathArgument identifier) {
+ this.parent = new WeakReference<>(parent);
+ this.identifier = identifier;
+ }
+
+ @Override
+ public PathArgument getIdentifier() {
+ return identifier;
+ }
+
+ @Override
+ public Optional<ListenerNode> getChild(final PathArgument child) {
+ return Optional.fromNullable(children.get(child));
+ }
+
+ /**
+ * Return the list of current listeners. This collection is guaranteed
+ * to be immutable only while the walker, through which this node is
+ * reachable remains unclosed.
+ *
+ * @return the list of current listeners
+ */
+ public Collection<DataChangeListenerRegistration<?>> getListeners() {
+ return listeners;
+ }
+
+ ListenerNode ensureChild(final PathArgument child) {
+ ListenerNode potential = children.get(child);
+ if (potential == null) {
+ potential = new ListenerNode(this, child);
+ children.put(child, potential);
+ }
+ return potential;
+ }
+
+ void addListener(final DataChangeListenerRegistration<?> listener) {
+ listeners.add(listener);
+ LOG.debug("Listener {} registered", listener);
+ }
+
+ void removeListener(final DataChangeListenerRegistrationImpl<?> listener) {
+ listeners.remove(listener);
+ LOG.debug("Listener {} unregistered", listener);
+
+ // We have been called with the write-lock held, so we can perform some cleanup.
+ removeThisIfUnused();
+ }
+
+ private void removeThisIfUnused() {
+ final ListenerNode p = parent.get();
+ if (p != null && listeners.isEmpty() && children.isEmpty()) {
+ p.removeChild(identifier);
+ }
+ }
+
+ private void removeChild(final PathArgument arg) {
+ children.remove(arg);
+ removeThisIfUnused();
+ }
+
+ @Override
+ public String toString() {
+ return "Node [identifier=" + identifier + ", listeners=" + listeners.size() + ", children=" + children.size() + "]";
+ }
+}
*/
package org.opendaylight.controller.md.sal.dom.store.impl.tree;
-import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
-
-import java.lang.ref.Reference;
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
-import javax.annotation.concurrent.GuardedBy;
-
import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
import org.opendaylight.controller.md.sal.dom.store.impl.DataChangeListenerRegistration;
import org.opendaylight.yangtools.concepts.AbstractListenerRegistration;
-import org.opendaylight.yangtools.concepts.Identifiable;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
-import org.opendaylight.yangtools.yang.data.api.schema.tree.StoreTreeNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A set of listeners organized as a tree by node to which they listen. This class
* allows for efficient lookup of listeners when we walk the DataTreeCandidate.
+ *
+ * @author Robert Varga
*/
public final class ListenerTree {
private static final Logger LOG = LoggerFactory.getLogger(ListenerTree.class);
private final ReadWriteLock rwLock = new ReentrantReadWriteLock(true);
- private final Node rootNode = new Node(null, null);
+ private final ListenerNode rootNode = new ListenerNode(null, null);
private ListenerTree() {
// Private to disallow direct instantiation
rwLock.writeLock().lock();
try {
- Node walkNode = rootNode;
+ ListenerNode walkNode = rootNode;
for (final PathArgument arg : path.getPathArguments()) {
walkNode = walkNode.ensureChild(arg);
}
- final Node node = walkNode;
+ final ListenerNode node = walkNode;
DataChangeListenerRegistration<L> reg = new DataChangeListenerRegistrationImpl<L>(listener) {
@Override
public DataChangeScope getScope() {
*
* @return A walker instance.
*/
- public Walker getWalker() {
+ public ListenerWalker getWalker() {
/*
* TODO: The only current user of this method is local to the datastore.
* Since this class represents a read-lock, losing a reference to
* external user exist, make the Walker a phantom reference, which
* will cleanup the lock if not told to do so.
*/
- final Walker ret = new Walker(rwLock.readLock(), rootNode);
+ final ListenerWalker ret = new ListenerWalker(rwLock.readLock(), rootNode);
rwLock.readLock().lock();
return ret;
}
- /**
- * A walking context, pretty much equivalent to an iterator, but it
- * exposes the underlying tree structure.
- */
- /*
- * FIXME: BUG-1511: split this class out as ListenerWalker.
- */
- public static final class Walker implements AutoCloseable {
- private final Lock lock;
- private final Node node;
-
- @GuardedBy("this")
- private boolean valid = true;
-
- private Walker(final Lock lock, final Node node) {
- this.lock = Preconditions.checkNotNull(lock);
- this.node = Preconditions.checkNotNull(node);
- }
-
- public Node getRootNode() {
- return node;
- }
-
- @Override
- public synchronized void close() {
- if (valid) {
- lock.unlock();
- valid = false;
- }
- }
- }
-
- /**
- * This is a single node within the listener tree. Note that the data returned from
- * and instance of this class is guaranteed to have any relevance or consistency
- * only as long as the {@link org.opendaylight.controller.md.sal.dom.store.impl.tree.ListenerTree.Walker} instance through which it is reached remains
- * unclosed.
- */
- /*
- * FIXME: BUG-1511: split this class out as ListenerNode.
- */
- public static final class Node implements StoreTreeNode<Node>, Identifiable<PathArgument> {
- private final Collection<DataChangeListenerRegistration<?>> listeners = new ArrayList<>();
- private final Map<PathArgument, Node> children = new HashMap<>();
- private final PathArgument identifier;
- private final Reference<Node> parent;
-
- private Node(final Node parent, final PathArgument identifier) {
- this.parent = new WeakReference<>(parent);
- this.identifier = identifier;
- }
-
- @Override
- public PathArgument getIdentifier() {
- return identifier;
- }
-
- @Override
- public Optional<Node> getChild(final PathArgument child) {
- return Optional.fromNullable(children.get(child));
- }
-
- /**
- * Return the list of current listeners. This collection is guaranteed
- * to be immutable only while the walker, through which this node is
- * reachable remains unclosed.
- *
- * @return the list of current listeners
- */
- public Collection<DataChangeListenerRegistration<?>> getListeners() {
- return listeners;
- }
-
- private Node ensureChild(final PathArgument child) {
- Node potential = children.get(child);
- if (potential == null) {
- potential = new Node(this, child);
- children.put(child, potential);
- }
- return potential;
- }
-
- private void addListener(final DataChangeListenerRegistration<?> listener) {
- listeners.add(listener);
- LOG.debug("Listener {} registered", listener);
- }
-
- private void removeListener(final DataChangeListenerRegistrationImpl<?> listener) {
- listeners.remove(listener);
- LOG.debug("Listener {} unregistered", listener);
-
- // We have been called with the write-lock held, so we can perform some cleanup.
- removeThisIfUnused();
- }
-
- private void removeThisIfUnused() {
- final Node p = parent.get();
- if (p != null && listeners.isEmpty() && children.isEmpty()) {
- p.removeChild(identifier);
- }
- }
-
- private void removeChild(final PathArgument arg) {
- children.remove(arg);
- removeThisIfUnused();
- }
-
- @Override
- public String toString() {
- return "Node [identifier=" + identifier + ", listeners=" + listeners.size() + ", children=" + children.size() + "]";
- }
-
-
- }
-
- private abstract static class DataChangeListenerRegistrationImpl<T extends AsyncDataChangeListener<YangInstanceIdentifier, NormalizedNode<?, ?>>> extends AbstractListenerRegistration<T> //
+ abstract static class DataChangeListenerRegistrationImpl<T extends AsyncDataChangeListener<YangInstanceIdentifier, NormalizedNode<?, ?>>> extends AbstractListenerRegistration<T> //
implements DataChangeListenerRegistration<T> {
public DataChangeListenerRegistrationImpl(final T listener) {
super(listener);
--- /dev/null
+/**
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl.tree;
+
+import com.google.common.base.Preconditions;
+import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
+import java.util.concurrent.locks.Lock;
+
+/**
+ * A walking context, pretty much equivalent to an iterator, but it
+ * exposes the underlying tree structure.
+ *
+ * @author Robert Varga
+ */
+public class ListenerWalker implements AutoCloseable {
+ private static final AtomicIntegerFieldUpdater<ListenerWalker> CLOSED_UPDATER = AtomicIntegerFieldUpdater.newUpdater(ListenerWalker.class, "closed");
+ private final Lock lock;
+ private final ListenerNode node;
+
+ // Used via CLOSED_UPDATER
+ @SuppressWarnings("unused")
+ private volatile int closed = 0;
+
+ ListenerWalker(final Lock lock, final ListenerNode node) {
+ this.lock = Preconditions.checkNotNull(lock);
+ this.node = Preconditions.checkNotNull(node);
+ }
+
+ public ListenerNode getRootNode() {
+ return node;
+ }
+
+ @Override
+ public void close() {
+ if (CLOSED_UPDATER.compareAndSet(this, 0, 1)) {
+ lock.unlock();
+ }
+ }
+}
\ No newline at end of file