2 * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
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
9 package org.opendaylight.mdsal.dom.store.inmemory;
11 import com.google.common.base.Preconditions;
12 import com.google.common.collect.ImmutableSet;
13 import java.util.Collection;
14 import java.util.concurrent.atomic.AtomicLong;
15 import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
16 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
17 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
18 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot;
19 import org.slf4j.Logger;
20 import org.slf4j.LoggerFactory;
22 class InMemoryDOMDataTreeShardProducer implements DOMDataTreeShardProducer {
24 private abstract static class State {
26 * Allocate a new snapshot.
28 * @return A new snapshot
30 protected abstract DataTreeSnapshot getSnapshot(Object transactionId);
33 private static final class Idle extends State {
34 private final InMemoryDOMDataTreeShardProducer producer;
36 Idle(final InMemoryDOMDataTreeShardProducer producer) {
37 this.producer = Preconditions.checkNotNull(producer);
41 protected DataTreeSnapshot getSnapshot(final Object transactionId) {
42 return producer.takeSnapshot();
47 * We have a transaction out there.
49 private static final class Allocated extends State {
50 private static final AtomicReferenceFieldUpdater<Allocated, DataTreeSnapshot> SNAPSHOT_UPDATER =
51 AtomicReferenceFieldUpdater.newUpdater(Allocated.class, DataTreeSnapshot.class, "snapshot");
52 private final InmemoryDOMDataTreeShardWriteTransaction transaction;
53 private volatile DataTreeSnapshot snapshot;
55 Allocated(final InmemoryDOMDataTreeShardWriteTransaction transaction) {
56 this.transaction = Preconditions.checkNotNull(transaction);
59 InmemoryDOMDataTreeShardWriteTransaction getTransaction() {
64 protected DataTreeSnapshot getSnapshot(final Object transactionId) {
65 final DataTreeSnapshot ret = snapshot;
66 Preconditions.checkState(ret != null,
67 "Could not get snapshot for transaction %s - previous transaction %s is not ready yet",
68 transactionId, transaction.getIdentifier());
72 void setSnapshot(final DataTreeSnapshot snapshot) {
73 final boolean success = SNAPSHOT_UPDATER.compareAndSet(this, null, snapshot);
74 Preconditions.checkState(success, "Transaction %s has already been marked as ready",
75 transaction.getIdentifier());
80 * Producer is logically shut down, no further allocation allowed.
82 private static final class Shutdown extends State {
83 private final String message;
85 Shutdown(final String message) {
86 this.message = Preconditions.checkNotNull(message);
90 protected DataTreeSnapshot getSnapshot(final Object transactionId) {
91 throw new IllegalStateException(message);
95 private static final Logger LOG = LoggerFactory.getLogger(InMemoryDOMDataTreeShard.class);
96 private static final AtomicLong COUNTER = new AtomicLong();
98 private final InMemoryDOMDataTreeShard parentShard;
99 private final Collection<DOMDataTreeIdentifier> prefixes;
100 private final Idle idleState = new Idle(this);
102 private static final AtomicReferenceFieldUpdater<InMemoryDOMDataTreeShardProducer, State> STATE_UPDATER =
103 AtomicReferenceFieldUpdater.newUpdater(InMemoryDOMDataTreeShardProducer.class, State.class, "state");
104 private volatile State state;
106 InMemoryDOMDataTreeShardProducer(final InMemoryDOMDataTreeShard parentShard,
107 final Collection<DOMDataTreeIdentifier> prefixes) {
108 this.parentShard = Preconditions.checkNotNull(parentShard);
109 this.prefixes = ImmutableSet.copyOf(prefixes);
114 public InmemoryDOMDataTreeShardWriteTransaction createTransaction() {
115 final String transactionId = nextIdentifier();
118 InmemoryDOMDataTreeShardWriteTransaction ret;
121 ret = parentShard.createTransaction(transactionId, this, prefixes, localState.getSnapshot(transactionId));
122 } while (!STATE_UPDATER.compareAndSet(this, localState, new Allocated(ret)));
127 void transactionReady(final InmemoryDOMDataTreeShardWriteTransaction tx, final DataTreeModification modification) {
128 final State localState = state;
129 LOG.debug("Transaction was readied {}, current state {}", tx.getIdentifier(), localState);
131 if (localState instanceof Allocated) {
132 final Allocated allocated = (Allocated) localState;
133 final InmemoryDOMDataTreeShardWriteTransaction transaction = allocated.getTransaction();
134 Preconditions.checkState(tx.equals(transaction),
135 "Mis-ordered ready transaction %s last allocated was %s", tx, transaction);
136 allocated.setSnapshot(modification);
138 LOG.debug("Ignoring transaction {} readiness due to state {}", tx, localState);
143 * Notify the base logic that a previously-submitted transaction has been committed successfully.
145 * @param transaction Transaction which completed successfully.
147 void onTransactionCommited(final InmemoryDOMDataTreeShardWriteTransaction transaction) {
148 // If the committed transaction was the one we allocated last,
149 // we clear it and the ready snapshot, so the next transaction
150 // allocated refers to the data tree directly.
151 final State localState = state;
152 LOG.debug("Transaction {} commit done, current state {}", transaction.getIdentifier(), localState);
154 if (!(localState instanceof Allocated)) {
155 // This can legally happen if the chain is shut down before the transaction was committed
157 LOG.debug("Ignoring successful transaction {} in state {}", transaction, localState);
161 final Allocated allocated = (Allocated) localState;
162 final InmemoryDOMDataTreeShardWriteTransaction tx = allocated.getTransaction();
163 if (!tx.equals(transaction)) {
164 LOG.debug("Ignoring non-latest successful transaction {} in state {}", transaction, allocated);
168 if (!STATE_UPDATER.compareAndSet(this, localState, idleState)) {
169 LOG.debug("Producer {} has already transitioned from {} to {}, not making it idle", this,
174 void transactionAborted(final InmemoryDOMDataTreeShardWriteTransaction tx) {
175 final State localState = state;
176 if (localState instanceof Allocated) {
177 final Allocated allocated = (Allocated)localState;
178 if (allocated.getTransaction().equals(tx)) {
179 final boolean success = STATE_UPDATER.compareAndSet(this, localState, idleState);
181 LOG.warn("Transaction {} aborted, but producer {} state already transitioned from {} to {}",
182 tx, this, localState, state);
188 private static String nextIdentifier() {
189 return "INMEMORY-SHARD-TX-" + COUNTER.getAndIncrement();
192 DataTreeSnapshot takeSnapshot() {
193 return parentShard.takeSnapshot();
197 public Collection<DOMDataTreeIdentifier> getPrefixes() {