*/
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;
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<InMemoryDataTreeModification> SEALED_UPDATER =
- AtomicIntegerFieldUpdater.newUpdater(InMemoryDataTreeModification.class, "sealed");
- private volatile int sealed = 0;
-
private final ModificationApplyOperation strategyTree;
private final InMemoryDataTreeSnapshot snapshot;
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);
}
@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);
}
}
@Override
- public void delete(final InstanceIdentifier path) {
+ public synchronized void delete(final InstanceIdentifier path) {
checkSealed();
resolveModificationFor(path).delete();
}
@Override
- public Optional<NormalizedNode<?, ?>> readNode(final InstanceIdentifier path) {
+ public synchronized Optional<NormalizedNode<?, ?>> 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,
try {
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;
}
@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
}
@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<TreeNode> tempRoot = strategyTree.apply(rootNode, Optional.of(originalSnapshotRoot), originalSnapshotRoot.getSubtreeVersion().next());
+
+ InMemoryDataTreeSnapshot tempTree = new InMemoryDataTreeSnapshot(snapshot.getSchemaContext(), tempRoot.get(), strategyTree);
+ return tempTree.newModification();
}
}