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%2Fbroker%2Fimpl%2FPingPongTransactionChain.java;h=c3a56ed454024b3cb316c1e5bec9d7184c897edc;hp=b3c03b3185efa421b3913aba39362ef6f701bba0;hb=1f14b44c584f97e7c992e611e6227e262fe0089e;hpb=d4a6eae2bde33111bb7198b66379110996b4ebd6 diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/PingPongTransactionChain.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/PingPongTransactionChain.java index b3c03b3185..c3a56ed454 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/PingPongTransactionChain.java +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/PingPongTransactionChain.java @@ -55,8 +55,6 @@ public final class PingPongTransactionChain implements DOMTransactionChain { private static final Logger LOG = LoggerFactory.getLogger(PingPongTransactionChain.class); private final DOMTransactionChain delegate; - @GuardedBy("this") - private PingPongTransaction inflightTransaction; @GuardedBy("this") private boolean failed; @@ -66,7 +64,6 @@ public final class PingPongTransactionChain implements DOMTransactionChain { */ private static final AtomicReferenceFieldUpdater READY_UPDATER = AtomicReferenceFieldUpdater.newUpdater(PingPongTransactionChain.class, PingPongTransaction.class, "readyTx"); - @SuppressWarnings("unused") // Accessed via READY_UPDATER private volatile PingPongTransaction readyTx; /** @@ -79,6 +76,14 @@ public final class PingPongTransactionChain implements DOMTransactionChain { AtomicReferenceFieldUpdater.newUpdater(PingPongTransactionChain.class, PingPongTransaction.class, "lockedTx"); private volatile PingPongTransaction lockedTx; + /** + * This updater is used to manipulate the "inflight" transaction. There can be at most + * one of these at any given time. We perform only compare-and-swap on these. + */ + private static final AtomicReferenceFieldUpdater INFLIGHT_UPDATER = + AtomicReferenceFieldUpdater.newUpdater(PingPongTransactionChain.class, PingPongTransaction.class, "inflightTx"); + private volatile PingPongTransaction inflightTx; + PingPongTransactionChain(final DOMDataBroker broker, final TransactionChainListener listener) { this.delegate = broker.createTransactionChain(new TransactionChainListener() { @Override @@ -86,14 +91,15 @@ public final class PingPongTransactionChain implements DOMTransactionChain { LOG.debug("Delegate chain {} reported failure in {}", chain, transaction, cause); final DOMDataReadWriteTransaction frontend; - if (inflightTransaction == null) { + final PingPongTransaction tx = inflightTx; + if (tx == null) { LOG.warn("Transaction chain {} failed with no pending transactions", chain); frontend = null; } else { - frontend = inflightTransaction.getFrontendTransaction(); + frontend = tx.getFrontendTransaction(); } - listener.onTransactionChainFailed(PingPongTransactionChain.this, frontend , cause); + listener.onTransactionChainFailed(PingPongTransactionChain.this, frontend, cause); delegateFailed(); } @@ -150,7 +156,10 @@ public final class PingPongTransactionChain implements DOMTransactionChain { return oldTx; } - // This forces allocateTransaction() on a slow path + /* + * This forces allocateTransaction() on a slow path, which has to happen after + * this method has completed executing. + */ @GuardedBy("this") private void processIfReady() { final PingPongTransaction tx = READY_UPDATER.getAndSet(this, null); @@ -174,10 +183,11 @@ public final class PingPongTransactionChain implements DOMTransactionChain { } LOG.debug("Submitting transaction {}", tx); - final CheckedFuture f = tx.getTransaction().submit(); - inflightTransaction = tx; + if (!INFLIGHT_UPDATER.compareAndSet(this, null, tx)) { + LOG.warn("Submitting transaction {} while {} is still running", tx, inflightTx); + } - Futures.addCallback(f, new FutureCallback() { + Futures.addCallback(tx.getTransaction().submit(), new FutureCallback() { @Override public void onSuccess(final Void result) { transactionSuccessful(tx, result); @@ -193,10 +203,10 @@ public final class PingPongTransactionChain implements DOMTransactionChain { private void transactionSuccessful(final PingPongTransaction tx, final Void result) { LOG.debug("Transaction {} completed successfully", tx); - synchronized (this) { - Preconditions.checkState(inflightTransaction == tx, "Successful transaction %s while %s was submitted", tx, inflightTransaction); + final boolean success = INFLIGHT_UPDATER.compareAndSet(this, tx, null); + Preconditions.checkState(success, "Successful transaction %s while %s was submitted", tx, inflightTx); - inflightTransaction = null; + synchronized (this) { processIfReady(); } @@ -207,23 +217,35 @@ public final class PingPongTransactionChain implements DOMTransactionChain { private void transactionFailed(final PingPongTransaction tx, final Throwable t) { LOG.debug("Transaction {} failed", tx, t); - synchronized (this) { - Preconditions.checkState(inflightTransaction == tx, "Failed transaction %s while %s was submitted", tx, inflightTransaction); - inflightTransaction = null; - } + final boolean success = INFLIGHT_UPDATER.compareAndSet(this, tx, null); + Preconditions.checkState(success, "Failed transaction %s while %s was submitted", tx, inflightTx); tx.onFailure(t); } private void readyTransaction(final @Nonnull PingPongTransaction tx) { + // First mark the transaction as not locked. final boolean lockedMatch = LOCKED_UPDATER.compareAndSet(this, tx, null); Preconditions.checkState(lockedMatch, "Attempted to submit transaction %s while we have %s", tx, lockedTx); - LOG.debug("Transaction {} unlocked", tx); - synchronized (this) { - if (inflightTransaction == null) { - processTransaction(tx); + /* + * The transaction is ready. It will then be picked up by either next allocation, + * or a background transaction completion callback. + */ + final boolean success = READY_UPDATER.compareAndSet(this, null, tx); + Preconditions.checkState(success, "Transaction %s collided on ready state", tx, readyTx); + LOG.debug("Transaction {} readied"); + + /* + * We do not see a transaction being in-flight, so we need to take care of dispatching + * the transaction to the backend. We are in the ready case, we cannot short-cut + * the checking of readyTx, as an in-flight transaction may have completed between us + * setting the field above and us checking. + */ + if (inflightTx == null) { + synchronized (this) { + processIfReady(); } } }