X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=blobdiff_plain;f=opendaylight%2Fmd-sal%2Fsal-dom-broker%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fcontroller%2Fmd%2Fsal%2Fdom%2Fstore%2Fimpl%2Ftree%2Fdata%2FInMemoryDataTreeModification.java;h=39ff4f0aa03f9b0ad1d3ccdce080b4cf7c7989a2;hp=df3ef8b7e128b2be29eb5473bd86d056cba218d2;hb=48814d6a264b8f13e5db1422336d9ef25cb05fa9;hpb=01ae144d488baf278143db152f78024ad4d524f3 diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/data/InMemoryDataTreeModification.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/data/InMemoryDataTreeModification.java index df3ef8b7e1..39ff4f0aa0 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/data/InMemoryDataTreeModification.java +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/data/InMemoryDataTreeModification.java @@ -7,14 +7,14 @@ */ package org.opendaylight.controller.md.sal.dom.store.impl.tree.data; -import static com.google.common.base.Preconditions.checkState; - import java.util.Map.Entry; -import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; + +import javax.annotation.concurrent.GuardedBy; import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataTreeModification; -import org.opendaylight.controller.md.sal.dom.store.impl.tree.StoreUtils; +import org.opendaylight.controller.md.sal.dom.store.impl.tree.ModificationType; import org.opendaylight.controller.md.sal.dom.store.impl.tree.TreeNodeUtils; +import org.opendaylight.controller.md.sal.dom.store.impl.tree.spi.TreeNode; import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; @@ -28,26 +28,20 @@ import com.google.common.base.Preconditions; final class InMemoryDataTreeModification implements DataTreeModification { private static final Logger LOG = LoggerFactory.getLogger(InMemoryDataTreeModification.class); - - /* - * FIXME: the thread safety of concurrent write/delete/read/seal operations - * needs to be evaluated. - */ - private static final AtomicIntegerFieldUpdater SEALED_UPDATER = - AtomicIntegerFieldUpdater.newUpdater(InMemoryDataTreeModification.class, "sealed"); - private volatile int sealed = 0; - private final ModificationApplyOperation strategyTree; private final InMemoryDataTreeSnapshot snapshot; - private final NodeModification rootNode; + private final ModifiedNode rootNode; + + @GuardedBy("this") + private boolean sealed = false; InMemoryDataTreeModification(final InMemoryDataTreeSnapshot snapshot, final ModificationApplyOperation resolver) { this.snapshot = Preconditions.checkNotNull(snapshot); this.strategyTree = Preconditions.checkNotNull(resolver); - this.rootNode = NodeModification.createUnmodified(snapshot.getRootNode()); + this.rootNode = ModifiedNode.createUnmodified(snapshot.getRootNode()); } - NodeModification getRootModification() { + ModifiedNode getRootModification() { return rootNode; } @@ -56,13 +50,13 @@ final class InMemoryDataTreeModification implements DataTreeModification { } @Override - public void write(final InstanceIdentifier path, final NormalizedNode value) { + public synchronized void write(final InstanceIdentifier path, final NormalizedNode value) { checkSealed(); resolveModificationFor(path).write(value); } @Override - public void merge(final InstanceIdentifier path, final NormalizedNode data) { + public synchronized void merge(final InstanceIdentifier path, final NormalizedNode data) { checkSealed(); mergeImpl(resolveModificationFor(path),data); } @@ -81,39 +75,41 @@ final class InMemoryDataTreeModification implements DataTreeModification { } @Override - public void delete(final InstanceIdentifier path) { + public synchronized void delete(final InstanceIdentifier path) { checkSealed(); resolveModificationFor(path).delete(); } @Override - public Optional> readNode(final InstanceIdentifier path) { - Entry modification = TreeNodeUtils.findClosestsOrFirstMatch(rootNode, path, NodeModification.IS_TERMINAL_PREDICATE); - - Optional result = resolveSnapshot(modification); + public synchronized Optional> readNode(final InstanceIdentifier path) { + /* + * Walk the tree from the top, looking for the first node between root and + * the requested path which has been modified. If no such node exists, + * we use the node itself. + */ + final Entry entry = TreeNodeUtils.findClosestsOrFirstMatch(rootNode, path, ModifiedNode.IS_TERMINAL_PREDICATE); + final InstanceIdentifier key = entry.getKey(); + final ModifiedNode mod = entry.getValue(); + + final Optional result = resolveSnapshot(key, mod); if (result.isPresent()) { NormalizedNode data = result.get().getData(); - return NormalizedNodeUtils.findNode(modification.getKey(), data, path); + return NormalizedNodeUtils.findNode(key, data, path); + } else { + return Optional.absent(); } - return Optional.absent(); } - private Optional resolveSnapshot( - final Entry keyModification) { - InstanceIdentifier path = keyModification.getKey(); - NodeModification modification = keyModification.getValue(); - return resolveSnapshot(path, modification); - } + private Optional resolveSnapshot(final InstanceIdentifier path, + final ModifiedNode modification) { + final Optional> potentialSnapshot = modification.getSnapshotCache(); + if(potentialSnapshot.isPresent()) { + return potentialSnapshot.get(); + } - private Optional resolveSnapshot(final InstanceIdentifier path, - final NodeModification modification) { try { - Optional> potentialSnapshot = modification.getSnapshotCache(); - if(potentialSnapshot.isPresent()) { - return potentialSnapshot.get(); - } return resolveModificationStrategy(path).apply(modification, modification.getOriginal(), - StoreUtils.increase(snapshot.getRootNode().getSubtreeVersion())); + snapshot.getRootNode().getSubtreeVersion().next()); } catch (Exception e) { LOG.error("Could not create snapshot for {}:{}", path,modification,e); throw e; @@ -126,7 +122,7 @@ final class InMemoryDataTreeModification implements DataTreeModification { } private OperationWithModification resolveModificationFor(final InstanceIdentifier path) { - NodeModification modification = rootNode; + ModifiedNode modification = rootNode; // We ensure strategy is present. ModificationApplyOperation operation = resolveModificationStrategy(path); for (PathArgument pathArg : path.getPath()) { @@ -136,14 +132,15 @@ final class InMemoryDataTreeModification implements DataTreeModification { } @Override - public void seal() { - final boolean success = SEALED_UPDATER.compareAndSet(this, 0, 1); - Preconditions.checkState(success, "Attempted to seal an already-sealed Data Tree."); + public synchronized void ready() { + Preconditions.checkState(!sealed, "Attempted to seal an already-sealed Data Tree."); + sealed = true; rootNode.seal(); } + @GuardedBy("this") private void checkSealed() { - checkState(sealed == 0, "Data Tree is sealed. No further modifications allowed."); + Preconditions.checkState(!sealed, "Data Tree is sealed. No further modifications allowed."); } @Override @@ -152,8 +149,30 @@ final class InMemoryDataTreeModification implements DataTreeModification { } @Override - public DataTreeModification newModification() { - // FIXME: transaction chaining - throw new UnsupportedOperationException("Implement this as part of transaction chaining"); + public synchronized DataTreeModification newModification() { + Preconditions.checkState(sealed, "Attempted to chain on an unsealed modification"); + + if(rootNode.getType() == ModificationType.UNMODIFIED) { + return snapshot.newModification(); + } + + /* + * FIXME: Add advanced transaction chaining for modification of not rebased + * modification. + * + * Current computation of tempRoot may yeld incorrect subtree versions + * if there are multiple concurrent transactions, which may break + * versioning preconditions for modification of previously occured write, + * directly nested under parent node, since node version is derived from + * subtree version. + * + * For deeper nodes subtree version is derived from their respective metadata + * nodes, so this incorrect root subtree version is not affecting us. + */ + TreeNode originalSnapshotRoot = snapshot.getRootNode(); + Optional tempRoot = strategyTree.apply(rootNode, Optional.of(originalSnapshotRoot), originalSnapshotRoot.getSubtreeVersion().next()); + + InMemoryDataTreeSnapshot tempTree = new InMemoryDataTreeSnapshot(snapshot.getSchemaContext(), tempRoot.get(), strategyTree); + return tempTree.newModification(); } }