[jboss-svn-commits] JBL Code SVN: r24736 - in labs/jbosstm/trunk/ArjunaCore/arjuna: classes/com/arjuna/ats/arjuna/coordinator and 1 other directories.

jboss-svn-commits at lists.jboss.org jboss-svn-commits at lists.jboss.org
Thu Jan 15 10:46:58 EST 2009


Author: mark.little at jboss.com
Date: 2009-01-15 10:46:58 -0500 (Thu, 15 Jan 2009)
New Revision: 24736

Added:
   labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/reaper/ReaperTestCase3.java
Modified:
   labs/jbosstm/trunk/ArjunaCore/arjuna/build.xml
   labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/coordinator/TransactionReaper.java
Log:
https://jira.jboss.org/jira/browse/JBTM-462

Modified: labs/jbosstm/trunk/ArjunaCore/arjuna/build.xml
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/build.xml	2009-01-15 15:14:08 UTC (rev 24735)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/build.xml	2009-01-15 15:46:58 UTC (rev 24736)
@@ -447,12 +447,8 @@
             </classpath>
             <batchtest haltonerror="yes" haltonfailure="yes" fork="yes"
                 todir="${com.hp.mwlabs.ts.arjuna.reports.dest}">
-                <fileset dir="${com.hp.mwlabs.ts.arjuna.tests.src}" includes="**/ReaperTestCase.java"/>
+                <fileset dir="${com.hp.mwlabs.ts.arjuna.tests.src}" includes="**/ReaperTestCase3.java"/>
             </batchtest>
-            <batchtest haltonerror="yes" haltonfailure="yes" fork="yes"
-                todir="${com.hp.mwlabs.ts.arjuna.reports.dest}">
-                <fileset dir="${com.hp.mwlabs.ts.arjuna.tests.src}" includes="**/ReaperTestCase2.java"/>
-            </batchtest>
         </junit>
     </target>
 

Modified: labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/coordinator/TransactionReaper.java
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/coordinator/TransactionReaper.java	2009-01-15 15:14:08 UTC (rev 24735)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/coordinator/TransactionReaper.java	2009-01-15 15:46:58 UTC (rev 24736)
@@ -237,6 +237,8 @@
 		    break;
 		}
 
+		tsLogger.arjLogger.warn("**working on "+e);
+
 		if (tsLogger.arjLoggerI18N.isWarnEnabled())
 		{
 		    tsLogger.arjLoggerI18N
@@ -426,8 +428,7 @@
 
 			synchronized(this)
 			{
-			    _timeouts.remove(e._control);
-			    _transactions.remove(e);
+			    removeElement(e);
 			}
 
 			try
@@ -482,8 +483,7 @@
 
 			synchronized(this)
 			{
-			    _timeouts.remove(e._control);
-			    _transactions.remove(e);
+			    removeElement(e);
 			}
 		    }
 		    break;
@@ -654,8 +654,7 @@
 
 		    synchronized(this)
 		    {
-			_timeouts.remove(e._control);
-			_transactions.remove(e);
+		        removeElement(e);
 		    }
 		}
 		else
@@ -686,8 +685,7 @@
 
 		    synchronized(this)
 		    {
-			_timeouts.remove(e._control);
-			_transactions.remove(e);
+		        removeElement(e);
 		    }
 
 		    try
@@ -845,7 +843,9 @@
 
 		    synchronized(this)
 		    {
-			return _transactions.remove(key);
+		        removeElement(key);
+		        
+		        return true;
 		    }
                 }
 	}
@@ -945,6 +945,121 @@
 		return timeout.intValue();
 	}
 
+	/*
+         * Terminate the transaction reaper. This is a synchronous operation
+         * and will only return once the reaper has been shutdown cleanly.
+         * 
+         * Note, this method assumes that the transaction system has been
+         * shutdown already so no new transactions can be created, or we
+         * could be here for a long time!
+         * 
+         * @param waitForTransactions if <code>true</code> then the reaper will
+         * wait until all transactions have terminated (or been terminated by it).
+         * If <code>false</code> then the reaper will call setRollbackOnly on all
+         * the transactions.
+         */
+	
+	private final void shutdown (boolean waitForTransactions)
+	{
+	    synchronized (_shutdownLock)
+	    {
+	        _inShutdown = true;
+
+	        /*
+	         * If the caller does not want to wait for the normal transaction timeout
+	         * periods to elapse before terminating, then we first start by enabling
+	         * our time machine!
+	         */
+	        
+	        if (!waitForTransactions)
+	        {
+	            Iterator iter = _transactions.iterator();
+	            ReaperElement e;
+	            
+	            while (iter.hasNext())
+	            {
+	                e = (ReaperElement) iter.next();
+	                
+	                e._absoluteTimeout = 0;
+	            }
+	        }
+
+	        /*
+	         * Wait for all of the transactions to
+	         * terminate normally.
+	         */
+
+	        while (_transactions.size() > 0)
+	        {
+	            try
+	            {
+	                _shutdownLock.wait();
+	            }
+	            catch (final Exception ex)
+	            {
+	            }
+	        }
+
+	        synchronized (_reaperThread)
+	        {
+	            _reaperThread.shutdown();               
+	            _reaperThread.interrupt();  // by this stage there should be no transactions left anyway.
+	            
+	            synchronized (this)
+	            {
+	                notify();
+	            }
+
+	            try
+	            {
+	                _reaperThread.join();
+	            }
+	            catch (final Exception ex)
+	            {	                
+	            }
+	        }
+
+	        _reaperThread = null;
+	                	               
+	        _reaperWorkerThread.shutdown();
+	        
+	        synchronized (_reaperWorkerThread)
+	        {
+	            try
+	            {
+	                _reaperWorkerThread.interrupt();
+	                _reaperWorkerThread.join();
+	            }
+	            catch (final Exception ex)
+	            {	                
+	            }
+	        }
+	        
+	        _reaperWorkerThread = null;
+	        
+	        _inShutdown = false;
+	    }
+	}
+	
+	/*
+	 * Remove element from list and trigger waiter if we are
+	 * being shutdown.
+	 */
+	
+	private final void removeElement (ReaperElement e)
+	{
+	    synchronized (_shutdownLock)
+	    {
+                _timeouts.remove(e._control);
+                _transactions.remove(e);
+                
+	        if (_inShutdown && (_transactions.size() == 0))
+	        {
+	            _shutdownLock.notify();
+	        }
+	    }
+	}
+	
 	/**
 	 * Currently we let the reaper thread run at same priority as other threads.
 	 * Could get priority from environment.
@@ -1133,6 +1248,29 @@
 			return _theReaper;
 	}
 
+	/**
+	 * Terminate the transaction reaper. This is a synchronous operation
+	 * and will only return once the reaper has been shutdown cleanly.
+	 * 
+	 * Note, this method assumes that the transaction system has been
+	 * shutdown already so no new transactions can be created, or we
+	 * could be here for a long time!
+	 * 
+	 * @param waitForTransactions if <code>true</code> then the reaper will
+	 * wait until all transactions have terminated (or been terminated by it).
+	 * If <code>false</code> then the reaper will call setRollbackOnly on all
+	 * the transactions.
+	 */
+	
+	public static void terminate (boolean waitForTransactions)
+	{
+	    if (_theReaper != null)
+	    {
+	        _theReaper.shutdown(waitForTransactions);
+	        _theReaper = null;
+	    }
+	}
+	
     public static boolean isDynamic() {
         return _dynamic;
     }
@@ -1194,4 +1332,11 @@
 	private static long _lifetime = 0;
 
 	private static int _zombieCount = 0;
+	
+	/*
+	 * Shutdown lock.
+	 */
+	
+	private static final Object _shutdownLock = new Object();
+	private static boolean _inShutdown = false;
 }

Added: labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/reaper/ReaperTestCase3.java
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/reaper/ReaperTestCase3.java	                        (rev 0)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/reaper/ReaperTestCase3.java	2009-01-15 15:46:58 UTC (rev 24736)
@@ -0,0 +1,247 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2007, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags.
+ * See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ * This program is distributed in the hope that it will be useful, but WITHOUT A
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License,
+ * v.2.1 along with this distribution; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA  02110-1301, USA.
+ *
+ * (C) 2007,
+ * @author JBoss Inc.
+ */
+package com.hp.mwtests.ts.arjuna.reaper;
+
+import junit.framework.TestCase;
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import com.arjuna.ats.arjuna.coordinator.TransactionReaper;
+import com.arjuna.ats.arjuna.coordinator.Reapable;
+import com.arjuna.ats.arjuna.coordinator.ActionStatus;
+import com.arjuna.ats.arjuna.common.Uid;
+import com.arjuna.ats.internal.arjuna.coordinator.ReaperElement;
+
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+
+public class ReaperTestCase3 extends TestCase
+{
+    public static Test suite()
+    {
+	return new TestSuite(ReaperTestCase3.class);
+    }
+
+    public void testReaperWait () throws Exception
+    {
+	TransactionReaper.create(500);
+	TransactionReaper reaper = TransactionReaper.transactionReaper();
+
+	// give the reaper worker time to start too
+
+	Thread.sleep(1000);
+
+	// create slow reapables some of which will not respond immediately
+        // to cancel requests and ensure that they get cancelled
+        // and that the reaper does not get wedged
+
+	SlowReapable reapable1 = new SlowReapable(new Uid(), 2000, 0, true, true);
+	SlowReapable reapable2 = new SlowReapable(new Uid(), 0, 0, true, true);
+	SlowReapable reapable3 = new SlowReapable(new Uid(), 100, 2000, false, true);
+	SlowReapable reapable4 = new SlowReapable(new Uid(), 1000, 1000, false, false);
+
+	// insert reapables so they timeout at 1 second intervals then
+	// check progress of cancellations and rollbacks
+
+	assertTrue(reaper.insert(reapable1, 1));
+
+	assertTrue(reaper.insert(reapable2, 2));
+
+	assertTrue(reaper.insert(reapable3, 3));
+
+	assertTrue(reaper.insert(reapable4, 4));
+
+	// make sure they were all registered
+
+	assertEquals(4, reaper.numberOfTransactions());
+	assertEquals(4, reaper.numberOfTimeouts());
+
+	// force a termination but wait for the transactions to timeout
+	
+	TransactionReaper.terminate(true);
+	
+	assertEquals(0, reaper.numberOfTransactions());
+	
+	assertTrue(reapable1.getCancelTried());
+	assertTrue(reapable2.getCancelTried());
+	assertTrue(reapable3.getCancelTried());
+	assertTrue(reapable4.getCancelTried());
+    }
+    
+    public void testReaperForce () throws Exception
+    {
+        TransactionReaper.create(5000);
+        TransactionReaper reaper = TransactionReaper.transactionReaper();
+
+        // give the reaper worker time to start too
+
+        Thread.sleep(1000);
+
+        // create slow reapables some of which will not respond immediately
+        // to cancel requests and ensure that they get cancelled
+        // and that the reaper does not get wedged
+
+        SlowReapable reapable1 = new SlowReapable(new Uid(), 2000, 0, true, true);
+        SlowReapable reapable2 = new SlowReapable(new Uid(), 0, 0, true, true);
+        SlowReapable reapable3 = new SlowReapable(new Uid(), 100, 2000, false, true);
+        SlowReapable reapable4 = new SlowReapable(new Uid(), 1000, 1000, false, false);
+
+        // insert reapables so they timeout at 1 second intervals then
+        // check progress of cancellations and rollbacks
+
+        assertTrue(reaper.insert(reapable1, 1));
+
+        assertTrue(reaper.insert(reapable2, 2));
+
+        assertTrue(reaper.insert(reapable3, 3));
+
+        assertTrue(reaper.insert(reapable4, 4));
+
+        // make sure they were all registered
+
+        assertEquals(4, reaper.numberOfTransactions());
+        assertEquals(4, reaper.numberOfTimeouts());
+
+        // force a termination and don't wait for the normal timeout periods
+        
+        TransactionReaper.terminate(false);
+        
+        assertEquals(0, reaper.numberOfTransactions());
+        
+        assertTrue(reapable1.getCancelTried());
+        assertTrue(reapable2.getCancelTried());
+        assertTrue(reapable3.getCancelTried());
+        assertTrue(reapable4.getCancelTried());
+        
+        /*
+         * Since we've (hopefully) just run two tests with new reapers in the same VM
+         * we've also shown that it's possible to start/terminate/start again!
+         */
+    }
+
+    public class SlowReapable implements Reapable
+    {
+	public SlowReapable(Uid uid, int callDelay, int interruptDelay, boolean doCancel, boolean doRollback)
+	{
+	    this.uid = uid;
+            this.callDelay = callDelay;
+            this.interruptDelay = interruptDelay;
+            this.doCancel = doCancel;
+            this.doRollback = doRollback;
+	    cancelTried = false;
+	    rollbackTried = false;
+	    running = true;
+        }
+
+	public boolean running()
+	{
+	    return getRunning();
+	}
+
+	public boolean preventCommit()
+	{
+	    setRollbackTried();
+	    clearRunning();
+	    return doRollback;
+	}
+
+	public int cancel()
+	{
+	    boolean interrupted = false;
+
+	    setCancelTried();
+
+	    // track the worker trying to do the cancel so we can
+	    // detect if it becomes a zombie
+
+	    setCancelThread(Thread.currentThread());
+
+	    if (callDelay > 0) {
+		try {
+		    Thread.sleep(callDelay);
+		} catch (InterruptedException e) {
+		    interrupted = true;
+		}
+	    }
+	    if (interrupted && interruptDelay > 0) {
+		try {
+		    Thread.sleep(interruptDelay);
+		} catch (InterruptedException e) {
+		}
+	    }
+
+	    if (doCancel) {
+		clearRunning();
+		return ActionStatus.ABORTED;
+	    } else {
+		return ActionStatus.RUNNING;
+	    }
+	}
+
+	public Uid get_uid()
+	{
+	    return uid;
+	}
+
+	private Uid uid;
+        private int callDelay; // in milliseconds
+        private int interruptDelay; // in milliseconds
+        private boolean doCancel;
+        private boolean doRollback;
+	private boolean cancelTried;
+	private boolean rollbackTried;
+	private boolean running;
+	private Thread cancelThread;
+
+	public synchronized void setCancelTried()
+	{
+	    cancelTried = true;
+	}
+	public synchronized boolean getCancelTried()
+	{
+	    return cancelTried;
+	}
+	public synchronized void setCancelThread(Thread cancelThread)
+	{
+	    this.cancelThread = cancelThread;
+	}
+	public synchronized Thread getCancelThread()
+	{
+	    return cancelThread;
+	}
+	public synchronized void setRollbackTried()
+	{
+	    rollbackTried = true;
+	}
+	public synchronized boolean getRollbackTried()
+	{
+	    return rollbackTried;
+	}
+	public synchronized void clearRunning()
+	{
+	    running = false;
+	}
+	public synchronized boolean getRunning()
+	{
+	    return running;
+	}
+    }
+}




More information about the jboss-svn-commits mailing list