Switch to using StampedLock
[mdsal.git] / dom / mdsal-dom-spi / src / main / java / org / opendaylight / mdsal / dom / spi / AbstractRegistrationTree.java
1 /**
2  * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.mdsal.dom.spi;
9
10 import java.util.concurrent.locks.Lock;
11 import java.util.concurrent.locks.StampedLock;
12 import javax.annotation.Nonnull;
13 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
14
15 /**
16  * An abstract tree of registrations. Allows a read-only snapshot to be taken.
17  *
18  * @param <T> Type of registered object
19  */
20 public abstract class AbstractRegistrationTree<T> {
21     private final RegistrationTreeNode<T> rootNode = new RegistrationTreeNode<>(null, null);
22     private final Lock writeLock;
23     private final Lock readLock;
24
25     protected AbstractRegistrationTree() {
26         final StampedLock lock = new StampedLock();
27         readLock = lock.asReadLock();
28         writeLock = lock.asWriteLock();
29     }
30
31     /**
32      * Acquire the read-write lock. This should be done before invoking {@link #findNodeFor(Iterable)}. This method
33      * must not be called when the lock is already held by this thread.
34      */
35     protected final void takeLock() {
36         writeLock.lock();
37     }
38
39     /**
40      * Release the read-write lock. This should be done after invocation of {@link #findNodeFor(Iterable)}
41      * and modification of the returned node. Note that callers should do so in a finally block.
42      */
43     protected final void releaseLock() {
44         writeLock.unlock();
45     }
46
47     /**
48      * Find an existing, or allocate a fresh, node for a particular path. Must be called with the lock held.
49      *
50      * @param path Path to find a node for
51      * @return A registration node for the specified path
52      */
53     @Nonnull protected final RegistrationTreeNode<T> findNodeFor(@Nonnull final Iterable<PathArgument> path) {
54         RegistrationTreeNode<T> walkNode = rootNode;
55         for (final PathArgument arg : path) {
56             walkNode = walkNode.ensureChild(arg);
57         }
58
59         return walkNode;
60     }
61
62     /**
63      * Add a registration to a particular node. The node must have been returned via {@link #findNodeFor(Iterable)}
64      * and the lock must still be held.
65      *
66      * @param node Tree node
67      * @param registration Registration instance
68      */
69     protected final void addRegistration(@Nonnull final RegistrationTreeNode<T> node, @Nonnull final T registration) {
70         node.addRegistration(registration);
71     }
72
73     /**
74      * Remove a registration from a particular node. This method must not be called while the lock is held.
75      *
76      * @param node Tree node
77      * @param registration Registration instance
78      */
79     protected final void removeRegistration(@Nonnull final RegistrationTreeNode<T> node, @Nonnull final T registration) {
80         // Take the write lock
81         writeLock.lock();
82         try {
83             node.removeRegistration(registration);
84         } finally {
85             // Always release the lock
86             writeLock.unlock();
87         }
88     }
89
90     /**
91      * Obtain a tree snapshot. This snapshot ensures a consistent view of
92      * registrations. The snapshot should be closed as soon as it is not required,
93      * because each unclosed instance blocks modification of this tree.
94      *
95      * @return A snapshot instance.
96      */
97     @Nonnull public final RegistrationTreeSnapshot<T> takeSnapshot() {
98         readLock.lock();
99         return new RegistrationTreeSnapshot<>(readLock, rootNode);
100     }
101 }