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
8 package org.opendaylight.mdsal.dom.store.inmemory;
10 import static com.google.common.base.Preconditions.checkState;
11 import static java.util.Objects.requireNonNull;
13 import com.google.common.collect.ImmutableSet;
14 import java.util.Collection;
15 import java.util.concurrent.atomic.AtomicLong;
16 import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
17 import org.eclipse.jdt.annotation.NonNull;
18 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
19 import org.opendaylight.mdsal.dom.spi.shard.DOMDataTreeShardProducer;
20 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
21 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot;
22 import org.slf4j.Logger;
23 import org.slf4j.LoggerFactory;
25 class InMemoryDOMDataTreeShardProducer implements DOMDataTreeShardProducer {
27 private abstract static class State {
29 * Allocate a new snapshot.
31 * @return A new snapshot
33 protected abstract DataTreeSnapshot getSnapshot(Object transactionId);
36 private static final class Idle extends State {
37 private final InMemoryDOMDataTreeShardProducer producer;
39 Idle(final InMemoryDOMDataTreeShardProducer producer) {
40 this.producer = requireNonNull(producer);
44 protected DataTreeSnapshot getSnapshot(final Object transactionId) {
45 return producer.takeSnapshot();
50 * We have a transaction out there.
52 private static final class Allocated extends State {
53 private static final AtomicReferenceFieldUpdater<Allocated, DataTreeSnapshot> SNAPSHOT_UPDATER =
54 AtomicReferenceFieldUpdater.newUpdater(Allocated.class, DataTreeSnapshot.class, "snapshot");
55 private final InmemoryDOMDataTreeShardWriteTransaction transaction;
56 private volatile DataTreeSnapshot snapshot;
58 Allocated(final InmemoryDOMDataTreeShardWriteTransaction transaction) {
59 this.transaction = requireNonNull(transaction);
62 InmemoryDOMDataTreeShardWriteTransaction getTransaction() {
67 protected DataTreeSnapshot getSnapshot(final Object transactionId) {
68 final DataTreeSnapshot ret = snapshot;
69 checkState(ret != null,
70 "Could not get snapshot for transaction %s - previous transaction %s is not ready yet",
71 transactionId, transaction.getIdentifier());
75 void setSnapshot(final DataTreeSnapshot snapshot) {
76 final boolean success = SNAPSHOT_UPDATER.compareAndSet(this, null, snapshot);
77 checkState(success, "Transaction %s has already been marked as ready",
78 transaction.getIdentifier());
83 * Producer is logically shut down, no further allocation allowed.
85 private static final class Shutdown extends State {
86 private final String message;
88 Shutdown(final String message) {
89 this.message = requireNonNull(message);
93 protected DataTreeSnapshot getSnapshot(final Object transactionId) {
94 throw new IllegalStateException(message);
98 private static final Logger LOG = LoggerFactory.getLogger(InMemoryDOMDataTreeShardProducer.class);
99 private static final AtomicLong COUNTER = new AtomicLong();
101 private final InMemoryDOMDataTreeShard parentShard;
102 private final Collection<DOMDataTreeIdentifier> prefixes;
103 private final Idle idleState = new Idle(this);
105 private static final AtomicReferenceFieldUpdater<InMemoryDOMDataTreeShardProducer, State> STATE_UPDATER =
106 AtomicReferenceFieldUpdater.newUpdater(InMemoryDOMDataTreeShardProducer.class, State.class, "state");
107 private volatile State state;
109 private InMemoryShardDataModificationFactory modificationFactory;
111 InMemoryDOMDataTreeShardProducer(final InMemoryDOMDataTreeShard parentShard,
112 final Collection<DOMDataTreeIdentifier> prefixes,
113 final InMemoryShardDataModificationFactory modificationFactory) {
114 this.parentShard = requireNonNull(parentShard);
115 this.prefixes = ImmutableSet.copyOf(prefixes);
116 this.modificationFactory = requireNonNull(modificationFactory);
121 public InmemoryDOMDataTreeShardWriteTransaction createTransaction() {
122 final String transactionId = nextIdentifier();
125 InmemoryDOMDataTreeShardWriteTransaction ret;
128 ret = parentShard.createTransaction(transactionId, this, localState.getSnapshot(transactionId));
129 } while (!STATE_UPDATER.compareAndSet(this, localState, new Allocated(ret)));
135 public void close() {
136 final Shutdown shutdown = new Shutdown("Producer closed");
137 if (!STATE_UPDATER.compareAndSet(this, idleState, shutdown)) {
138 throw new IllegalStateException("Producer " + this + " in unexpected state " + state);
141 // FIXME: This call is ugly, it's better to clean up all by exposing only one entrance,
142 // 'closeProducer' of shard or this 'close'.
143 getParentShard().closeProducer(this);
144 getModificationFactory().close();
147 void transactionReady(final InmemoryDOMDataTreeShardWriteTransaction tx, final DataTreeModification modification) {
148 final State localState = state;
149 LOG.debug("Transaction was readied {}, current state {}", tx.getIdentifier(), localState);
151 if (localState instanceof Allocated) {
152 final Allocated allocated = (Allocated) localState;
153 final InmemoryDOMDataTreeShardWriteTransaction transaction = allocated.getTransaction();
154 checkState(tx.equals(transaction), "Mis-ordered ready transaction %s last allocated was %s", tx,
156 allocated.setSnapshot(modification);
158 LOG.debug("Ignoring transaction {} readiness due to state {}", tx, localState);
163 * Notify the base logic that a previously-submitted transaction has been committed successfully.
165 * @param transaction Transaction which completed successfully.
167 void onTransactionCommited(final InmemoryDOMDataTreeShardWriteTransaction transaction) {
168 // If the committed transaction was the one we allocated last,
169 // we clear it and the ready snapshot, so the next transaction
170 // allocated refers to the data tree directly.
171 final State localState = state;
172 LOG.debug("Transaction {} commit done, current state {}", transaction.getIdentifier(), localState);
174 if (!(localState instanceof Allocated)) {
175 // This can legally happen if the chain is shut down before the transaction was committed
177 LOG.debug("Ignoring successful transaction {} in state {}", transaction, localState);
181 final Allocated allocated = (Allocated) localState;
182 final InmemoryDOMDataTreeShardWriteTransaction tx = allocated.getTransaction();
183 if (!tx.equals(transaction)) {
184 LOG.debug("Ignoring non-latest successful transaction {} in state {}", transaction, allocated);
188 if (!STATE_UPDATER.compareAndSet(this, localState, idleState)) {
189 LOG.debug("Producer {} has already transitioned from {} to {}, not making it idle", this,
194 void transactionAborted(final InmemoryDOMDataTreeShardWriteTransaction tx) {
195 final State localState = state;
196 if (localState instanceof Allocated) {
197 final Allocated allocated = (Allocated) localState;
198 if (allocated.getTransaction().equals(tx)) {
199 final boolean success = STATE_UPDATER.compareAndSet(this, localState, idleState);
201 LOG.warn("Transaction {} aborted, but producer {} state already transitioned from {} to {}",
202 tx, this, localState, state);
208 private static String nextIdentifier() {
209 return "INMEMORY-SHARD-TX-" + COUNTER.getAndIncrement();
212 DataTreeSnapshot takeSnapshot() {
213 return parentShard.takeSnapshot();
217 public Collection<DOMDataTreeIdentifier> getPrefixes() {
221 @NonNull InMemoryDOMDataTreeShard getParentShard() {
225 InMemoryShardDataModificationFactory getModificationFactory() {
226 return modificationFactory;
229 void setModificationFactory(final InMemoryShardDataModificationFactory modificationFactory) {
230 this.getModificationFactory().close();
231 this.modificationFactory = requireNonNull(modificationFactory);