[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