FileDocCategorySizeDatePackage
QueuedTxLock.javaAPI DocJBoss 4.2.112484Fri Jul 13 21:02:26 BST 2007org.jboss.aspects.txlock

QueuedTxLock

public class QueuedTxLock extends Object implements org.jboss.util.deadlock.Resource
This class is holds threads awaiting the transactional lock to be free in a fair FIFO transactional queue. Non-transactional threads are also put in this wait queue as well. This class pops the next waiting transaction from the queue and notifies only those threads waiting associated with that transaction. As of 04/10/2002, you can now specify in jboss.xml method attributes that define methods as read-only. read-only methods(and read-only beans) will release transactional locks at the end of the invocation. This decreases likelyhood of deadlock and increases performance. FIXME marcf: we should get solid numbers on this locking, bench in multi-thread environments We need someone with serious SUN hardware to run this lock into the ground
author
Marc Fleury
author
Bill Burke
version
$Revision: 57186 $

Fields Summary
public static final String
TXLOCK
public static final String
TIMEOUT
private HashMap
txLocks
private LinkedList
txWaitQueue
private Transaction
tx
Transaction holding lock on bean
private boolean
synched
private boolean
isSynchronized
private Logger
log
Constructors Summary
Methods Summary
public voidendInvocation(javax.transaction.Transaction thetx)

      if (log.isTraceEnabled())
         log.trace("endInvocation: miTx=" + thetx + " " + toString());
   
public voidendTransaction()

      boolean trace = log.isTraceEnabled();
      if (trace)
         log.trace("endTransaction: " + toString());
      nextTransaction(trace);
   
public java.lang.ObjectgetResourceHolder()


     
   
      return tx;
   
public javax.transaction.TransactiongetTransaction()

      return tx;
   
protected org.jboss.aspects.txlock.QueuedTxLock$TxLockgetTxLock(javax.transaction.Transaction miTx)

      TxLock lock = null;
      TxLock key = new TxLock(miTx);
      lock = (TxLock) txLocks.get(key);
      if (lock == null)
      {
         txLocks.put(key, key);
         txWaitQueue.addLast(key);
         lock = key;
      }
      return lock;
   
protected booleanisTxExpired(javax.transaction.Transaction miTx)

      if (miTx != null && miTx.getStatus() == Status.STATUS_MARKED_ROLLBACK)
      {
         return true;
      }
      return false;
   
public booleanlockNoWait(javax.transaction.Transaction transaction)

      this.sync();
      if (log.isTraceEnabled())
         log.trace("lockNoWait tx=" + transaction + " " + toString());
      try
      {
         // And are we trying to enter with another transaction?
         if (getTransaction() != null && !getTransaction().equals(transaction))
         {
            return false;
         }
         setTransaction(transaction);
         return true;
      }
      finally
      {
         this.releaseSync();
      }
   
protected voidnextTransaction(boolean trace)

      if (!synched)
      {
         throw new IllegalStateException("do not call nextTransaction while not synched!");
      }

      setTransaction(null);
      // is there a waiting list?
      TxLock thelock = null;
      if (!txWaitQueue.isEmpty())
      {
         thelock = (TxLock) txWaitQueue.removeFirst();
         txLocks.remove(thelock);
         thelock.isQueued = false;
         // The new transaction is the next one, important to set it up to avoid race with
         // new incoming calls
         if (thelock.waitingTx != null)
            DeadlockDetector.singleton.removeWaiting(thelock.waitingTx);
         setTransaction(thelock.waitingTx);
         synchronized (thelock)
         {
            // notify All threads waiting on this transaction.
            // They will enter the methodLock wait loop.
            thelock.notifyAll();
         }
      }
      if (trace)
         log.trace("nextTransaction: " + thelock + " " + toString());
   
public voidreleaseSync()

      synchronized (this)
      {
         synched = false;
         this.notify();
      }
   
public voidschedule(javax.transaction.Transaction miTx, org.jboss.aop.joinpoint.Invocation mi)
doSchedule(Invocation) doSchedule implements a particular policy for scheduling the threads coming in. There is always the spec required "serialization" but we can add custom scheduling in here Synchronizing on lock: a failure to get scheduled must result in a wait() call and a release of the lock. Schedulation must return with lock.

      boolean trace = log.isTraceEnabled();
      this.sync();
      try
      {
         if (trace) log.trace("Begin schedule");

         if (isTxExpired(miTx))
         {
            log.error("Saw rolled back tx=" + miTx);
            throw new RuntimeException("Transaction marked for rollback, possibly a timeout");
         }

         //Next test is independent of whether the context is locked or not, it is purely transactional
         // Is the instance involved with another transaction? if so we implement pessimistic locking
         waitForTx(mi, miTx, trace);
         if (!isSynchronized)
         {
            isSynchronized = true;
            miTx.registerSynchronization(new TxLockSynchronization());
         }
      }
      finally
      {
         this.releaseSync();
      }

   
public voidsetTransaction(javax.transaction.Transaction tx)
The setTransaction associates a transaction with the lock. The current transaction is associated by the schedule call.

      this.tx = tx;
   
public voidsync()

      synchronized (this)
      {
         while (synched)
         {
            this.wait();
         }
         synched = true;
      }
   
public java.lang.StringtoString()

      StringBuffer buffer = new StringBuffer(100);
      buffer.append(" hash=").append(hashCode());
      buffer.append(" tx=").append(getTransaction());
      buffer.append(" synched=").append(synched);
      buffer.append(" queue=").append(txWaitQueue);
      return buffer.toString();
   
protected voidwaitForTx(org.jboss.aop.joinpoint.Invocation mi, javax.transaction.Transaction miTx, boolean trace)
Wait until no other transaction is running with this lock.

return
Returns true if this thread was scheduled in txWaitQueue

      boolean wasScheduled = false;
      // Do we have a running transaction with the context?
      // We loop here until either until success or until transaction timeout
      // If we get out of the loop successfully, we can successfully
      // set the transaction on this puppy.
      TxLock txLock = null;
      while (getTransaction() != null &&
              // And are we trying to enter with another transaction?
              !getTransaction().equals(miTx))
      {
         // Check for a deadlock on every cycle
         try
         {
            DeadlockDetector.singleton.deadlockDetection(miTx, this);
         }
         catch (Exception e)
         {
            // We were queued, not any more
            if (txLock != null && txLock.isQueued)
            {
               txLocks.remove(txLock);
               txWaitQueue.remove(txLock);
            }
            throw e;
         }

         wasScheduled = true;
         // That's no good, only one transaction per context
         // Let's put the thread to sleep the transaction demarcation will wake them up
         if (trace) log.trace("Transactional contention on context miTx=" + miTx + " " + toString());

         if (txLock == null)
            txLock = getTxLock(miTx);

         if (trace) log.trace("Begin wait on " + txLock + " " + toString());

         // And lock the threads on the lock corresponding to the Tx in MI
         synchronized (txLock)
         {
            releaseSync();
            try
            {
               int txTimeout = 0;
               Integer timeout = (Integer) mi.getMetaData(TXLOCK, TIMEOUT);
               if (timeout != null) txTimeout = timeout.intValue();
               txLock.wait(txTimeout);
            }
            catch (InterruptedException ignored)
            {
            }
         } // end synchronized(txLock)

         this.sync();

         if (trace) log.trace("End wait on " + txLock + " " + toString());
         if (isTxExpired(miTx))
         {
            log.error(Thread.currentThread() + "Saw rolled back tx=" + miTx + " waiting for txLock");
            if (txLock.isQueued)
            {
               // Remove the TxLock from the queue because this thread is exiting.
               // Don't worry about notifying other threads that share the same transaction.
               // They will timeout and throw the below RuntimeException
               txLocks.remove(txLock);
               txWaitQueue.remove(txLock);
            }
            else if (getTransaction() != null && getTransaction().equals(miTx))
            {
               // We're not qu
               nextTransaction(trace);
            }
            if (miTx != null)
            {
               DeadlockDetector.singleton.removeWaiting(miTx);
            }
            throw new RuntimeException("Transaction marked for rollback, possibly a timeout");
         }
      } // end while(tx!=miTx)

      // If we get here, this means that we have the txlock
      if (!wasScheduled) setTransaction(miTx);
      return;