[jboss-svn-commits] JBL Code SVN: r32861 - in labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP: ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna and 13 other directories.

jboss-svn-commits at lists.jboss.org jboss-svn-commits at lists.jboss.org
Thu May 13 07:44:32 EDT 2010


Author: jhalliday
Date: 2010-05-13 07:44:31 -0400 (Thu, 13 May 2010)
New Revision: 32861

Added:
   labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/coordinator/ReaperElementManager.java
   labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaCore/arjuna/tests/byteman-scripts/
   labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaCore/arjuna/tests/byteman-scripts/objectstore.txt
   labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaCore/arjuna/tests/byteman-scripts/reaper.txt
Modified:
   labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaCore/arjuna/build.xml
   labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/AtomicAction.java
   labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/coordinator/TransactionReaper.java
   labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/coordinator/ReaperElement.java
   labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/reaper/ReaperTestCase.java
   labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/reaper/ReaperTestCase2.java
   labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/reaper/ReaperTestCase3.java
   labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/transaction/arjunacore/subordinate/SubordinateAtomicAction.java
   labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaJTS/jtax/classes/com/arjuna/ats/internal/jta/transaction/jts/subordinate/jca/SubordinateAtomicTransaction.java
   labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaJTS/jts/classes/com/arjuna/ats/internal/jts/interposition/ServerFactory.java
   labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaJTS/jts/classes/com/arjuna/ats/internal/jts/orbspecific/TransactionFactoryImple.java
   labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaJTS/jts/classes/com/arjuna/ats/internal/jts/orbspecific/coordinator/ArjunaTransactionImple.java
   labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaJTS/jts/classes/com/arjuna/ats/jts/OTSManager.java
   labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/atsintegration/classes/com/arjuna/ats/jbossatx/jta/TransactionManagerService.java
   labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/atsintegration/classes/com/arjuna/ats/jbossatx/jts/TransactionManagerService.java
Log:
Backport transaction reaper performance improvements to the 4.6.1.CP branch. JBTM-624


Modified: labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaCore/arjuna/build.xml
===================================================================
--- labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaCore/arjuna/build.xml	2010-05-13 11:36:42 UTC (rev 32860)
+++ labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaCore/arjuna/build.xml	2010-05-13 11:44:31 UTC (rev 32861)
@@ -439,28 +439,17 @@
         <mkdir dir="${com.hp.mwlabs.ts.arjuna.reports.dest}"/>
         <junit printsummary="yes">
             <formatter type="plain"/>
-            <classpath>
+        	<!--jvmarg value="-Xdebug"/>
+         <jvmarg value="-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000"/-->
+
+            	<classpath>
                 <pathelement location="${com.hp.mwlabs.ts.arjuna.tests.dest}"/>
                 <path location="${com.hp.mwlabs.ts.arjuna.dest}"/>
                 <pathelement path="${build.classpath}"/>
                 <pathelement location="etc"/>
             </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"/>
-        	            </batchtest>
-		<!-- decommissioned becaue of timing problems. thsi has been
-		     dealt with in trunk using byteman rules
-		<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>
-		-->
-            <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="**/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="**/TxControlUnitTest.java"/>
         	</batchtest>
@@ -469,6 +458,28 @@
         					<fileset dir="${com.hp.mwlabs.ts.arjuna.tests.src}" includes="**/PersistenceUnitTest.java"/>
         	</batchtest>
         </junit>
+
+        <junit printsummary="yes" fork="true" dir="${basedir}" showoutput="false">
+            <formatter type="plain"/>
+            <classpath>
+                <pathelement location="${com.hp.mwlabs.ts.arjuna.tests.dest}"/>
+                <path location="${com.hp.mwlabs.ts.arjuna.dest}"/>
+                <pathelement path="${build.classpath}"/>
+                <pathelement location="etc"/>
+            </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}">
+                    <include name="**/reaper/ReaperTestCase.java"/>
+                    <include name="**/reaper/ReaperTestCase2.java"/>
+                    <include name="**/reaper/ReaperTestCase3.java"/>
+                </fileset>
+            </batchtest>
+
+            <jvmarg value="-javaagent:../../ext/byteman.jar=script:tests/byteman-scripts/reaper.txt"/>
+            <jvmarg value="-Dorg.jboss.byteman.debug"/>
+        </junit>
     </target>
 
 

Modified: labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/AtomicAction.java
===================================================================
--- labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/AtomicAction.java	2010-05-13 11:36:42 UTC (rev 32860)
+++ labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/AtomicAction.java	2010-05-13 11:44:31 UTC (rev 32861)
@@ -144,7 +144,7 @@
 				_timeout = TxControl.getDefaultTimeout();
 			
 			if (_timeout > 0)
-				TransactionReaper.transactionReaper(true).insert(this, _timeout);
+				TransactionReaper.transactionReaper().insert(this, _timeout);
 		}
 
 		return status;
@@ -182,7 +182,7 @@
 
 		ThreadActionData.popAction();
 
-		TransactionReaper.create().remove(this);
+		TransactionReaper.transactionReaper().remove(this);
 
 		return status;
 	}
@@ -206,7 +206,7 @@
 
 		ThreadActionData.popAction();
 
-		TransactionReaper.create().remove(this);
+		TransactionReaper.transactionReaper().remove(this);
 
 		return status;
 	}
@@ -220,7 +220,7 @@
 		 * the thread-to-tx association though.
 		 */
 
-		TransactionReaper.create().remove(this);
+		TransactionReaper.transactionReaper().remove(this);
 
 		return outcome;
 	}
@@ -234,7 +234,7 @@
 		 * the thread-to-tx association though.
 		 */
 
-		TransactionReaper.create().remove(this);
+		TransactionReaper.transactionReaper().remove(this);
 
 		return outcome;
 	}

Modified: labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/coordinator/TransactionReaper.java
===================================================================
--- labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/coordinator/TransactionReaper.java	2010-05-13 11:36:42 UTC (rev 32860)
+++ labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/coordinator/TransactionReaper.java	2010-05-13 11:44:31 UTC (rev 32861)
@@ -33,18 +33,16 @@
 
 import com.arjuna.ats.arjuna.common.Environment;
 import com.arjuna.ats.arjuna.common.arjPropertyManager;
-import com.arjuna.ats.arjuna.coordinator.Reapable;
-import com.arjuna.ats.arjuna.coordinator.ActionStatus;
+import com.arjuna.ats.arjuna.coordinator.listener.ReaperMonitor;
 
 import com.arjuna.ats.internal.arjuna.coordinator.*;
 
 import com.arjuna.ats.arjuna.logging.tsLogger;
-import com.arjuna.ats.arjuna.logging.FacilityCode;
 
-import com.arjuna.common.util.logging.*;
-
 import java.util.*;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicLong;
 
 /**
  * Class to record transactions with non-zero timeout values, and class to
@@ -55,1098 +53,876 @@
  * @version $Id: TransactionReaper.java 2342 2006-03-30 13:06:17Z $
  * @since JTS 1.0.
  *
- *
  * @message com.arjuna.ats.arjuna.coordinator.TransactionReaper_1
- *          [com.arjuna.ats.arjuna.coordinator.TransactionReaper_1] -
- *          TransactionReaper - could not create transaction list. Out of
- *          memory.
+ * [com.arjuna.ats.arjuna.coordinator.TransactionReaper_1] -
+ * TransactionReaper - attempting to insert an element that is already present.
  * @message com.arjuna.ats.arjuna.coordinator.TransactionReaper_2
- *          [com.arjuna.ats.arjuna.coordinator.TransactionReaper_2] -
- *          TransactionReaper::check - comparing {0}
+ * [com.arjuna.ats.arjuna.coordinator.TransactionReaper_2] -
+ * TransactionReaper::check - comparing {0}
  * @message com.arjuna.ats.arjuna.coordinator.TransactionReaper_3
- *          [com.arjuna.ats.arjuna.coordinator.TransactionReaper_3] -
- *          TransactionReaper::getTimeout for {0} returning {1}
+ * [com.arjuna.ats.arjuna.coordinator.TransactionReaper_3] -
+ * TransactionReaper::getTimeout for {0} returning {1}
  * @message com.arjuna.ats.arjuna.coordinator.TransactionReaper_17
- *          [com.arjuna.ats.arjuna.coordinator.TransactionReaper_17] -
- *          TransactionReaper::getRemainingTimeoutMillis for {0} returning {1}
+ * [com.arjuna.ats.arjuna.coordinator.TransactionReaper_17] -
+ * TransactionReaper::getRemainingTimeoutMillis for {0} returning {1}
  * @message com.arjuna.ats.arjuna.coordinator.TransactionReaper_4
- *          [com.arjuna.ats.arjuna.coordinator.TransactionReaper_4] -
- *          TransactionReaper::check interrupting cancel in progress for {0}
+ * [com.arjuna.ats.arjuna.coordinator.TransactionReaper_4] -
+ * TransactionReaper::check interrupting cancel in progress for {0}
  * @message com.arjuna.ats.arjuna.coordinator.TransactionReaper_5
- *          [com.arjuna.ats.arjuna.coordinator.TransactionReaper_5] -
- *          TransactionReaper::check worker zombie count {0} exceeds specified limit
+ * [com.arjuna.ats.arjuna.coordinator.TransactionReaper_5] -
+ * TransactionReaper::check worker zombie count {0} exceeds specified limit
  * @message com.arjuna.ats.arjuna.coordinator.TransactionReaper_6
- *          [com.arjuna.ats.arjuna.coordinator.TransactionReaper_6] -
- *          TransactionReaper::check worker {0} not responding to interrupt when cancelling TX {1} -- worker marked as zombie and TX scheduled for mark-as-rollback
+ * [com.arjuna.ats.arjuna.coordinator.TransactionReaper_6] -
+ * TransactionReaper::check worker {0} not responding to interrupt when cancelling TX {1} -- worker marked as zombie and TX scheduled for mark-as-rollback
  * @message com.arjuna.ats.arjuna.coordinator.TransactionReaper_7
- *          [com.arjuna.ats.arjuna.coordinator.TransactionReaper_7] -
- *          TransactionReaper::doCancellations worker {0} successfully canceled TX {1}
+ * [com.arjuna.ats.arjuna.coordinator.TransactionReaper_7] -
+ * TransactionReaper::doCancellations worker {0} successfully canceled TX {1}
  * @message com.arjuna.ats.arjuna.coordinator.TransactionReaper_8
- *          [com.arjuna.ats.arjuna.coordinator.TransactionReaper_8] -
- *          TransactionReaper::doCancellations worker {0} failed to cancel TX {1} -- rescheduling for mark-as-rollback
+ * [com.arjuna.ats.arjuna.coordinator.TransactionReaper_8] -
+ * TransactionReaper::doCancellations worker {0} failed to cancel TX {1} -- rescheduling for mark-as-rollback
  * @message com.arjuna.ats.arjuna.coordinator.TransactionReaper_9
- *          [com.arjuna.ats.arjuna.coordinator.TransactionReaper_9] -
- *          TransactionReaper::doCancellations worker {0} exception during cancel of TX {1} -- rescheduling for mark-as-rollback
+ * [com.arjuna.ats.arjuna.coordinator.TransactionReaper_9] -
+ * TransactionReaper::doCancellations worker {0} exception during cancel of TX {1} -- rescheduling for mark-as-rollback
  * @message com.arjuna.ats.arjuna.coordinator.TransactionReaper_10
- *          [com.arjuna.ats.arjuna.coordinator.TransactionReaper_10] -
- *          TransactionReaper::check successfuly marked TX {0} as rollback only
+ * [com.arjuna.ats.arjuna.coordinator.TransactionReaper_10] -
+ * TransactionReaper::check successfuly marked TX {0} as rollback only
  * @message com.arjuna.ats.arjuna.coordinator.TransactionReaper_11
- *          [com.arjuna.ats.arjuna.coordinator.TransactionReaper_11] -
- *          TransactionReaper::check failed to mark TX {0}  as rollback only
+ * [com.arjuna.ats.arjuna.coordinator.TransactionReaper_11] -
+ * TransactionReaper::check failed to mark TX {0}  as rollback only
  * @message com.arjuna.ats.arjuna.coordinator.TransactionReaper_12
- *          [com.arjuna.ats.arjuna.coordinator.TransactionReaper_12] -
- *          TransactionReaper::check exception while marking TX {0} as rollback only
+ * [com.arjuna.ats.arjuna.coordinator.TransactionReaper_12] -
+ * TransactionReaper::check exception while marking TX {0} as rollback only
  * @message com.arjuna.ats.arjuna.coordinator.TransactionReaper_13
- *          [com.arjuna.ats.arjuna.coordinator.TransactionReaper_13] -
- *          TransactionReaper::doCancellations worker {0} missed interrupt when cancelling TX {1} -- exiting as zombie (zombie count decremented to {2})
+ * [com.arjuna.ats.arjuna.coordinator.TransactionReaper_13] -
+ * TransactionReaper::doCancellations worker {0} missed interrupt when cancelling TX {1} -- exiting as zombie (zombie count decremented to {2})
  * @message com.arjuna.ats.arjuna.coordinator.TransactionReaper_14
- *          [com.arjuna.ats.arjuna.coordinator.TransactionReaper_14] -
- *          TransactionReaper::doCancellations worker {0} successfuly marked TX {1} as rollback only
+ * [com.arjuna.ats.arjuna.coordinator.TransactionReaper_14] -
+ * TransactionReaper::doCancellations worker {0} successfuly marked TX {1} as rollback only
  * @message com.arjuna.ats.arjuna.coordinator.TransactionReaper_15
- *          [com.arjuna.ats.arjuna.coordinator.TransactionReaper_15] -
- *          TransactionReaper::doCancellations worker {0} failed to mark TX {1}  as rollback only
+ * [com.arjuna.ats.arjuna.coordinator.TransactionReaper_15] -
+ * TransactionReaper::doCancellations worker {0} failed to mark TX {1}  as rollback only
  * @message com.arjuna.ats.arjuna.coordinator.TransactionReaper_16
- *          [com.arjuna.ats.arjuna.coordinator.TransactionReaper_16] -
- *          TransactionReaper::doCancellations worker {0} exception while marking TX {1} as rollback only
+ * [com.arjuna.ats.arjuna.coordinator.TransactionReaper_16] -
+ * TransactionReaper::doCancellations worker {0} exception while marking TX {1} as rollback only
  * @message com.arjuna.ats.arjuna.coordinator.TransactionReaper_18
- *          [com.arjuna.ats.arjuna.coordinator.TransactionReaper_18] -
- *          TransactionReaper::check timeout for TX {0} in state  {1}
+ * [com.arjuna.ats.arjuna.coordinator.TransactionReaper_18] -
+ * TransactionReaper::check timeout for TX {0} in state  {1}
  * @message com.arjuna.ats.arjuna.coordinator.TransactionReaper_19
- *          [com.arjuna.ats.arjuna.coordinator.TransactionReaper_19] -
- *          TransactionReaper NORMAL mode is deprecated. Update config to use PERIODIC for equivalent behaviour.
+ * [com.arjuna.ats.arjuna.coordinator.TransactionReaper_19] -
+ * TransactionReaper NORMAL mode is deprecated. Update config to use PERIODIC for equivalent behaviour.
  */
 
 public class TransactionReaper
 {
 
-	public static final String NORMAL = "NORMAL";
+    public static final String NORMAL = "NORMAL";
 
-	public static final String DYNAMIC = "DYNAMIC";
+    public static final String DYNAMIC = "DYNAMIC";
 
     public static final String PERIODIC = "PERIODIC"; // the new name for 'NORMAL'
 
-    public TransactionReaper(long checkPeriod)
-	{
-		if (tsLogger.arjLogger.debugAllowed())
-		{
-			tsLogger.arjLogger.debug(DebugLevel.CONSTRUCTORS,
-					VisibilityLevel.VIS_PUBLIC, FacilityCode.FAC_ATOMIC_ACTION,
-					"TransactionReaper::TransactionReaper ( " + checkPeriod
-							+ " )");
-		}
+    private TransactionReaper(long checkPeriod)
+    {
+        if (tsLogger.arjLogger.isDebugEnabled()) {
+            tsLogger.arjLogger.debug("TransactionReaper::TransactionReaper ( " + checkPeriod
+                    + " )");
+        }
 
-		_checkPeriod = checkPeriod;
+        _checkPeriod = checkPeriod;
+    }
 
-		if (_transactions == null)
-		{
-			if (tsLogger.arjLoggerI18N.isFatalEnabled())
-			{
-				tsLogger.arjLoggerI18N
-						.fatal("com.arjuna.ats.arjuna.coordinator.TransactionReaper_1");
-			}
+    public final long checkingPeriod()
+    {
+        if (_dynamic) {
+            return nextDynamicCheckTime.get() - System.currentTimeMillis();
+        } else {
+            // if we have a cancel in progress which needs
+            // checking up on then we have to wake up in time
+            // for it whether we are using a static or
+            // dynamic model
 
-			throw new OutOfMemoryError();
-		}
-	}
+            final ReaperElement head = _reaperElements.getFirst();
+            if(head != null) {
+                if (head._status != ReaperElement.RUN) {
+                    long waitTime = head.getAbsoluteTimeout() - System.currentTimeMillis();
+                    if (waitTime < _checkPeriod) {
+                        return waitTime;
+                    }
+                }
+            }
 
-	public final long checkingPeriod()
-	{
-		if (_dynamic)
-		{
-			try
-			{
-				final ReaperElement head = (ReaperElement) _transactions.first();  //_list.peak();
-				return head.getAbsoluteTimeout() - System.currentTimeMillis();
-			}
-			catch (final NoSuchElementException nsee)
-			{
-				return Long.MAX_VALUE; // list is empty, so we can sleep until something is inserted.
-			}
-		}
-		else
-		{
-                     // if we have a cancel in progress which needs
-                     // checking up on then we have to wake up in time
-                     // for it whether we are using a static or
-                     // dynamic model
+            return _checkPeriod;
+        }
+    }
 
-                     try
-                     {
-                          final ReaperElement head = (ReaperElement) _transactions.first();  //_list.peak();
-                          if (head._status != ReaperElement.RUN) {
-                               long waitTime = head.getAbsoluteTimeout() - System.currentTimeMillis();
-                               if (waitTime < _checkPeriod)
-                               {
-                                    return head.getAbsoluteTimeout() - System.currentTimeMillis();
-                               }
-                          }
-                     }
-                    catch (final NoSuchElementException nsee) {}
+    /**
+     * process all entries in the timeout queue which have
+     * expired. entries for newly expired transactions are passed
+     * to a worker thread for cancellation and requeued for
+     * subsequent progress checks. the worker is given a kick if
+     * such checks find it is wedged.
+     * <p/>
+     * Timeout is given in milliseconds.
+     */
 
-		    return _checkPeriod;
-		}
-	}
+    public final void check()
+    {
+        if (tsLogger.arjLogger.isDebugEnabled()) {
+            tsLogger.arjLogger.debug("TransactionReaper::check ()");
+        }
 
-	/**
-         * process all entries in the timeout queue which have
-         * expired. entries for newly expired transactions are passed
-         * to a worker thread for cancellation and requeued for
-         * subsequent progress checks. the worker is given a kick if
-         * such checks find it is wedged.
-	 *
-	 * Timeout is given in milliseconds.
-	 */
+        do {
+            final ReaperElement reaperElement;
 
-	public final boolean check()
-	{
-	    if (tsLogger.arjLogger.debugAllowed())
-	    {
-            tsLogger.arjLogger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC,
-                    FacilityCode.FAC_ATOMIC_ACTION,  "TransactionReaper::check ()");
-	    }
+            synchronized (this) {
+                final long now = System.currentTimeMillis();
+                final long next = nextDynamicCheckTime.get();
 
-	    do
-	    {
-		final ReaperElement e ;
+                if (tsLogger.arjLoggerI18N.isDebugEnabled()) {
+                    tsLogger.arjLoggerI18N.debug("com.arjuna.ats.arjuna.coordinator.TransactionReaper_2", new Object[]{Long.toString(next)});
+                }
 
-        synchronized (this)
-        {
-            // purge the pending inerts before doing anything else. This may hold up other inserts
-            // for a while. Future versions may prefer to insert only a portion of the pending set.
-            Set<Map.Entry<Reapable,ReaperElement>> entrySet = _pendingInsertions.entrySet();
-            if(entrySet != null) {
-                Iterator<Map.Entry<Reapable,ReaperElement>> queueIter = entrySet.iterator();
-                while(queueIter.hasNext()) {
-                    Map.Entry<Reapable,ReaperElement> entry = queueIter.next();
-                    ReaperElement element = entry.getValue();
-                    // inert is also locked on (this), but remove is not. So, we are careful to check that
-                    // we don't insert an element that's been removed from the pending set by a concurrent thread.
-                    if(entrySet.remove(entry)) {
-                        synchronousInsert(element);
+                if (now < next) {
+                    break;
+                }
+
+                reaperElement = _reaperElements.getFirst();
+                // TODO close window where first can change - maybe record nextDynamicCheckTime before probing first,
+                // then use compareAndSet? Although something will need to check before sleeping anyhow...
+                if(reaperElement == null) {
+                    nextDynamicCheckTime.set(Long.MAX_VALUE);
+                    return;
+                } else {
+                    if(reaperElement.getAbsoluteTimeout() > now) {
+                        return; // nothing to do yet.
                     }
                 }
             }
 
-            try
-            {
-                e = (ReaperElement)_transactions.first();
+            if (tsLogger.arjLoggerI18N.isWarnEnabled()) {
+                tsLogger.arjLoggerI18N.warn("com.arjuna.ats.arjuna.coordinator.TransactionReaper_18",
+                                new Object[] {reaperElement._control.get_uid(), reaperElement.statusName()});
             }
-            catch (final NoSuchElementException nsee)
-            {
-                return true ;
-            }
-            if (tsLogger.arjLoggerI18N.isDebugEnabled())
-            {
-                tsLogger.arjLoggerI18N
-                .debug(
-                    DebugLevel.FUNCTIONS,
-                    VisibilityLevel.VIS_PUBLIC,
-                    FacilityCode.FAC_ATOMIC_ACTION,
-                    "com.arjuna.ats.arjuna.coordinator.TransactionReaper_2",
-                    new Object[]
-                    { Long.toString(e.getAbsoluteTimeout()) });
-            }
 
-            final long now = System.currentTimeMillis();
+            // if we have to synchronize on multiple objects we always
+            // do so in a fixed order ReaperElement before Reaper and
+            // ReaperElement before Reaper._cancelQueue in order to
+            // ensure we don't deadlock. We never sychronize on the
+            // reaper and the cancel queue at the same time.
 
-            if (now < e.getAbsoluteTimeout())
-            {
-                // go back to sleep
+            synchronized (reaperElement) {
+                switch (reaperElement._status) {
+                    case ReaperElement.RUN: {
+                        // this tx has just timed out. remove it from the
+                        // TX list, update the timeout to take account of
+                        // cancellation period and reinsert as a cancelled
+                        // TX. this ensures we process it again if it does
+                        // not get cancelled in time
 
-                break;
-            }
-        }
+                        reaperElement._status = ReaperElement.SCHEDULE_CANCEL;
 
-		if (tsLogger.arjLoggerI18N.isWarnEnabled())
-		{
-		    tsLogger.arjLoggerI18N
-			.warn(
-                    "com.arjuna.ats.arjuna.coordinator.TransactionReaper_18",
-                    new Object[]
-                    { e._control.get_uid() , e.statusName() });
-		}
+                        reinsertElement(reaperElement, _cancelWaitPeriod);
 
-		// if we have to synchronize on multiple objects we always
-		// do so in a fixed order ReaperElement before Reaper and
-		// ReaperElement before Reaper._cancelQueue in order to
-		// ensure we don't deadlock. We never sychronize on the
-		// reaper and the cancel queue at the same time.
+                        if (tsLogger.arjLogger.isDebugEnabled()) {
+                            tsLogger.arjLogger.debug("Reaper scheduling TX for cancellation " + reaperElement._control.get_uid());
+                        }
 
-		synchronized(e) {
-		    switch (e._status)
-		    {
-		    case ReaperElement.RUN:
-		    {
-			// this tx has just timed out. remove it from the
-			// TX list, update the timeout to take account of
-			// cancellation period and reinsert as a cancelled
-			// TX. this ensures we process it again if it does
-			// not get cancelled in time
+                        // insert into cancellation queue for a worker
+                        // thread to process and then make sure a worker
+                        // thread is awake
 
-			e._status = ReaperElement.SCHEDULE_CANCEL;
+                        synchronized (_workQueue) {
+                            _workQueue.add(reaperElement);
+                            _workQueue.notifyAll();
+                        }
+                    }
+                    break;
+                    case ReaperElement.SCHEDULE_CANCEL: {
+                        // hmm, a worker is taking its time to
+                        // start processing this scheduled entry.
+                        // we may just be running slow ... but the
+                        // worker may be wedged under a cancel for
+                        // some other TX. add an extra delay to
+                        // give the worker more time to complete
+                        // its current task and progress this
+                        // entry to the CANCEL state. if the
+                        // worker *is* wedged then this will
+                        // ensure the wedged TX entry comes to the
+                        // front of the queue.
 
-			synchronized (this)
-			{
-			    _transactions.remove(e);
+                        reinsertElement(reaperElement, _cancelWaitPeriod);
 
-			    e.setAbsoluteTimeout((System.currentTimeMillis() + _cancelWaitPeriod));
-			    _transactions.add(e);
-			}
+                        if (tsLogger.arjLogger.isDebugEnabled()) {
+                            tsLogger.arjLogger.debug("Reaper deferring interrupt for TX scheduled for cancel " + reaperElement._control.get_uid());
+                        }
+                    }
+                    break;
+                    case ReaperElement.CANCEL: {
+                        // ok, the worker must be wedged under a
+                        // call to cancel() -- kick the thread and
+                        // reschedule the element for a later
+                        // check to ensure the thread responded to
+                        // the kick
 
-			if (tsLogger.arjLogger.debugAllowed())
-			{
-			    tsLogger.arjLogger
-				.debug(
-				    DebugLevel.FUNCTIONS,
-				    VisibilityLevel.VIS_PUBLIC, FacilityCode.FAC_ATOMIC_ACTION,
-				    "Reaper scheduling TX for cancellation " + e._control.get_uid());
-			}
+                        reaperElement._status = ReaperElement.CANCEL_INTERRUPTED;
 
-			// insert into cancellation queue for a worker
-			// thread to process and then make sure a worker
-			// thread is awake
+                        reaperElement._worker.interrupt();
 
-			synchronized (_workQueue)
-			{
-			    _workQueue.add(e);
-			    _workQueue.notifyAll();
-			}
-		    }
-		    break;
-		    case ReaperElement.SCHEDULE_CANCEL:
-		    {
-			// hmm, a worker is taking its time to
-			// start processing this scheduled entry.
-			// we may just be running slow ... but the
-			// worker may be wedged under a cancel for
-			// some other TX. add an extra delay to
-			// give the worker more time to complete
-			// its current task and progress this
-			// entry to the CANCEL state. if the
-			// worker *is* wedged then this will
-			// ensure the wedged TX entry comes to the
-			// front of the queue.
+                        reinsertElement(reaperElement, _cancelFailWaitPeriod);
 
-			synchronized (this)
-			{
-			    _transactions.remove(e);
+                        // log that we interrupted cancel()
 
-			    e.setAbsoluteTimeout((System.currentTimeMillis() + _cancelWaitPeriod));
+                        if (tsLogger.arjLoggerI18N.isDebugEnabled()) {
+                            tsLogger.arjLoggerI18N.debug("com.arjuna.ats.arjuna.coordinator.TransactionReaper_4", new Object[]{reaperElement._control.get_uid()});
+                        }
+                    }
+                    break;
+                    case ReaperElement.CANCEL_INTERRUPTED: {
+                        // cancellation got truly wedged -- mark
+                        // the element as a zombie so the worker
+                        // exits when (if?) it wakes up and create
+                        // a new worker thread to handle further
+                        // cancellations. then mark the
+                        // transaction as rollback only.
 
-			    _transactions.add(e);
-			}
+                        reaperElement._status = ReaperElement.ZOMBIE;
 
-			if (tsLogger.arjLogger.debugAllowed())
-			{
-			    tsLogger.arjLogger
-				.debug(
-				    DebugLevel.FUNCTIONS,
-				    VisibilityLevel.VIS_PUBLIC, FacilityCode.FAC_ATOMIC_ACTION,
-				    "Reaper deferring interrupt for TX scheduled for cancel " + e._control.get_uid());
-			}
-		    }
-		    break;
-		    case ReaperElement.CANCEL:
-		    {
-			// ok, the worker must be wedged under a
-			// call to cancel() -- kick the thread and
-			// reschedule the element for a later
-			// check to ensure the thread responded to
-			// the kick
+                        synchronized (this) {
+                            _zombieCount++;
 
-			e._status = ReaperElement.CANCEL_INTERRUPTED;
+                            if (tsLogger.arjLogger.isDebugEnabled()) {
+                                tsLogger.arjLogger.debug("Reaper " + Thread.currentThread() + " got a zombie " + reaperElement._worker + " (zombie count now " + _zombieCount + ") cancelling " + reaperElement._control.get_uid());
+                            }
 
-			e._worker.interrupt();
+                            if (_zombieCount == _zombieMax) {
+                                // log zombie overflow error call()
 
-			synchronized (this)
-			{
-			    _transactions.remove(e);
+                                if (tsLogger.arjLoggerI18N.isErrorEnabled()) {
+                                    tsLogger.arjLoggerI18N.error(
+                                                    "com.arjuna.ats.arjuna.coordinator.TransactionReaper_5",
+                                                    new Object[]{new Integer(_zombieCount)});
+                                }
+                            }
+                        }
 
-			    e.setAbsoluteTimeout((System.currentTimeMillis() + _cancelFailWaitPeriod));
+                        _reaperWorkerThread = new ReaperWorkerThread(TransactionReaper._theReaper);
+                        _reaperWorkerThread.setDaemon(true);
 
-			    _transactions.add(e);
-			}
+                        _reaperWorkerThread.start();
 
-			// log that we interrupted cancel()
+                        // log a failed cancel()
 
-			if (tsLogger.arjLoggerI18N.isDebugEnabled())
-			{
-			    tsLogger.arjLoggerI18N
-				.debug(
-				    DebugLevel.FUNCTIONS,
-				    VisibilityLevel.VIS_PUBLIC,
-				    FacilityCode.FAC_ATOMIC_ACTION,
-				    "com.arjuna.ats.arjuna.coordinator.TransactionReaper_4",
-				    new Object[]{e._control.get_uid()});
-			}
-		    }
-		    break;
-		    case ReaperElement.CANCEL_INTERRUPTED:
-		    {
-			// cancellation got truly wedged -- mark
-			// the element as a zombie so the worker
-			// exits when (if?) it wakes up and create
-			// a new worker thread to handle further
-			// cancellations. then mark the
-			// transaction as rollback only.
+                        if (tsLogger.arjLoggerI18N.isWarnEnabled()) {
+                            tsLogger.arjLoggerI18N.warn(
+                                            "com.arjuna.ats.arjuna.coordinator.TransactionReaper_6",
+                                            new Object[]{reaperElement._worker,
+                                                    reaperElement._control.get_uid()});
+                        }
 
-			e._status = ReaperElement.ZOMBIE;
+                        // ok, since the worker was wedged we need to
+                        // remove the entry from the timeouts and
+                        // transactions lists then mark this tx as
+                        // rollback only. we have to log a message
+                        // whether we succeed, fail or get interrupted
 
-			synchronized(this)
-			{
-			    _zombieCount++;
+                        removeElementReaper(reaperElement);
 
-			    if (tsLogger.arjLogger.isDebugEnabled())
-			    {
-				tsLogger.arjLogger
-				    .debug(
-					DebugLevel.FUNCTIONS,
-					VisibilityLevel.VIS_PUBLIC,
-					FacilityCode.FAC_ATOMIC_ACTION, "Reaper " + Thread.currentThread() + " got a zombie " + e._worker + " (zombie count now " + _zombieCount + ") cancelling "  + e._control.get_uid());
-			    }
+                        try {
+                            if (reaperElement._control.preventCommit()) {
 
-			    if (_zombieCount == _zombieMax)
-			    {
-				// log zombie overflow error call()
+                                // log a successful preventCommit()
 
-				if (tsLogger.arjLoggerI18N.isErrorEnabled())
-				{
-				    tsLogger.arjLoggerI18N
-					.error(
-					    "com.arjuna.ats.arjuna.coordinator.TransactionReaper_5",
-					    new Object[]{new Integer(_zombieCount)});
-				}
-			    }
-			}
+                                if (tsLogger.arjLoggerI18N.isWarnEnabled()) {
+                                    tsLogger.arjLoggerI18N.warn(
+                                                    "com.arjuna.ats.arjuna.coordinator.TransactionReaper_10",
+                                                    new Object[]{reaperElement._control.get_uid()});
+                                }
 
-			_reaperWorkerThread = new ReaperWorkerThread(TransactionReaper._theReaper);
-			_reaperWorkerThread.setDaemon(true);
+                                notifyListeners(reaperElement._control, false);
+                            } else {
+                                // log a failed preventCommit()
 
-			_reaperWorkerThread.start();
+                                if (tsLogger.arjLoggerI18N.isWarnEnabled()) {
+                                    tsLogger.arjLoggerI18N.warn(
+                                                    "com.arjuna.ats.arjuna.coordinator.TransactionReaper_11",
+                                                    new Object[]{reaperElement._control.get_uid()});
+                                }
+                            }
+                        }
+                        catch (Exception e1) {
+                            // log an exception under preventCommit()
 
-			// log a failed cancel()
+                            if (tsLogger.arjLoggerI18N.isWarnEnabled()) {
+                                tsLogger.arjLoggerI18N
+                                        .warn("com.arjuna.ats.arjuna.coordinator.TransactionReaper_12",
+                                                new Object[]{reaperElement._control.get_uid()}, e1);
+                            }
+                        }
+                    }
+                    break;
+                    case ReaperElement.FAIL:
+                    case ReaperElement.COMPLETE: {
+                        // ok, the worker should remove the tx
+                        // from the transactions queue very soon
+                        // but we need to progress to the next
+                        // entry so we will steal in and do it
+                        // first
 
-			if (tsLogger.arjLoggerI18N.isWarnEnabled())
-			{
-			    tsLogger.arjLoggerI18N
-				.warn(
-				    "com.arjuna.ats.arjuna.coordinator.TransactionReaper_6",
-				    new Object[]{e._worker,
-						 e._control.get_uid()});
-			}
+                        removeElementReaper(reaperElement);
+                    }
+                    break;
 
-			// ok, since the worker was wedged we need to
-			// remove the entry from the timeouts and
-			// transactions lists then mark this tx as
-			// rollback only. we have to log a message
-			// whether we succeed, fail or get interrupted
+                }
+            }
+        } while (true);
 
-			synchronized(this)
-			{
-			    removeElement(e);
-			}
+    }
 
-			try
-			{
-			    if (e._control.preventCommit()) {
+    /**
+     * called by check, this method removes and reinserts an element in the timeout
+     * ordered set, recalculating the next wakeup time accordingly.
+     */
+    private void reinsertElement(ReaperElement e, long delay)
+    {
+        synchronized (this) {
+            long newWakeup = _reaperElements.reorder(e, delay);
+            nextDynamicCheckTime.set(newWakeup); // TODO - set should be atomic with reorder?
+        }
+    }
 
-				// log a successful preventCommit()
-
-				if (tsLogger.arjLoggerI18N.isWarnEnabled())
-				{
-				    tsLogger.arjLoggerI18N
-					.warn(
-					    "com.arjuna.ats.arjuna.coordinator.TransactionReaper_10",
-					    new Object[]{e._control.get_uid()});
-				}
-			    }
-			    else
-			    {
-				// log a failed preventCommit()
-
-				if (tsLogger.arjLoggerI18N.isWarnEnabled())
-				{
-				    tsLogger.arjLoggerI18N
-					.warn(
-					       "com.arjuna.ats.arjuna.coordinator.TransactionReaper_11",
-					       new Object[]{e._control.get_uid()});
-				}
-			    }
-			}
-			catch(Exception e1)
-			{
-			    // log an exception under preventCommit()
-
-			    if (tsLogger.arjLoggerI18N.isWarnEnabled()) {
-				tsLogger.arjLoggerI18N
-				    .warn(
-					"com.arjuna.ats.arjuna.coordinator.TransactionReaper_12",
-					new Object[]{e._control.get_uid()},
-                    e1);
-			    }
-			}
-		    }
-		    break;
-		    case ReaperElement.FAIL:
-		    case ReaperElement.COMPLETE:
-		    {
-			// ok, the worker should remove the tx
-			// from the transactions queue very soon
-			// but we need to progress to the next
-			// entry so we will steal in and do it
-			// first
-
-			synchronized(this)
-			{
-			    removeElement(e);
-			}
-		    }
-		    break;
-
-		    }
-		}
-	    } while(true) ;
-
-	    return true;
-	}
-
-        public final void waitForCancellations()
-        {
-             synchronized(_workQueue)
-             {
-                  try
-                  {
-                       while (_workQueue.isEmpty())
-                       {
-                            _workQueue.wait();
-                       }
-                  }
-                  catch (InterruptedException e)
-                  {
-                  }
-             }
+    public final void waitForCancellations()
+    {
+        synchronized (_workQueue) {
+            try {
+                while (_workQueue.isEmpty()) {
+                    _workQueue.wait();
+                }
+            }
+            catch (InterruptedException e) {
+            }
         }
+    }
 
-	public final void doCancellations()
-        {
-	    for (;;)
-	    {
-		ReaperElement e;
+    public final void doCancellations()
+    {
+        for (; ;) {
+            ReaperElement e;
 
-		// see if we have any cancellations to process
+            // see if we have any cancellations to process
 
-		synchronized(_workQueue)
-		{
-		    try
-		    {
-			e = (ReaperElement)_workQueue.remove(0);
-		    }
-		    catch (IndexOutOfBoundsException ioobe) {break;}
-		}
+            synchronized (_workQueue) {
+                try {
+                    e = _workQueue.remove(0);
+                }
+                catch (IndexOutOfBoundsException ioobe) {
+                    break;
+                }
+            }
 
+            // ok, current status must be SCHEDULE_CANCEL.
+            // progress state to CANCEL and call cancel()
 
-		// ok, current status must be SCHEDULE_CANCEL.
-		// progress state to CANCEL and call cancel()
+            if (tsLogger.arjLogger.isDebugEnabled()) {
+                tsLogger.arjLogger.debug("Reaper Worker " + Thread.currentThread() + " attempting to cancel " + e._control.get_uid());
+            }
 
+            boolean cancelled = false;
+            Exception exception = null;
 
-		if (tsLogger.arjLogger.debugAllowed())
-		{
-		    tsLogger.arjLogger
-			.debug(
-			    DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC,
-			    FacilityCode.FAC_ATOMIC_ACTION, "Reaper Worker " + Thread.currentThread() + " attempting to cancel "  + e._control.get_uid());
-		}
+            synchronized (e) {
+                e._worker = Thread.currentThread();
+                e._status = ReaperElement.CANCEL;
+                e.notifyAll();
+            }
 
-		boolean cancelled = false;
-		Exception exception = null;
+            // we are now exposed to at most one interrupt from
+            // the reaper. test for running and try the cancel if
+            // required
 
-		synchronized(e)
-		{
-		    e._worker = Thread.currentThread();
-		    e._status = ReaperElement.CANCEL;
-		    e.notifyAll();
-		}
+            try {
+                if (e._control.running()) {
 
-		// we are now exposed to at most one interrupt from
-		// the reaper. test for running and try the cancel if
-		// required
+                    // try to cancel the transaction
 
-		try
-		{
-            if (e._control.running()) {
+                    if (e._control.cancel() == ActionStatus.ABORTED) {
+                        cancelled = true;
+                        
+                        if (TxControl.enableStatistics) {
+                            // note that we also count timeouts as application rollbacks via
+                            // the stats unpdate in the TwoPhaseCoordinator cancel() method.
+                            TxStats.incrementTimeouts();
+                        }
 
-                // try to cancel the transaction
-
-                if (e._control.cancel() == ActionStatus.ABORTED)
-                {
-                    cancelled = true;
-
-                    if (TxControl.enableStatistics) {
-                        // note that we also count timeouts as application rollbacks via
-                        // the stats unpdate in the TwoPhaseCoordinator cancel() method.
-                        TxStats.incrementTimeouts();
+                        notifyListeners(e._control, true);
                     }
                 }
             }
-		}
-		catch (Exception e1)
-		{
-		    exception = e1;
-		}
+            catch (Exception e1) {
+                exception = e1;
+            }
 
-		// ok, close the interrupt window by resetting the
-		// state -- unless we have been told to go away by
-		// being set to ZOMBIE
+            // ok, close the interrupt window by resetting the
+            // state -- unless we have been told to go away by
+            // being set to ZOMBIE
 
-		synchronized (e)
-		{
-		    if (e._status == ReaperElement.ZOMBIE)
-		    {
-			// we need to decrement the zombie count and
-			// force an immediate thread exit. the reaper
-			// will have removed the entry from the
-			// transactions list and started another
-			// worker thread.
+            synchronized (e) {
+                if (e._status == ReaperElement.ZOMBIE) {
+                    // we need to decrement the zombie count and
+                    // force an immediate thread exit. the reaper
+                    // will have removed the entry from the
+                    // transactions list and started another
+                    // worker thread.
 
-			ReaperWorkerThread worker = (ReaperWorkerThread)Thread.currentThread();
-			worker.shutdown();
+                    ReaperWorkerThread worker = (ReaperWorkerThread) Thread.currentThread();
+                    worker.shutdown();
 
-			synchronized(this)
-			{
-			    _zombieCount--;
-			}
+                    synchronized (this) {
+                        _zombieCount--;
+                    }
 
-			if (tsLogger.arjLoggerI18N.isWarnEnabled())
-			{
-			    tsLogger.arjLoggerI18N
-				.warn(
-				    "com.arjuna.ats.arjuna.coordinator.TransactionReaper_13",
-				    new Object[]{Thread.currentThread(),
-						 e._control.get_uid(),
-						 new Integer(_zombieCount)});
-			}
+                    if (tsLogger.arjLoggerI18N.isWarnEnabled()) {
+                        tsLogger.arjLoggerI18N
+                                .warn(
+                                        "com.arjuna.ats.arjuna.coordinator.TransactionReaper_13",
+                                        new Object[]{Thread.currentThread(),
+                                                e._control.get_uid(),
+                                                new Integer(_zombieCount)});
+                    }
 
-			// this gets us out of the for(;;) loop and
-			// the shutdown call above makes sure we exit
-			// after returning
+                    // this gets us out of the for(;;) loop and
+                    // the shutdown call above makes sure we exit
+                    // after returning
 
-			break;
-		    }
-		    else if (cancelled &&
-			     e._status == ReaperElement.CANCEL_INTERRUPTED)
-		    {
-			// ok the call to cancel() returned true but
-			// we cannot trust it because the reaper sent
-			// the thread an interrupt
+                    break;
+                } else if (cancelled &&
+                        e._status == ReaperElement.CANCEL_INTERRUPTED) {
+                    // ok the call to cancel() returned true but
+                    // we cannot trust it because the reaper sent
+                    // the thread an interrupt
 
-			cancelled = false;
-			e._status = ReaperElement.FAIL;
-			e.notifyAll();
-		    }
-		    else
-		    {
-			e._status = (cancelled
-				     ? ReaperElement.COMPLETE
-				     : ReaperElement.FAIL);
-			e.notifyAll();
-		    }
-		}
+                    cancelled = false;
+                    e._status = ReaperElement.FAIL;
+                    e.notifyAll();
+                } else {
+                    e._status = (cancelled
+                            ? ReaperElement.COMPLETE
+                            : ReaperElement.FAIL);
+                    e.notifyAll();
+                }
+            }
 
-		// log a message notifying success, failure or
-		// exception during cancel(), remove the element from
-		// the transactions queue and mark TX as rollback only
+            // log a message notifying success, failure or
+            // exception during cancel(), remove the element from
+            // the transactions queue and mark TX as rollback only
 
-		if (cancelled)
-		{
-		    if (tsLogger.arjLoggerI18N.isWarnEnabled())
-		    {
-			tsLogger.arjLoggerI18N
-			    .warn(
-				"com.arjuna.ats.arjuna.coordinator.TransactionReaper_7",
-				new Object[]{Thread.currentThread(),
-					     e._control.get_uid()});
-		    }
-		}
-		else if (e._control.running())
-		{
-		    if (exception != null)
-		    {
-			if (tsLogger.arjLoggerI18N.isWarnEnabled())
-			{
-			    tsLogger.arjLoggerI18N
-				.warn(
-				    "com.arjuna.ats.arjuna.coordinator.TransactionReaper_9",
-				    new Object[]{Thread.currentThread(),
-						 e._control.get_uid()},
-                        exception);
-			}
-		    }
-		    else
-		    {
-			if (tsLogger.arjLoggerI18N.isWarnEnabled())
-			{
-			    tsLogger.arjLoggerI18N
-				.warn(
-				    "com.arjuna.ats.arjuna.coordinator.TransactionReaper_8",
-				    new Object[]{Thread.currentThread(),
-						 e._control.get_uid()});
-			}
-		    }
+            if (cancelled) {
+                if (tsLogger.arjLoggerI18N.isWarnEnabled()) {
+                    tsLogger.arjLoggerI18N
+                            .warn(
+                                    "com.arjuna.ats.arjuna.coordinator.TransactionReaper_7",
+                                    new Object[]{Thread.currentThread(),
+                                            e._control.get_uid()});
+                }
+            } else if (e._control.running()) {
+                if (exception != null) {
+                    if (tsLogger.arjLoggerI18N.isWarnEnabled()) {
+                        tsLogger.arjLoggerI18N
+                                .warn(
+                                        "com.arjuna.ats.arjuna.coordinator.TransactionReaper_9",
+                                        new Object[]{Thread.currentThread(),
+                                                e._control.get_uid()},
+                                        exception);
+                    }
+                } else {
+                    if (tsLogger.arjLoggerI18N.isWarnEnabled()) {
+                        tsLogger.arjLoggerI18N
+                                .warn(
+                                        "com.arjuna.ats.arjuna.coordinator.TransactionReaper_8",
+                                        new Object[]{Thread.currentThread(),
+                                                e._control.get_uid()});
+                    }
+                }
 
-		    try
-		    {
-			if (e._control.preventCommit()) {
-			    // log a successful preventCommit()
+                try {
+                    if (e._control.preventCommit()) {
+                        // log a successful preventCommit()
 
-			    if (tsLogger.arjLoggerI18N.isWarnEnabled())
-			    {
-				tsLogger.arjLoggerI18N
-				    .warn(
-					"com.arjuna.ats.arjuna.coordinator.TransactionReaper_14",
-					new Object[]{Thread.currentThread(),
-						     e._control.get_uid()});
-			    }
-			}
-			else
-			{
-			    // log a failed preventCommit()
+                        if (tsLogger.arjLoggerI18N.isWarnEnabled()) {
+                            tsLogger.arjLoggerI18N
+                                    .warn(
+                                            "com.arjuna.ats.arjuna.coordinator.TransactionReaper_14",
+                                            new Object[]{Thread.currentThread(),
+                                                    e._control.get_uid()});
+                        }
 
-			    if (tsLogger.arjLoggerI18N.isWarnEnabled())
-			    {
-				tsLogger.arjLoggerI18N
-				    .warn(
-					"com.arjuna.ats.arjuna.coordinator.TransactionReaper_15",
-					new Object[]{Thread.currentThread(),
-						     e._control.get_uid()});
-			    }
-			}
-		    }
-		    catch(Exception e1)
-		    {
-			// log an exception under preventCommit()
+                        notifyListeners(e._control, false);
+                    } else {
+                        // log a failed preventCommit()
 
-			if (tsLogger.arjLoggerI18N.isWarnEnabled()) {
-			    tsLogger.arjLoggerI18N
-				.warn(
-				    "com.arjuna.ats.arjuna.coordinator.TransactionReaper_16",
-				    new Object[]{Thread.currentThread(),
-						 e._control.get_uid()},
-                    e1);
-			}
-		    }
-		}
+                        if (tsLogger.arjLoggerI18N.isWarnEnabled()) {
+                            tsLogger.arjLoggerI18N
+                                    .warn(
+                                            "com.arjuna.ats.arjuna.coordinator.TransactionReaper_15",
+                                            new Object[]{Thread.currentThread(),
+                                                    e._control.get_uid()});
+                        }
+                    }
+                }
+                catch (Exception e1) {
+                    // log an exception under preventCommit()
 
-		synchronized(this)
-		{
-			removeElement(e);
-		}
+                    if (tsLogger.arjLoggerI18N.isWarnEnabled()) {
+                        tsLogger.arjLoggerI18N
+                                .warn(
+                                        "com.arjuna.ats.arjuna.coordinator.TransactionReaper_16",
+                                        new Object[]{Thread.currentThread(),
+                                                e._control.get_uid()},
+                                        e1);
+                    }
+                }
+            }
 
-	    }
+            removeElementReaper(e);
         }
+    }
 
-	/**
-	 * @return the number of items in the reaper's list.
-	 * @since JTS 2.2.
-	 */
+    /**
+     * @return the number of items in the reaper's list.
+     * @since JTS 2.2.
+     *
+     * Note: this is a) expensive and b) an approximation. Should be called only by test code.
+     */
+    public final long numberOfTransactions()
+    {
+        return _reaperElements.size();
+    }
 
-	public final long numberOfTransactions()
-	{
-		return _transactions.size() + _pendingInsertions.size();
-	}
-
-        /**
-         * Return the number of timeouts registered.
-         * @return The number of timeouts registered.
-         */
-        public final long numberOfTimeouts()
-        {
-                return _timeouts.size() + _pendingInsertions.size();
-        }
-
-
     /**
+     * Return the number of timeouts registered.
+     * Note: this is a) expensive and b) an approximation. Should be called only by test code.
+     *
+     * @return The number of timeouts registered.
+     */
+    public final long numberOfTimeouts()
+    {
+        return _timeouts.size();
+    }
+    
+    /**
      * timeout is given in seconds, but we work in milliseconds.
+     *
+     * Attempting to insert an element that is already present is an error (IllegalStateException)
      */
-    public final boolean insert(Reapable control, int timeout)
+    public final void insert(Reapable control, int timeout)
     {
-        if (tsLogger.arjLogger.debugAllowed())
-        {
-            tsLogger.arjLogger.debug(DebugLevel.FUNCTIONS,
-                    VisibilityLevel.VIS_PUBLIC, FacilityCode.FAC_ATOMIC_ACTION,
-                    "TransactionReaper::insert ( " + control + ", " + timeout
-                            + " )");
+        if (tsLogger.arjLogger.isDebugEnabled()) {
+            tsLogger.arjLogger.debug("TransactionReaper::insert ( " + control + ", " + timeout
+                    + " )");
         }
 
         /*
          * Ignore if the timeout is zero, since this means the transaction
          * should never timeout.
          */
-
         if (timeout == 0)
-            return true;
+            return;
 
+        ReaperElement reaperElement = new ReaperElement(control, timeout);
 
+        _lifetime.addAndGet(timeout);
 
-        ReaperElement e = new ReaperElement(control, timeout);
+        // insert the element only if it's not already present. We check _timeouts first, as elements
+        // maybe temporarily removed and reinserted in _reaperElements, so that is not as good a check.
+        // We use lazy eval to ensure we insert to _reaperElements only if we inserted to _timeouts.
+        // Note: removal works in reverse order i.e. _reaperElements then _timeouts.
+        if ((_timeouts.putIfAbsent(reaperElement._control, reaperElement) == null)) {
+            _reaperElements.add(reaperElement);
+        } else {
+            throw new IllegalStateException(tsLogger.arjLoggerI18N.getString("com.arjuna.ats.arjuna.coordinator.TransactionReaper_1"));
+        }
 
-        boolean asyncInsert = false;
+        if (_dynamic && reaperElement.getAbsoluteTimeout() < nextDynamicCheckTime.get()) {
+            updateCheckTimeForEarlierInsert(reaperElement.getAbsoluteTimeout());
+        }
+    }
 
-        synchronized (this)
-        {
-            if(_transactions.size() > 0) {
-                ReaperElement first = (ReaperElement)_transactions.first();
-                // if the new element would timeout after the earliest one we already have,
-                // we can delay its insertion until that earlier timeout. Hopefully the new tx will
-                // complete before then and we'll never have to insert it at all.
-                if (first != null && e.compareTo(first) > 0) {
-                    // first make sure we have not seen this control already
-                    if (_timeouts.containsKey(control)) {
-                        // hmm, this probably means that the hash or equals implementation on the element has been
-                        // coded wrong
-                        return false;
-                    }
-                    // put it in in the pending list for later insertion but also
-                    // check the return value in case this entry already exists
-                    ReaperElement old = _pendingInsertions.put(control, e);
-                    if (old != null) {
-                        // hmm, this probably means that the hash or equals implementation on the element has been
-                        // coded wrong -- restore the old entry and return false. n.b. checking for a duplicate
-                        //  this way avoids having to do a containsKey test in the normal case.
-                        _pendingInsertions.put(control, old);
-                        return false;
-                    }
-                    asyncInsert = true;
+    /**
+     * Reset the next wakeup time, when a new element has a timeout earlier than the currently scheduled wakeup.
+     *
+     * @param newCheckTime absolute time in ms.
+     */
+    private void updateCheckTimeForEarlierInsert(long newCheckTime)
+    {
+        synchronized (this) {
+            long oldCheckTime = nextDynamicCheckTime.get();
+            while (newCheckTime < oldCheckTime) {
+                if (nextDynamicCheckTime.compareAndSet(oldCheckTime, newCheckTime)) {
+                    notifyAll(); // force recalc of next wakeup time, taking into account the newly inserted element(s)
+                } else {
+                    oldCheckTime = nextDynamicCheckTime.get();
                 }
             }
-
-            if(asyncInsert) {
-                return true;
-            } else {
-                return synchronousInsert(e);
-            }
         }
     }
 
-
-	private final boolean synchronousInsert(ReaperElement elementToInsert)
-	{
-		synchronized (this)
-		{
-			TransactionReaper._lifetime += elementToInsert._timeout;
-
-			ReaperElement old = (ReaperElement)_timeouts.put(elementToInsert._control, elementToInsert);
-            if (old != null) {
-                // hmm, this probably means that the hash or equals implementation on the element has been
-                // coded wrong -- restore the old entry and return false. n.b. checking for a duplicate
-                //  this way avoids having to do a containsKey test in the normal case.
-                _timeouts.put(elementToInsert._control, old);
-                return false;
-            }
-
-            // we should not get an error here unless the user has coded the compareTo test wrong
-
-			boolean rtn = _transactions.add(elementToInsert);
-
-			if(_dynamic && _transactions.first() == elementToInsert)
-			{
-				notifyAll(); // force recalc of next wakeup time, taking into account the newly inserted element
-			}
-
-			return rtn;
-		}
-	}
-
-    public final boolean remove(java.lang.Object control)
+    // takes an Object because OTSManager.destroyControl(Control|ControlImple) uses PseudoControlWrapper not Reapable
+    public final void remove(Object control)
     {
-        // _pendingInsertions is a concurrent structure, so we don't lock it here. That means we need to be careful
-        // when transferring elements from pending to the real structures, see check() above. The synchronous remove
-        // must also take care to lock, or things may leak if a remove attempt happens concurrent to a pending copy. 
-
-        if(_pendingInsertions.remove(control) != null) {
-            return true;
-        } else {
-            return synchronousRemove(control);
+        if (tsLogger.arjLogger.isDebugEnabled()) {
+            tsLogger.arjLogger.debug("TransactionReaper::remove ( " + control + " )");
         }
-    }
 
-	public final boolean synchronousRemove(java.lang.Object control)
-	{
-		if (tsLogger.arjLogger.debugAllowed())
-		{
-			tsLogger.arjLogger.debug(DebugLevel.FUNCTIONS,
-					VisibilityLevel.VIS_PUBLIC, FacilityCode.FAC_ATOMIC_ACTION,
-					"TransactionReaper::remove ( " + control + " )");
-		}
+        if (control == null)
+            return;
 
-		if (control == null)
-			return false;
+        ReaperElement key = _timeouts.get(control);
+        if (key == null) {
+            return;
+        }
 
-		ReaperElement key;
+        // if a cancellation is in progress then we have to
+        // see it through as we have to ensure that the worker
+        // thread does not get wedged. so we have to tell the
+        // control has gone away. in order to test the status
+        // we need to synchronize on the element before we
+        // synchronize on this so we can ensure that we don't
+        // deadlock ourselves.
 
-                synchronized(this)
-                {
-                    key = (ReaperElement)_timeouts.remove(control);
-                    if(key == null) {
-                            return false;
-                    }
-		}
+        synchronized (key) {
+            if (key._status != ReaperElement.RUN) {
+                // we are cancelling this TX anyway and need
+                // to track the progress of the cancellation
+                // using this entry so we cnanot remove it
+                return;
+            }
 
-		// if a cancellation is in progress then we have to
-		// see it through as we have to ensure that the worker
-		// thread does not get wedged. so we have to tell the
-		// control has gone away. in order to test the status
-		// we need to synchronize on the element before we
-		// synchronize on this so we can ensure that we don't
-		// deadlock ourselves.
-
-		synchronized(key)
-		{
-                    if (key._status != ReaperElement.RUN)
-                    {
-                         // we are cancelling this TX anyway and need
-                         // to track the progress of the cancellation
-                         // using this entry so we cnanot remove it
-
-                         return false;
-                    }
-
-		    synchronized(this)
-		    {
-		        removeElement(key);
-
-		        return true;
-		    }
+            removeElementClient(key);
         }
-	}
+    }
 
     /**
-	 * Given the transaction instance, this will return the time left before the
-	 * transaction is automatically rolled back if it has not been terminated.
-	 *
-	 * @param control
-	 * @return the remaining time in milliseconds.
-	 */
+     * Given the transaction instance, this will return the time left before the
+     * transaction is automatically rolled back if it has not been terminated.
+     *
+     * @param control
+     * @return the remaining time in milliseconds.
+     */
+    public final long getRemainingTimeoutMills(Object control)
+    {
+        // arg is an Object because ArjunaTransactionImple.propagationContext does not have a Reapable
 
-	public final long getRemainingTimeoutMills(Object control)
-	{
-        if ((_transactions.size() == 0) || (control == null))
-        {
-            if (tsLogger.arjLogger.debugAllowed())
-            {
-                tsLogger.arjLogger.debug(DebugLevel.FUNCTIONS,
-                        VisibilityLevel.VIS_PUBLIC,
-                        FacilityCode.FAC_ATOMIC_ACTION,
-                        "TransactionReaper::getRemainingTimeout for " + control
-                                + " returning 0");
+        if ((_timeouts.isEmpty()) || (control == null)) {
+            if (tsLogger.arjLogger.isDebugEnabled()) {
+                tsLogger.arjLogger.debug("TransactionReaper::getRemainingTimeout for " + control
+                        + " returning 0");
             }
 
             return 0;
         }
 
-        final ReaperElement reaperElement = (ReaperElement)_timeouts.get(control);
+        final ReaperElement reaperElement = _timeouts.get(control);
         long timeout = 0;
 
-        if (reaperElement == null)
-        {
+        if (reaperElement == null) {
             timeout = 0;
-        }
-        else
-        {
+        } else {
             // units are in milliseconds at this stage.
             timeout = reaperElement.getAbsoluteTimeout() - System.currentTimeMillis();
         }
 
         if (tsLogger.arjLoggerI18N.isDebugEnabled()) {
-            tsLogger.arjLoggerI18N
-                    .debug(
-                            DebugLevel.FUNCTIONS,
-                            VisibilityLevel.VIS_PUBLIC,
-                            FacilityCode.FAC_ATOMIC_ACTION,
-                            "com.arjuna.ats.arjuna.coordinator.TransactionReaper_17",
-                            new Object[]
-                                    { control, timeout });
+            tsLogger.arjLoggerI18N.debug("com.arjuna.ats.arjuna.coordinator.TransactionReaper_17", new Object[]
+                    {control, timeout});
         }
 
         return timeout;
-	}
+    }
 
-	/**
-	 * Given a Control, return the associated timeout, or 0 if we do not know
-	 * about it.
-	 *
-	 * Return in seconds!
-	 */
+    /**
+     * Given a Control, return the associated timeout, or 0 if we do not know
+     * about it.
+     * <p/>
+     * Return in seconds!
+     *
+     * Takes an Object because TransactionFactoryImple.getTransactionInfo and
+     * ArjunaTransactionImple.propagationContext use it and don't have a Reapable.
+     */
+    public final int getTimeout(Object control)
+    {
+        if ((_timeouts.isEmpty()) || (control == null)) {
+            if (tsLogger.arjLogger.isDebugEnabled()) {
+                tsLogger.arjLogger.debug("TransactionReaper::getTimeout for " + control
+                        + " returning 0");
+            }
 
-	public final int getTimeout(Object control)
-	{
-		if ((_transactions.size() == 0) || (control == null))
-		{
-			if (tsLogger.arjLogger.debugAllowed())
-			{
-				tsLogger.arjLogger.debug(DebugLevel.FUNCTIONS,
-						VisibilityLevel.VIS_PUBLIC,
-						FacilityCode.FAC_ATOMIC_ACTION,
-						"TransactionReaper::getTimeout for " + control
-								+ " returning 0");
-			}
+            return 0;
+        }
 
-			return 0;
-		}
+        final ReaperElement reaperElement = _timeouts.get(control);
 
-		final ReaperElement reaperElement = (ReaperElement)_timeouts.get(control);
+        int timeout = (reaperElement == null ? 0 : reaperElement._timeout);
 
-                final Integer timeout ;
-		if(reaperElement == null) {
-			timeout = new Integer(0);
-		} else {
-		        timeout = new Integer(reaperElement._timeout) ;
-                }
+        tsLogger.arjLoggerI18N.debug("com.arjuna.ats.arjuna.coordinator.TransactionReaper_3", new Object[]
+                {control, timeout});
 
-		tsLogger.arjLoggerI18N
-				.debug(
-						DebugLevel.FUNCTIONS,
-						VisibilityLevel.VIS_PUBLIC,
-						FacilityCode.FAC_ATOMIC_ACTION,
-						"com.arjuna.ats.arjuna.coordinator.TransactionReaper_3",
-						new Object[]
-								{ control, timeout });
+        return timeout;
+    }
 
-		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.
+    */
 
-	/*
-         * 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)
-	{
+    private final void shutdown(boolean waitForTransactions)
+    {
         // the reaper thread synchronizes and waits on this
 
-	    synchronized (this)
-	    {
-	        _inShutdown = true;
+        synchronized (this) {
+            _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 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;
+            if (!waitForTransactions) {
+                _reaperElements.setAllTimeoutsToZero();
+            }
 
-	            while (iter.hasNext())
-	            {
-	                e = (ReaperElement) iter.next();
+            /*
+                * Wait for all of the transactions to
+                * terminate normally.
+                */
+            while (!_reaperElements.isEmpty()) {
+                try {
+                    this.wait();
+                }
+                catch (final Exception ex) {
+                }
+            }
 
-	                e.setAbsoluteTimeout(0);
-	            }
-	        }
-
-	        /*
-	         * Wait for all of the transactions to
-	         * terminate normally.
-	         */
-
-	        while (_transactions.size() > 0)
-	        {
-	            try
-	            {
-	                this.wait();
-	            }
-	            catch (final Exception ex)
-	            {
-	            }
-	        }
-
-
             _reaperThread.shutdown();
 
             notifyAll();
         }
-        try
-        {
+        try {
             _reaperThread.join();
         }
-        catch (final Exception ex)
-        {
+        catch (final Exception ex) {
         }
 
         _reaperThread = null;
 
         // the reaper worker thread synchronizes and wais on the work queue
 
-        synchronized(_workQueue) {
+        synchronized (_workQueue) {
             _reaperWorkerThread.shutdown();
             _workQueue.notifyAll();
             // hmm, not sure we really need to do this but . . .
             _reaperWorkerThread.interrupt();
         }
 
-        try
-        {
+        try {
             _reaperWorkerThread.join();
         }
-        catch (final Exception ex)
-        {
+        catch (final Exception ex) {
         }
 
         _reaperWorkerThread = null;
-	}
+    }
 
-	/*
-	 * Remove element from list and trigger waiter if we are
-	 * being shutdown.
-	 *
-	 * n.b. must only be called when synchronized on this
-	 */
+    // called (indirectly) by user code doing removals on e.g. commit/rollback
+    // does not reset the wakeup time - we prefer leaving an unnecessary wakeup as it's
+    // cheaper than locking to recalculate the new time here.
+    private final void removeElementClient(ReaperElement reaperElement)
+    {
+        _reaperElements.remove(reaperElement);        
+        _timeouts.remove(reaperElement._control);
 
-	private final void removeElement (ReaperElement e)
-	{
-        _timeouts.remove(e._control);
-        _transactions.remove(e);
+        // don't recalc time, just wake up as planned
 
-        if (_inShutdown && (_transactions.size() == 0))
-        {
-            this.notifyAll();
+        if(_inShutdown) {
+            synchronized (this) {
+                this.notifyAll(); // TODO: use different lock for shutdown?
+            }
         }
-	}
+    }
 
-	/**
-	 * Currently we let the reaper thread run at same priority as other threads.
-	 * Could get priority from environment.
-	 */
+    /*
+      * Remove element from list and trigger waiter if we are
+      * being shutdown.
+      *
+      */
+    // called internally by the reaper when removing elements - note the different
+    // behaviour with regard to check time recalculation. Here we need to ensure the
+    // new time is correct.
+    private final void removeElementReaper(ReaperElement reaperElement)
+    {
+        _reaperElements.remove(reaperElement);
+        _timeouts.remove(reaperElement._control);
 
-	public static synchronized TransactionReaper create(long checkPeriod)
-	{
-		if (tsLogger.arjLogger.debugAllowed())
-		{
-			tsLogger.arjLogger.debug(DebugLevel.FUNCTIONS,
-					VisibilityLevel.VIS_PUBLIC, FacilityCode.FAC_ATOMIC_ACTION,
-					"TransactionReaper::create ( " + checkPeriod + " )");
-		}
+        synchronized (this) {
 
-		if (TransactionReaper._theReaper == null)
-		{
+            // TODO set needs tobe atomic to getFirst?
+            ReaperElement first = _reaperElements.getFirst();
+            if(first != null) {
+                nextDynamicCheckTime.set(first.getAbsoluteTimeout());
+            } else {
+                nextDynamicCheckTime.set(Long.MAX_VALUE);
+                if(_inShutdown) {
+                    this.notifyAll(); // TODO: use different lock for shutdown?
+                }
+            }
+        }
+    }
+
+
+
+    private final void notifyListeners(Reapable element, boolean rollback)
+    {
+        // notify listeners. Ignore errors.
+
+        for (int i = 0; i < _listeners.size(); i++) {
+            try {
+                if (rollback)
+                    _listeners.get(i).rolledBack(element.get_uid());
+                else
+                    _listeners.get(i).markedRollbackOnly(element.get_uid());
+            }
+            catch (final Throwable ex) {
+                // ignore
+            }
+        }
+    }
+
+    /**
+     * Currently we let the reaper thread run at same priority as other threads.
+     * Could get priority from environment.
+     */
+    public static synchronized void instantiate()
+    {
+        if (TransactionReaper._theReaper == null)
+        {
+            if (tsLogger.arjLogger.isDebugEnabled()) {
+                tsLogger.arjLogger.debug("TransactionReaper::instantiate()");
+            }
+
             // default to dynamic mode
             TransactionReaper._dynamic = true;
 
-            String mode = arjPropertyManager.propertyManager
-					.getProperty(Environment.TX_REAPER_MODE);
-
-			if (mode != null)
+            String mode = arjPropertyManager.propertyManager.getProperty(Environment.TX_REAPER_MODE);
+            
+            if (mode != null)
 			{
 				if (mode.compareTo(TransactionReaper.PERIODIC) == 0) {
 					TransactionReaper._dynamic = false;
@@ -1162,246 +938,234 @@
                 }
             }
 
-            if (!TransactionReaper._dynamic)
-			{
-				String timeoutEnv = arjPropertyManager.propertyManager
-						.getProperty(Environment.TX_REAPER_TIMEOUT);
+            long checkPeriod = Long.MAX_VALUE;
+            if (!TransactionReaper._dynamic) 
+            {
+            	String timeoutEnv = arjPropertyManager.propertyManager
+				.getProperty(Environment.TX_REAPER_TIMEOUT);
 
 				if (timeoutEnv != null)
 				{
 					Long l = null;
-
+		
 					try
 					{
 						l = new Long(timeoutEnv);
 						checkPeriod = l.longValue();
-
+		
 						l = null;
 					}
 					catch (NumberFormatException e)
 					{
-                        if (tsLogger.arjLogger.isWarnEnabled()) {
-                        tsLogger.arjLogger.warn("TransactionReaper::create - "
+		                if (tsLogger.arjLogger.isWarnEnabled()) {
+		                tsLogger.arjLogger.warn("TransactionReaper::create - "
 								+ e);
-                        }
-                    }
+		                }
+		            }
 				}
-                else
-                {
-                    checkPeriod = defaultCheckPeriod;
-                }
-			}
-			else
-				checkPeriod = Long.MAX_VALUE;
+		        else
+		        {
+		            checkPeriod = defaultCheckPeriod;
+		        }
+            }
+            TransactionReaper._theReaper = new TransactionReaper(checkPeriod);
 
-			TransactionReaper._theReaper = new TransactionReaper(checkPeriod);
-
-			String cancelWait = arjPropertyManager.propertyManager
-					.getProperty(Environment.TX_REAPER_CANCEL_WAIT_PERIOD);
+            String cancelWait = arjPropertyManager.propertyManager
+				.getProperty(Environment.TX_REAPER_CANCEL_WAIT_PERIOD);
+            
 			if (cancelWait != null)
 			{
-                             try
-                             {
-				 TransactionReaper._theReaper._cancelWaitPeriod = Long.valueOf(cancelWait).longValue();
-                             }
-                             catch (NumberFormatException e)
-                             {
-                                  TransactionReaper._theReaper._cancelWaitPeriod = defaultCancelWaitPeriod;
-                             }
+                 try
+                 {
+                	 TransactionReaper._theReaper._cancelWaitPeriod = Long.valueOf(cancelWait).longValue();
+                 }
+                 catch (NumberFormatException e)
+                 {
+                      TransactionReaper._theReaper._cancelWaitPeriod = defaultCancelWaitPeriod;
+                 }
 
 			     // must give TX at least 10 millisecs to
 			     // respond to cancel
 
-                             if (TransactionReaper._theReaper._cancelWaitPeriod < 10) {
-                                  TransactionReaper._theReaper._cancelWaitPeriod = 10;
-                             }
+                 if (TransactionReaper._theReaper._cancelWaitPeriod < 10) {
+                      TransactionReaper._theReaper._cancelWaitPeriod = 10;
+                 }
 			}
-                        else
-                        {
-                             TransactionReaper._theReaper._cancelWaitPeriod = defaultCancelWaitPeriod;
-                        }
+            else
+            {
+                 TransactionReaper._theReaper._cancelWaitPeriod = defaultCancelWaitPeriod;
+            }
 
 			String cancelFailWait = arjPropertyManager.propertyManager
-					.getProperty(Environment.TX_REAPER_CANCEL_FAIL_WAIT_PERIOD);
+				.getProperty(Environment.TX_REAPER_CANCEL_FAIL_WAIT_PERIOD);
 			if (cancelFailWait != null)
 			{
-                             try
-                             {
-                                  TransactionReaper._theReaper._cancelFailWaitPeriod = Long.valueOf(cancelFailWait).longValue();
-                             }
-                             catch (NumberFormatException e)
-                             {
-                                  TransactionReaper._theReaper._cancelFailWaitPeriod = defaultCancelFailWaitPeriod;
-                             }
+	            try
+	            {
+	                TransactionReaper._theReaper._cancelFailWaitPeriod = Long.valueOf(cancelFailWait).longValue();
+	            }
+	            catch (NumberFormatException e)
+	            {
+	                TransactionReaper._theReaper._cancelFailWaitPeriod = defaultCancelFailWaitPeriod;
+	            }
+				// must give TX at least 10 millisecs to
+				// respond to cancel
 
-			     // must give TX at least 10 millisecs to
-			     // respond to cancel
-
-                             if (TransactionReaper._theReaper._cancelFailWaitPeriod < 10) {
-                                  TransactionReaper._theReaper._cancelFailWaitPeriod = 10;
-                             }
+	            if (TransactionReaper._theReaper._cancelFailWaitPeriod < 10) 
+	            {
+	               TransactionReaper._theReaper._cancelFailWaitPeriod = 10;
+	            }
 			}
-                        else
-                        {
-                             TransactionReaper._theReaper._cancelFailWaitPeriod = defaultCancelFailWaitPeriod;
-                        }
+            else
+            {
+                 TransactionReaper._theReaper._cancelFailWaitPeriod = defaultCancelFailWaitPeriod;
+            }
 
 			String zombieMax = arjPropertyManager.propertyManager
-					.getProperty(Environment.TX_REAPER_ZOMBIE_MAX);
+				.getProperty(Environment.TX_REAPER_ZOMBIE_MAX);
 			if (zombieMax != null)
 			{
-                             try
-                             {
-                                  TransactionReaper._theReaper._zombieMax = Integer.valueOf(zombieMax).intValue();
-                             }
-                             catch (NumberFormatException e)
-                             {
-                                  TransactionReaper._theReaper._zombieMax = defaultZombieMax;
-                             }
-			     // we start bleating if the zombie count
-			     // reaches zombieMax so it has to be at
-			     // least 1
-
-                             if (TransactionReaper._theReaper._zombieMax <= 0) {
-                                  TransactionReaper._theReaper._zombieMax = 1;
-                             }
+	            try
+	            {
+	               TransactionReaper._theReaper._zombieMax = Integer.valueOf(zombieMax).intValue();
+	            }
+	            catch (NumberFormatException e)
+	            {
+	               TransactionReaper._theReaper._zombieMax = defaultZombieMax;
+	            }
+	            // we start bleating if the zombie count
+	            // reaches zombieMax so it has to be at
+				// least 1
+	
+	            if (TransactionReaper._theReaper._zombieMax <= 0) {
+	               TransactionReaper._theReaper._zombieMax = 1;
+	            }
 			}
-                        else
-                        {
-                             TransactionReaper._theReaper._zombieMax = defaultZombieMax;
-                        }
+            else
+            {
+                 TransactionReaper._theReaper._zombieMax = defaultZombieMax;
+            }
+			TransactionReaper._theReaper._cancelWaitPeriod = defaultCancelWaitPeriod;
+            TransactionReaper._theReaper._cancelFailWaitPeriod = defaultCancelFailWaitPeriod;
+            TransactionReaper._theReaper._zombieMax = defaultZombieMax;
 
-                        // use defaults for now
-
-                        TransactionReaper._theReaper._cancelWaitPeriod = defaultCancelWaitPeriod;
-                        TransactionReaper._theReaper._cancelFailWaitPeriod = defaultCancelFailWaitPeriod;
-                        TransactionReaper._theReaper._zombieMax = defaultZombieMax;
-
 			_reaperThread = new ReaperThread(TransactionReaper._theReaper);
-			// _reaperThread.setPriority(Thread.MIN_PRIORITY);
+            // _reaperThread.setPriority(Thread.MIN_PRIORITY);
 
-			_reaperThread.setDaemon(true);
+            _reaperThread.setDaemon(true);
 
-			_reaperWorkerThread = new ReaperWorkerThread(TransactionReaper._theReaper);
-			_reaperWorkerThread.setDaemon(true);
+            _reaperWorkerThread = new ReaperWorkerThread(TransactionReaper._theReaper);
+            _reaperWorkerThread.setDaemon(true);
 
-			_reaperThread.start();
+            _reaperThread.start();
 
-			_reaperWorkerThread.start();
-		}
+            _reaperWorkerThread.start();
+        }
+    }
 
-		return TransactionReaper._theReaper;
-	}
+    /**
+     * Starting with 4.8, this method will always return an instance, will never return null.
+     * This causes the reaper to be instantiated unnecessarily in some cases, but that's cheaper
+     * than the alternatives.
+     *
+     * @return a TransactionReaper singleton.
+     */
+    public static TransactionReaper transactionReaper() {
+        if(_theReaper == null) {
+            instantiate();
+        }
+        return _theReaper;
+    }
 
-	public static TransactionReaper create()
-	{
-		return create(TransactionReaper.defaultCheckPeriod);
-	}
+    /**
+     * Terminate the transaction reaper. This is a synchronous operation
+     * and will only return once the reaper has been shutdown cleanly.
+     * <p/>
+     * 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 TransactionReaper transactionReaper()
-	{
-		return transactionReaper(false);
-	}
+    public static synchronized void terminate(boolean waitForTransactions)
+    {
+        if (_theReaper != null) {
+            _theReaper.shutdown(waitForTransactions);
+            _theReaper = null;
+        }
+    }
 
-	/*
-	 * If parameter is true then do a create.
-	 */
-
-	public static synchronized TransactionReaper transactionReaper(
-			boolean createReaper)
-	{
-		if (createReaper)
-			return create();
-		else
-			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 synchronized void terminate (boolean waitForTransactions)
-	{
-	    if (_theReaper != null)
-	    {
-	        _theReaper.shutdown(waitForTransactions);
-	        _theReaper = null;
-	    }
-	}
-
-    public static boolean isDynamic() {
+    public static boolean isDynamic()
+    {
         return _dynamic;
     }
 
-    /*
-	 * Don't bother synchronizing as this is only an estimate anyway.
-	 */
+    public static synchronized long transactionLifetime()
+    {
+        return _lifetime.get();
+    }
 
-	public static final synchronized long transactionLifetime()
-	{
-		return TransactionReaper._lifetime;
-	}
+    public static final long defaultCheckPeriod = 120000; // in milliseconds
+    public static final long defaultCancelWaitPeriod = 500; // in milliseconds
+    public static final long defaultCancelFailWaitPeriod = 500; // in milliseconds
+    public static final int defaultZombieMax = 8;
 
-	public static final long defaultCheckPeriod = 120000; // in milliseconds
-	public static final long defaultCancelWaitPeriod = 500; // in milliseconds
-	public static final long defaultCancelFailWaitPeriod = 500; // in milliseconds
-	public static final int defaultZombieMax = 8;
+    static final synchronized void reset()
+    {
+        _theReaper = null;
+    }
 
-	static final void reset()
-	{
-		_theReaper = null;
-	}
+    private final ReaperElementManager _reaperElements = new ReaperElementManager();
 
-	private SortedSet _transactions = Collections.synchronizedSortedSet(new TreeSet()); // C of ReaperElement
-	private Map _timeouts = Collections.synchronizedMap(new HashMap()); // key = Reapable, value = ReaperElement
+    // The keys are actually Reapable, as that's what insert takes. However, some functions use get(Object)
+    // and rely on clever hashcode/equals behaviour, especially for the JTS. Thus the generics key type is Object.
+    private final ConcurrentMap<Object, ReaperElement> _timeouts = new ConcurrentHashMap<Object, ReaperElement>();
 
-    private final Map<Reapable, ReaperElement> _pendingInsertions = new ConcurrentHashMap<Reapable, ReaperElement>();
+    private final List<ReaperElement> _workQueue = new LinkedList<ReaperElement>();
 
-	private List _workQueue = new LinkedList(); // C of ReaperElement
+    private final Vector<ReaperMonitor> _listeners = new Vector<ReaperMonitor>(); // TODO sync properly
 
-	private long _checkPeriod = 0;
+    private long _checkPeriod = 0;
 
-        /**
-	 * number of millisecs delay afer a cancel() is scheduled
-	 * before the reaper tries to interrupt the worker thread
-	 * executing the cancel()
-	 */
-	private long _cancelWaitPeriod = 0;
+    // Although it is atomic, writes (but not reads) need to by synchronized(this) i.e. on the TransactionReaper instance
+    // in order to ensure proper timing with respect to wait/notify and wakeups on the _reaperElements queue.
+    private final AtomicLong nextDynamicCheckTime = new AtomicLong(Long.MAX_VALUE);
 
-        /**
-	 * number of millisecs delay afer a worker thread is
-	 * interrupted before the reaper writes the it off as a zombie
-	 * and starts a new thread
-	 */
-	private long _cancelFailWaitPeriod = 0;
+    /**
+     * number of millisecs delay afer a cancel() is scheduled
+     * before the reaper tries to interrupt the worker thread
+     * executing the cancel()
+     */
+    private long _cancelWaitPeriod = 0;
 
-        /**
-	 * threshold for count of non-exited zombies at which system
-	 * starts logging error messages
-	 */
-	private int _zombieMax = 0;
+    /**
+     * number of millisecs delay afer a worker thread is
+     * interrupted before the reaper writes the it off as a zombie
+     * and starts a new thread
+     */
+    private long _cancelFailWaitPeriod = 0;
 
-	private static TransactionReaper _theReaper = null;
+    /**
+     * threshold for count of non-exited zombies at which system
+     * starts logging error messages
+     */
+    private int _zombieMax = 0;
 
-	private static ReaperThread _reaperThread = null;
+    private static volatile TransactionReaper _theReaper = null;
 
-	private static ReaperWorkerThread _reaperWorkerThread = null;
+    private static ReaperThread _reaperThread = null;
 
-	private static boolean _dynamic = true;
+    private static ReaperWorkerThread _reaperWorkerThread = null;
 
-	private static long _lifetime = 0;
+    private static boolean _dynamic = true;
 
-	private static int _zombieCount = 0;
+    private static AtomicLong _lifetime = new AtomicLong(0);
 
+    private static int _zombieCount = 0;
+
 	private boolean _inShutdown = false;
 }

Modified: labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/coordinator/ReaperElement.java
===================================================================
--- labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/coordinator/ReaperElement.java	2010-05-13 11:36:42 UTC (rev 32860)
+++ labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/coordinator/ReaperElement.java	2010-05-13 11:44:31 UTC (rev 32861)
@@ -31,13 +31,15 @@
 
 package com.arjuna.ats.internal.arjuna.coordinator;
 
-import com.arjuna.common.util.logging.*;
+import java.util.concurrent.atomic.AtomicInteger;
 
-import com.arjuna.ats.arjuna.logging.tsLogger;
 import com.arjuna.ats.arjuna.coordinator.Reapable;
 import com.arjuna.ats.arjuna.logging.FacilityCode;
+import com.arjuna.ats.arjuna.logging.tsLogger;
+import com.arjuna.common.util.logging.DebugLevel;
+import com.arjuna.common.util.logging.VisibilityLevel;
 
-public class ReaperElement implements Comparable
+public class ReaperElement implements Comparable<ReaperElement>
 {
 
     /*
@@ -48,7 +50,7 @@
 
 	public ReaperElement(Reapable control, int timeout)
 	{
-		if (tsLogger.arjLogger.debugAllowed())
+		if (tsLogger.arjLogger.isDebugEnabled())
 		{
 			tsLogger.arjLogger.debug(DebugLevel.CONSTRUCTORS,
 					VisibilityLevel.VIS_PUBLIC, FacilityCode.FAC_ATOMIC_ACTION,
@@ -72,19 +74,22 @@
         _bias = getBiasCounter();
 
 	}
+	
+	public String toString ()
+	{
+	    return "ReaperElement < "+_control+", "+_timeout+", "+statusName()+", "+_worker+" >";
+	}
 
 	/**
 	 * Order by absoluteTimeout first, then by Uid.
 	 * This is required so that the set maintained by the TransactionReaper
 	 * is in timeout order for efficient processing.
 	 *
-	 * @param o
-	 * @return
+	 * @param other the ReaperElement to compare
+	 * @return 0 if equal, 1 if this is greater, -1 if this is smaller
 	 */
-	public int compareTo(Object o)
+	public int compareTo(ReaperElement other)
 	{
-		ReaperElement other = (ReaperElement)o;
-
         if(this == other) {
             return 0;
         }
@@ -114,16 +119,20 @@
     // bias is used to distinguish/sort instances with the same _absoluteTimeoutMills
     // as using Uid for this purpose is expensive. JBTM-611
 
-    private static int biasCounter = 0;
+    private static int MAX_BIAS = 1000000;
+    private static AtomicInteger biasCounter = new AtomicInteger();
 
-    public static synchronized int getBiasCounter()
+    private static int getBiasCounter()
     {
-        if(biasCounter >= 1000000-1) {
-            biasCounter = 0;
-        } else {
-            biasCounter++;
-        }
-        return biasCounter;
+        int value = 0;
+        do {
+            value = biasCounter.getAndIncrement();
+            if(value == MAX_BIAS) {
+                biasCounter.set(0);
+            }
+        } while(value >= MAX_BIAS);
+
+        return value; // range 0 to MAX_BIAS-1 inclusive.
     }
 
     public int _timeout;

Added: labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/coordinator/ReaperElementManager.java
===================================================================
--- labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/coordinator/ReaperElementManager.java	                        (rev 0)
+++ labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/coordinator/ReaperElementManager.java	2010-05-13 11:44:31 UTC (rev 32861)
@@ -0,0 +1,175 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2009, 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) 2009,
+ * @author JBoss by Red Hat.
+ */
+package com.arjuna.ats.internal.arjuna.coordinator;
+
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+
+/*
+ * Encapsulation of a specialised data structure with API and performance characteristics
+ * designed specifically for use by the transaction reaper.
+ *
+ * ReaperElements represent transactions which need timing out. To do this, the reaper needs
+ * to wake periodically and process any timeouts that are due. New elements are added on transaction
+ * creation and will be removed prior to their timeout if they terminate normally.
+ *
+ * For high concurrency, normal inserts and removes should not block. However, to determine the next element
+ * which needs (will need) processing, the elements must be ordered or at least searched. These requirements
+ * are in conflict, since ordering/searching requires stability i.e. locking.
+ *
+ * To achieve the desired performance characteristics, we combine two data structures: an unsorted, concurrent
+ * collection and a sorted, non-threadsafe one which is guarded by the the ReaperElementManager instance lock.
+ *
+ * Inserts are done, potentially concurrently, to the unsorted hash set. Removes likewise check this first
+ * and can return successfully without blocking if the element is found in this collection. Thus the insert/remove
+ * are cheap operations.
+ *
+ * When it is required to know the smallest (i.e. earliest to timeout) element, the contents
+ * of the unsorted set are moved to the sorted set. Since this happens infrequently compared to the insert/delete,
+ * only a fraction of the elements inserted should ever be copied - most will be removed without ever migrating.
+ *
+ * Note that additional external synchronization will be needed to ensure first element does not change
+ * between getFirst and any operation depending on its timeout value. This is the TransactionReaper's problem. 
+ *
+ * The sorted set is maintained manually, rather than using Collections.sort or other comparator based structure.
+ * This is because compareTo on reaper elements is relatively expensive and we wish to avoid liner scans to minimise
+ * the number of such calls. Hence we prefer ArrayList with binary search, despite the higher insert/remove cost
+ * compared to LinkedList.
+ *
+ * Pay careful attention to locking and performance characteristics if altering this class.
+ *
+ *
+ * @author Jonathan Halliday (jonathan.halliday at redhat.com) 2009-10
+ */
+public class ReaperElementManager
+{
+    /**
+     * @return the first (i.e. earliest to time out) element of the colleciton or null if empty
+     */
+    public synchronized ReaperElement getFirst() {
+        flushPending(); // we need to order the elements before we can tell which is first.
+        if(elementsOrderedByTimeout.isEmpty()) {
+            return null;
+        } else {
+            return elementsOrderedByTimeout.get(0);
+        }
+    }
+
+    // Note - unsynchronized for performance.
+    public void add(ReaperElement reaperElement) throws IllegalStateException {
+        if(pendingInsertions.putIfAbsent(reaperElement, reaperElement) != null) {
+            // note this is best effort - we'll allow double inserts if the element is also in the ordered set.
+            throw new IllegalStateException();
+        }
+    }
+
+    /**
+     * @param reaperElement the reaper element to reorder in the sorted set.
+     * @param delayMillis the amount of time to increment the element's timeout by.
+     * @return the new soonest timeout in the set (not necessarily that of the reordered element)
+     */
+    public synchronized long reorder(ReaperElement reaperElement, long delayMillis) {
+        // assume it must be in the sorted list, as it was likely obtained via getFirst...
+        removeSorted(reaperElement);
+        // we could add delay to the original timeout, but using current time is probably safer.
+        reaperElement.setAbsoluteTimeout((System.currentTimeMillis() + delayMillis));
+        // reinsert into its new position.
+        insertSorted(reaperElement);
+
+        // getFirst takes care of flushing the pending set for us.
+        return getFirst().getAbsoluteTimeout();
+    }
+
+    // use only for testing, it's nasty from a performance perspective.
+    public synchronized int size() {
+        return (elementsOrderedByTimeout.size() + pendingInsertions.size());
+    }
+
+    public synchronized boolean isEmpty() {
+        return (elementsOrderedByTimeout.isEmpty() && pendingInsertions.isEmpty());
+    }
+
+    // strange hack to force instant expire of tx during shutdown.
+    public synchronized void setAllTimeoutsToZero() {
+        flushPending();
+        for(ReaperElement reaperElement : elementsOrderedByTimeout) {
+            reaperElement.setAbsoluteTimeout(0);
+        }
+    }
+
+    // Note - mostly unsynchronized for performance.
+    public void remove(ReaperElement reaperElement) {
+        if(pendingInsertions.remove(reaperElement) != null) {
+            return;
+        }
+
+        // we missed finding it in the unsorted set - perhaps it has already been copied to the sorted set...
+        synchronized(this) {
+            removeSorted(reaperElement);
+        }
+    }
+
+    ////////////
+
+    // Private methods and structures are guarded where needed by ReaperElementManager instance locks in the
+    // public methods - see class header doc comments for concurrency/performance info.
+
+    private final ArrayList<ReaperElement> elementsOrderedByTimeout = new ArrayList<ReaperElement>();
+    private final ConcurrentHashMap<ReaperElement, ReaperElement> pendingInsertions = new ConcurrentHashMap<ReaperElement, ReaperElement>();
+
+    private void removeSorted(ReaperElement reaperElement) {
+        int location = Collections.binarySearch(elementsOrderedByTimeout, reaperElement);
+        if(location >= 0) {
+            elementsOrderedByTimeout.remove(location);
+        }
+    }
+
+    private void insertSorted(ReaperElement reaperElement) {
+        int location = Collections.binarySearch(elementsOrderedByTimeout, reaperElement);
+        if(location >= 0) {
+            throw new IllegalStateException();
+        }
+        int insertionPoint = -(location + 1);
+        elementsOrderedByTimeout.add(insertionPoint, reaperElement);
+    }
+
+    private void flushPending() {
+
+        // purge the pending inserts before doing anything else. This is potentially expensive.
+        // Future versions may prefer to insert only a portion of the pending set, or
+        // iterate it each time to determine the smallest (head) element.
+        Set<Map.Entry<ReaperElement,ReaperElement>> entrySet = pendingInsertions.entrySet();
+        if(entrySet != null) {
+            Iterator<Map.Entry<ReaperElement, ReaperElement>> queueIter = entrySet.iterator();
+            // iterator is weakly consistent - will traverse elements present at its time of creation,
+            // may or may not see later updates.
+            while(queueIter.hasNext()) {
+                Map.Entry<ReaperElement,ReaperElement> entry = queueIter.next();
+                ReaperElement element = entry.getValue();
+                // insert/remove not locked, so we are careful to check that we don't insert
+                // an element that has been removed from the pending set by a concurrent thread.
+                if(entrySet.remove(entry)) {
+                    insertSorted(element);
+                }
+            }
+        }
+    }
+}

Added: labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaCore/arjuna/tests/byteman-scripts/objectstore.txt
===================================================================
--- labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaCore/arjuna/tests/byteman-scripts/objectstore.txt	                        (rev 0)
+++ labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaCore/arjuna/tests/byteman-scripts/objectstore.txt	2010-05-13 11:44:31 UTC (rev 32861)
@@ -0,0 +1,109 @@
+########################################################################
+#
+# byteman script used to ensure that core tests can sequence various
+# operations which normally depend upon waiting around for a background
+# thread to be scheduled.
+#
+
+#########################################################################
+# 3 rules to stop the log purger from proceeding until it is signalled
+# at a suitable point during a test. the log purger also signals in
+# order to allow the test code to detect that a purge has completed
+# should it wish to do so
+
+# rule to ensure the transaction log purger uses a very small timeout
+# so that it is ready to proceed straight away when signalled
+
+RULE override TRANSACTION_LOG_PURGE_TIME
+CLASS com.arjuna.ats.internal.arjuna.objectstore.LogStore
+METHOD <clinit>
+AT ENTRY
+BIND NOTHING
+IF TRUE
+DO com.arjuna.ats.arjuna.common.arjPropertyManager.getObjectStoreEnvironmentBean().setPurgeTime(100)
+ENDRULE
+
+# rule to ensure that the LogStore does not proceed to purge any
+# logs until the test signals it do so
+
+RULE delay log purge
+CLASS com.arjuna.ats.internal.arjuna.objectstore.LogPurger
+METHOD run()
+AT CALL writeRemovalEntries
+BIND NOTHING
+IF TRUE
+DO debug("LogStore waiting before purge"),
+   waitFor("LogStore.purge"),
+   flag("LogStore do purge"),
+   debug("LogStore proceeding with purge")
+ENDRULE
+
+# matching rule to ensure that the LogStore signals any thread which
+# is waiting for it to finish purging the logs
+
+RULE done log purge
+CLASS com.arjuna.ats.internal.arjuna.objectstore.LogPurger
+METHOD run()
+AFTER CALL truncateLogs
+BIND NOTHING
+IF TRUE
+DO debug("Signalling purge complete"),
+   signalWake("LogStore.purged", true),
+   flag("LogStore done purge"),
+   debug("Signalled purge complete")
+ENDRULE
+
+#########################################################################
+#
+# rules appropriate to specific tests
+#
+# n.b. several of the test don't want the purger to run. this happens by
+# default since the purger hist the waitFor and never gets signalled
+#
+
+#########################################################################
+# LogStoreRecoveryTest wants to delay the purge until it is ready for it
+# and then delay proceeding with the test until the purge has actually
+# happened
+
+RULE log store recovery test allow purge to proceed
+CLASS com.hp.mwtests.ts.arjuna.objectstore.LogStoreRecoveryTest
+METHOD test()
+AT CALL InputObjectState.<init>
+BIND NOTHING
+IF TRUE
+DO debug("Signalling purge to proceed"),
+   signalWake("LogStore.purge", true),
+   debug("Signalled purge to proceed -- waiting for completion"),
+   waitFor("LogStore.purged"),
+   debug("Purge completed")
+ENDRULE
+
+# throw an error if the purge did not start and finish correctly
+
+RULE log store recovery test throw error if not purged
+CLASS com.hp.mwtests.ts.arjuna.objectstore.LogStoreRecoveryTest
+METHOD test()
+AT EXIT
+BIND NOTHING
+IF !(flagged("LogStore do purge") && flagged("LogStore done purge"))
+DO throw RuntimeException("failed to run purge")
+ENDRULE
+
+#########################################################################
+# LogStoreTest2 wants to delay the purge until it is ready for it
+# and then delay proceeding with the test until the purge has actually
+# happened
+
+RULE log store recovery test allow purge to proceed 2
+CLASS com.hp.mwtests.ts.arjuna.objectstore.LogStoreTest2
+METHOD test()
+AT CALL InputObjectState.<init>
+BIND NOTHING
+IF TRUE
+DO debug("Signalling purge to proceed"),
+   signalWake("LogStore.purge", true),
+   debug("Signalled purge to proceed -- waiting for completion"),
+   waitFor("LogStore.purged"),
+   debug("Purge completed")
+ENDRULE

Added: labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaCore/arjuna/tests/byteman-scripts/reaper.txt
===================================================================
--- labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaCore/arjuna/tests/byteman-scripts/reaper.txt	                        (rev 0)
+++ labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaCore/arjuna/tests/byteman-scripts/reaper.txt	2010-05-13 11:44:31 UTC (rev 32861)
@@ -0,0 +1,359 @@
+########################################################################
+#
+# byteman script used to ensure that core transaction reaper tests can
+# sequence various operations which normally depend upon waiting around
+# for the reaper thread and reaper worker thread to be scheduled.
+#
+
+#########################################################################
+# rules to control progress of the transaction reaper thread
+#
+
+# for each possible point at which a client may want to pause the
+# reaper check if a rendezvous has been set up and if so make the
+# reaper rendezvous twice so the client can latch it and then unleash it
+
+# rendezvous before checking the reaper element queue
+
+RULE pause transaction reaper 1
+CLASS com.arjuna.ats.arjuna.coordinator.TransactionReaper
+METHOD check
+AT SYNCHRONIZE
+BIND NOTHING
+IF isRendezvous("reaper1", 2)
+DO debug("reaper1"),
+   rendezvous("reaper1"),
+   debug("reaper1"),
+   rendezvous("reaper1")
+ENDRULE
+
+# rendezvous and set a flag to record which reaper element is about to be timed out
+# if a client enables this rendezvous then it must also check and clear the flag
+RULE track next element to be processed
+CLASS com.arjuna.ats.arjuna.coordinator.TransactionReaper
+METHOD check
+AT SYNCHRONIZE 2
+BIND NOTHING
+IF isRendezvous("reaper element", 2)
+DO debug("reaper element"),
+   flag($reaperElement._control),
+   rendezvous("reaper element"),
+   debug("reaper element"),
+   rendezvous("reaper element")
+ENDRULE
+
+# rendezvous before processing a timed out reaper element
+
+RULE pause transaction reaper 2
+CLASS com.arjuna.ats.arjuna.coordinator.TransactionReaper
+METHOD check
+AT READ _status
+BIND NOTHING
+IF isRendezvous("reaper2", 2)
+DO debug("reaper2"),
+   rendezvous("reaper2"),
+   debug("reaper2"),
+   rendezvous("reaper2")
+ENDRULE
+
+# rendezvous before queueing in the worker queue a timed out reaper element tagged for cancellation
+
+RULE pause transaction reaper 3
+CLASS com.arjuna.ats.arjuna.coordinator.TransactionReaper
+METHOD check
+AT CALL add 2
+BIND NOTHING
+IF isRendezvous("reaper3", 2)
+DO debug("reaper3"),
+   rendezvous("reaper3"),
+   debug("reaper3"),
+   rendezvous("reaper3")
+ENDRULE
+
+# rendezvous before rescheduling checkup on a timed out reaper element tagged for cancellation
+
+RULE pause transaction reaper 4
+CLASS com.arjuna.ats.arjuna.coordinator.TransactionReaper
+METHOD check
+AT CALL add 3
+BIND NOTHING
+IF isRendezvous("reaper4", 2)
+DO debug("reaper4"),
+   rendezvous("reaper4"),
+   debug("reaper4"),
+   rendezvous("reaper4")
+ENDRULE
+
+# rendezvous before interrupting a timed out reaper element tagged for cancellation
+
+RULE pause transaction reaper 5
+CLASS com.arjuna.ats.arjuna.coordinator.TransactionReaper
+METHOD check
+AT CALL interrupt
+BIND NOTHING
+IF isRendezvous("reaper5", 2)
+DO debug("reaper5"),
+   rendezvous("reaper5"),
+   debug("reaper5"),
+   rendezvous("reaper5")
+ENDRULE
+
+# rendezvous before setting a worker thread to be a zombie
+
+RULE pause transaction reaper 6
+CLASS com.arjuna.ats.arjuna.coordinator.TransactionReaper
+METHOD check
+AT WRITE _status 3
+BIND NOTHING
+IF isRendezvous("reaper6", 2)
+DO debug("reaper6"),
+   rendezvous("reaper6"),
+   debug("reaper6"),
+   rendezvous("reaper6")
+ENDRULE
+
+# rendezvous before calling prevent commit on a reaper element tagged cancel interrupted
+
+RULE pause transaction reaper 7
+CLASS com.arjuna.ats.arjuna.coordinator.TransactionReaper
+METHOD check
+AT WRITE _status 3
+BIND NOTHING
+IF isRendezvous("reaper7", 2)
+DO debug("reaper7"),
+   rendezvous("reaper7"),
+   debug("reaper7"),
+   rendezvous("reaper7")
+ENDRULE
+
+#########################################################################
+# rules to control progress of the transaction reaper worker thread
+#
+
+# for each possible point at which a client may want to pause the
+# reaper worker thread check if a rendezvous has been set up and if so
+# make the reaper worker rendezvous twice so the client can latch it and
+# then unleash it
+
+# rendezvous before removing an element from the reaper element queue
+
+RULE pause transaction reaper worker 1
+CLASS com.arjuna.ats.arjuna.coordinator.TransactionReaper
+METHOD doCancellations
+AT SYNCHRONIZE
+BIND NOTHING
+IF isRendezvous("reaperworker1", 2)
+DO debug("reaperworker1"),
+   rendezvous("reaperworker1"),
+   debug("reaperworker1"),
+   rendezvous("reaperworker1")
+ENDRULE
+
+# rendezvous before marking an element as CANCEL
+
+RULE pause transaction reaper worker 2
+CLASS com.arjuna.ats.arjuna.coordinator.TransactionReaper
+METHOD doCancellations
+AT SYNCHRONIZE 2
+BIND NOTHING
+IF isRendezvous("reaperworker2", 2)
+DO debug("reaperworker2"),
+   rendezvous("reaperworker2"),
+   debug("reaperworker2"),
+   rendezvous("reaperworker2")
+ENDRULE
+
+# rendezvous before calling cancel on an element
+
+RULE pause transaction reaper worker 3
+CLASS com.arjuna.ats.arjuna.coordinator.TransactionReaper
+METHOD doCancellations
+AT CALL cancel
+BIND NOTHING
+IF isRendezvous("reaperworker3", 2)
+DO debug("reaperworker3"),
+   rendezvous("reaperworker3"),
+   debug("reaperworker3"),
+   rendezvous("reaperworker3")
+ENDRULE
+
+# rendezvous before calling prevent_commit on an element
+
+RULE pause transaction reaper worker 4
+CLASS com.arjuna.ats.arjuna.coordinator.TransactionReaper
+METHOD doCancellations
+AT CALL prevent_commit
+BIND NOTHING
+IF isRendezvous("reaperworker4", 2)
+DO debug("reaperworker4"),
+   rendezvous("reaperworker4"),
+   debug("reaperworker4"),
+   rendezvous("reaperworker4")
+ENDRULE
+
+#########################################################################
+#  rules to track and flag actions by the reaper and reaper worker
+
+RULE ReaperTestCase2 flag interrupt
+CLASS com.arjuna.ats.arjuna.coordinator.TransactionReaper
+METHOD check
+AFTER CALL interrupt
+BIND NOTHING
+IF TRUE
+DO debug("reaper called interrupt on " + $reaperElement),
+   flag("interrupted")
+ENDRULE
+
+RULE ReaperTestCase2 flag zombie
+CLASS com.arjuna.ats.arjuna.coordinator.TransactionReaper
+METHOD check
+AFTER WRITE _zombieCount
+BIND NOTHING
+IF TRUE
+DO debug("reaper incremented zombie count " + $reaperElement),
+   flag("zombied")
+ENDRULE
+
+#########################################################################
+#
+# rules appropriate to specific tests
+
+#########################################################################
+# ReaperMonitorTest wants remote control of the reaper thread
+#
+
+RULE ReaperMonitorTest reaper remote control
+CLASS com.hp.mwtests.ts.arjuna.reaper.ReaperMonitorTest
+METHOD test()
+AT CALL TransactionReaper.insert
+BIND NOTHING
+IF TRUE
+DO createRendezvous("reaper1", 2, true)
+ENDRULE
+
+# ReaperMonitorTest wants to delay the reaper check until it has inserted
+# a reapable
+
+RULE ReaperMonitorTest unlatch reaper thread
+CLASS com.hp.mwtests.ts.arjuna.reaper.ReaperMonitorTest
+METHOD test()
+AFTER CALL TransactionReaper.insert
+BIND NOTHING
+IF TRUE
+DO debug("reaper1"),
+   rendezvous("reaper1"),
+   # ensure timed out
+   delay(1000),
+   debug("reaper1"),
+   rendezvous("reaper1")
+ENDRULE
+
+#########################################################################
+# ReaperTestCaseControl provides methods used by its three
+# ReaperTestCase<N> subclasses to enable, disable and trigger rule
+# activity during the course of the test.
+
+RULE ReaperTestCaseControl enable trigger rendezvous
+CLASS com.hp.mwtests.ts.arjuna.reaper.ReaperTestCaseControl
+METHOD enableRendezvous(Object, boolean)
+AT ENTRY
+BIND NOTHING
+IF TRUE
+DO createRendezvous($1, 2, $2)
+ENDRULE
+
+RULE ReaperTestCaseControl disable trigger rendezvous
+CLASS com.hp.mwtests.ts.arjuna.reaper.ReaperTestCaseControl
+METHOD disableRendezvous(Object)
+AT ENTRY
+BIND NOTHING
+IF TRUE
+DO deleteRendezvous($1, 2)
+ENDRULE
+
+# if the supplied string matches a known rendezvous then trigger it
+
+RULE ReaperTestCaseControl rendezvous
+CLASS com.hp.mwtests.ts.arjuna.reaper.ReaperTestCaseControl
+METHOD triggerRendezvous(Object)
+AT ENTRY
+BIND thread = Thread.currentThread()
+IF isRendezvous($1, 2)
+DO debug("" + $1 + " " + thread),
+   rendezvous($1),
+   return
+ENDRULE
+
+RULE ReaperTestCaseControl rendezvous 2
+CLASS com.hp.mwtests.ts.arjuna.reaper.ReaperTestCaseControl
+METHOD triggerRendezvous(Object)
+AT ENTRY
+BIND NOTHING
+IF NOT isRendezvous($1, 2)
+DO throw RuntimeException("invalid rendezvous for trigger " + $1)
+ENDRULE
+
+# trigger a delay
+
+RULE ReaperTestCaseControl wait
+CLASS com.hp.mwtests.ts.arjuna.reaper.ReaperTestCaseControl
+METHOD triggerWait(int)
+AT ENTRY
+BIND NOTHING
+IF TRUE
+DO debug("wait " + $1),
+   delay($1),
+   return
+ENDRULE
+
+RULE ReaperTestCaseControl check and clear
+CLASS com.hp.mwtests.ts.arjuna.reaper.ReaperTestCaseControl
+METHOD checkAndClearFlag(Object)
+AT ENTRY
+BIND value = flagged($1)
+IF TRUE
+DO debug("setAndClear(" + $1 + ") => " + value),
+   clear($1),
+   return value
+ENDRULE
+
+#########################################################################
+# ReaperTestCase wants to ensure that the reaper is single stepped
+# through processing of inserted reaper elements
+#
+
+# debug tracing rules
+# RULE ReaperTestCase trace element remove
+# CLASS com.arjuna.ats.arjuna.coordinator.TransactionReaper
+# METHOD doCancellations
+# AT CALL removeElement
+# BIND NOTHING
+# IF TRUE
+# DO debug("removing reapable " + $e)
+# ENDRULE
+
+# RULE ReaperTestCase trace element add
+# CLASS com.arjuna.ats.arjuna.coordinator.TransactionReaper
+# METHOD check
+# AT CALL List.add
+# BIND NOTHING
+# IF TRUE
+# DO debug("adding reapable " + $e)
+# ENDRULE
+
+#########################################################################
+# ReaperTestCase3 wants the reaper and reaper worker threads to
+# be suspended until it has reset the timeouts for the reapables
+# to zero forcing them to be cancelled early
+#
+
+RULE ReaperTestCase3 unlatch reaper and reaper worker
+CLASS com.arjuna.ats.arjuna.coordinator.TransactionReaper
+METHOD shutdown
+AT CALL wait
+BIND NOTHING
+IF TRUE
+DO debug("removing latches on reaper and reaper worker"),
+   deleteRendezvous("reaper1", 2),
+   deleteRendezvous("reaperworker1", 2)   
+ENDRULE

Modified: labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/reaper/ReaperTestCase.java
===================================================================
--- labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/reaper/ReaperTestCase.java	2010-05-13 11:36:42 UTC (rev 32860)
+++ labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/reaper/ReaperTestCase.java	2010-05-13 11:44:31 UTC (rev 32861)
@@ -20,9 +20,6 @@
  */
 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.common.Uid;
@@ -33,109 +30,151 @@
 
 /**
  * Exercises some aspects of the TransactionReaper functionality.
+ *
  * @author jonathan.halliday at redhat.com, 2007-04-30
  */
-public class ReaperTestCase extends TestCase
+public class ReaperTestCase extends ReaperTestCaseControl
 {
-	public static Test suite() {
-		return new TestSuite(ReaperTestCase.class);
-	}
+    public void testReaper() throws Exception
+    {
 
-	public void testReaper() throws Exception {
+        TransactionReaper reaper = TransactionReaper.transactionReaper();
 
-		// test set+readback of interval
-		TransactionReaper.create(100);
-		TransactionReaper reaper = TransactionReaper.transactionReaper();
-        // set value is ignored in default DYNAMIC mode, it uses max long instead.
-        assertEquals(Long.MAX_VALUE, reaper.checkingPeriod());
 
+        Reapable reapable = new MockReapable(new Uid());
+        Reapable reapable2 = new MockReapable(new Uid());
+        Reapable reapable3 = new MockReapable(new Uid());
 
-		Reapable reapable = new MockReapable(new Uid());
-		Reapable reapable2 = new MockReapable(new Uid());
-		Reapable reapable3 = new MockReapable(new Uid());
+        ReaperElement reaperElement = new ReaperElement(reapable, 30);
+        ReaperElement reaperElement2 = new ReaperElement(reapable2, 20);
+        ReaperElement reaperElement3 = new ReaperElement(reapable3, 10);
 
-		ReaperElement reaperElement = new ReaperElement(reapable, 30);
-		ReaperElement reaperElement2 = new ReaperElement(reapable2, 20);
-		ReaperElement reaperElement3 = new ReaperElement(reapable3, 10);
+        // test that ordering is by timeout, regardless of insertion order
+        SortedSet sortedSet = new TreeSet();
+        sortedSet.add(reaperElement);
+        sortedSet.add(reaperElement3);
+        sortedSet.add(reaperElement2);
 
-		// test that ordering is by timeout, regardless of insertion order
-		SortedSet sortedSet = new TreeSet();
-		sortedSet.add(reaperElement);
-		sortedSet.add(reaperElement3);
-		sortedSet.add(reaperElement2);
+        assertEquals(sortedSet.first(), reaperElement3);
+        assertEquals(sortedSet.last(), reaperElement);
 
-		assertEquals(sortedSet.first(), reaperElement3);
-		assertEquals(sortedSet.last(), reaperElement);
+        // test insertion of timeout=0 is a nullop
+        reaper.insert(reapable, 0);
+        assertEquals(0, reaper.numberOfTransactions());
+        assertEquals(0, reaper.numberOfTimeouts());
+        reaper.remove(reapable);
 
-		// test insertion of timeout=0 is a nullop
-		assertTrue(reaper.insert(reapable, 0));
-		assertEquals(0, reaper.numberOfTransactions());
-                assertEquals(0, reaper.numberOfTimeouts());
-		assertFalse(reaper.remove(reapable));
+        // test that duplicate insertion fails
+        reaper.insert(reapable, 10);
+        assertEquals(1, reaper.numberOfTransactions());
+        assertEquals(1, reaper.numberOfTimeouts());
+        try {
+            reaper.insert(reapable, 10);
+            fail("duplicate insert failed to blow up");
+        } catch(Exception e) {
+        }
+        reaper.remove(reapable);
+        assertEquals(0, reaper.numberOfTransactions());
+        assertEquals(0, reaper.numberOfTimeouts());
 
-		// test that duplicate insertion fails
-		assertTrue(reaper.insert(reapable, 10));
-		assertFalse(reaper.insert(reapable, 10));
-		assertEquals(1, reaper.numberOfTransactions());
-                assertEquals(1, reaper.numberOfTimeouts());
-		assertTrue(reaper.remove(reapable));
-		assertEquals(0, reaper.numberOfTransactions());
-                assertEquals(0, reaper.numberOfTimeouts());
+        // test that timeout change fails
+        reaper.insert(reapable, 10);
+        try {
+            reaper.insert(reapable, 20);
+            fail("timeout change insert failed to blow up");
+        } catch(Exception e) {
+        }
+        assertEquals(1, reaper.numberOfTransactions());
+        assertEquals(1, reaper.numberOfTimeouts());
+        assertEquals(10, reaper.getTimeout(reapable));
+        reaper.remove(reapable);
+        assertEquals(0, reaper.numberOfTransactions());
+        assertEquals(0, reaper.numberOfTimeouts());
 
-		// test that timeout change fails
-		assertTrue(reaper.insert(reapable, 10));
-		assertFalse(reaper.insert(reapable, 20));
-		assertEquals(1, reaper.numberOfTransactions());
-                assertEquals(1, reaper.numberOfTimeouts());
-                assertEquals(10, reaper.getTimeout(reapable));
-		assertTrue(reaper.remove(reapable));
-                assertEquals(0, reaper.numberOfTransactions());
-                assertEquals(0, reaper.numberOfTimeouts());
+        // enable a repeatable rendezvous before checking the reapable queue
+        enableRendezvous("reaper1", true);
+        // enable a repeatable rendezvous before scheduling a reapable in the worker queue for cancellation
+        enableRendezvous("reaper2", true);
+        // enable a repeatable rendezvous before checking the worker queue
+        enableRendezvous("reaperworker1", true);
 
-		// test reaping
-		reaper.insert(reapable, 1); // seconds
-		reaper.insert(reapable2, 5);
-                assertEquals(2, reaper.numberOfTransactions());
-                assertEquals(2, reaper.numberOfTimeouts());
-		reaper.check();
-                assertEquals(2, reaper.numberOfTransactions());
-		Thread.sleep(2*1000);
-		reaper.check();
-                assertEquals(1, reaper.numberOfTransactions());
-                assertEquals(1, reaper.numberOfTimeouts());
-		Thread.sleep(4*1000);
-		reaper.check();
-                assertEquals(0, reaper.numberOfTransactions());
-                assertEquals(0, reaper.numberOfTimeouts());
+        // test reaping
+        reaper.insert(reapable, 1); // seconds
+        reaper.insert(reapable2, 2);
 
-	}
+        // the reaper will be latched before it processes any of the reapables
+        triggerRendezvous("reaper1");
+        assertEquals(2, reaper.numberOfTransactions());
+        assertEquals(2, reaper.numberOfTimeouts());
+        // ensure we have waited at lest 1 second so the first reapable is timed out
+        triggerWait(1000);
+        // let the reaper proceed with the dequeue and add the entry to the work queue
+        triggerRendezvous("reaper1");
+        triggerRendezvous("reaper2");
+        triggerRendezvous("reaper2");
+        // now latch the reaper worker at the dequeue
+        triggerRendezvous("reaperworker1");
+        // we shoudl still have two reapables in the reaper queue
+        assertEquals(2, reaper.numberOfTransactions());
+        assertEquals(2, reaper.numberOfTimeouts());
+        // now let the worker process the work queue element -- it should not call cancel since the
+        // mock reapable will not claim to be running
+        triggerRendezvous("reaperworker1");
+        // latch the reaper and reaper worker before they check their respective queues
+        // latch the reaper before it dequeues the next reapable
+        triggerRendezvous("reaper1");
+        triggerRendezvous("reaperworker1");
+        // we should now have only 1 element in the reaper queue
+        assertEquals(1, reaper.numberOfTransactions());
+        assertEquals(1, reaper.numberOfTimeouts());
+        // ensure we have waited at lest 1 second so the second reapable is timed out
+        triggerWait(1000);
+        // now let the reaper proceed with the next dequeue and enqueue the reapable for the worker to process
+        triggerRendezvous("reaper1");
+        triggerRendezvous("reaper2");
+        triggerRendezvous("reaper2");
+        // relatch the reaper next time round the loop so we can be sure it is not monkeying around
+        // with the transactions queue
+        triggerRendezvous("reaper1");
+        // the worker is still latched so we should still have one entry in the work queue
+        assertEquals(1, reaper.numberOfTransactions());
+        assertEquals(1, reaper.numberOfTimeouts());
+        // now let the worker process the work queue element -- it should not call cancel since the
+        // mock reapable wil not claim to be running
+        triggerRendezvous("reaperworker1");
+        // latch reaper worker again so we know it has finished processing the element
+        triggerRendezvous("reaperworker1");
+        assertEquals(0, reaper.numberOfTransactions());
+        assertEquals(0, reaper.numberOfTimeouts());
+    }
 
-	public class MockReapable implements Reapable
-	{
-		public MockReapable(Uid uid) {
-			this.uid = uid;
-		}
+    public class MockReapable implements Reapable
+    {
+        public MockReapable(Uid uid)
+        {
+            this.uid = uid;
+        }
 
-		public boolean running()
-		{
-			return false;  //To change body of implemented methods use File | Settings | File Templates.
-		}
+        public boolean running()
+        {
+            return false;  //To change body of implemented methods use File | Settings | File Templates.
+        }
 
-		public boolean preventCommit()
-		{
-			return false;  //To change body of implemented methods use File | Settings | File Templates.
-		}
+        public boolean preventCommit()
+        {
+            return false;  //To change body of implemented methods use File | Settings | File Templates.
+        }
 
-		public int cancel()
-		{
-			return 0;  //To change body of implemented methods use File | Settings | File Templates.
-		}
+        public int cancel()
+        {
+            return 0;  //To change body of implemented methods use File | Settings | File Templates.
+        }
 
-		public Uid get_uid()
-		{
-			return uid;
-		}
+        public Uid get_uid()
+        {
+            return uid;
+        }
 
-		private Uid uid;
-	}
+        private Uid uid;
+    }
 }

Modified: labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/reaper/ReaperTestCase2.java
===================================================================
--- labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/reaper/ReaperTestCase2.java	2010-05-13 11:36:42 UTC (rev 32860)
+++ labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/reaper/ReaperTestCase2.java	2010-05-13 11:44:31 UTC (rev 32861)
@@ -20,286 +20,441 @@
  */
 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;
-
 /**
  * Exercise cancellation behaviour of TransactionReaper with resources
  * that time out and, optionally, get wedged either when a cancel is
  * tried and/or when an interrupt is delivered
  *
- * @author Andrwe Dinn (adinn at redhat.com), 2007-07-09
+ * @author Andrew Dinn (adinn at redhat.com), 2007-07-09
  */
 
-public class ReaperTestCase2 extends TestCase
+public class ReaperTestCase2  extends ReaperTestCaseControl
 {
-    public static Test suite()
-    {
-	return new TestSuite(ReaperTestCase2.class);
-    }
-
     public void testReaper() throws Exception
     {
-	TransactionReaper.create(500);
-	TransactionReaper reaper = TransactionReaper.transactionReaper();
+        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
+        // 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, false);
-	SlowReapable reapable2 = new SlowReapable(new Uid(), 0, 0, true, true, false);
-	SlowReapable reapable3 = new SlowReapable(new Uid(), 100, 2000, false, true, false);
-	SlowReapable reapable4 = new SlowReapable(new Uid(), 1000, 1000, false, false, false);
+        // the rendezvous for the reapables are keyed by the reapable's uid
 
-	// insert reapables so they timeout at 1 second intervals then
-	// check progress of cancellations and rollbacks
+        Uid uid0 = new Uid();
+        Uid uid1 = new Uid();
+        Uid uid2 = new Uid();
+        Uid uid3 = new Uid();
 
-	assertTrue(reaper.insert(reapable1, 1));
+        // reapable0 will return CANCELLED from cancel and will rendezvous inside the cancel call
+        // so we can delay it. prevent_commit should not get called so we don't care about the arguments
+        TestReapable reapable0 = new TestReapable(uid0, true, true, false, false);
+        // reapable1 will return CANCELLED from cancel and will not rendezvous inside the cancel call
+        // prevent_commit should not get called so we don't care about the arguments
+        TestReapable reapable1 = new TestReapable(uid1, true, false, false, false);
+        // reapable2 will return RUNNING from cancel and will rendezvous inside the cancel call
+        // the call will get delayed causing the worker to exit as a zombie
+        // prevent_commit will be called from the reaper thread and will fail but will not rendezvous
+        TestReapable reapable2 = new TestReapable(uid2, false, true, false, false);
+        // reapable3 will return RUNNING from cancel and will not rendezvous inside the cancel call
+        // prevent_commit should get called and should return true without a rendezvous
+        TestReapable reapable3 = new TestReapable(uid3, false, false, true, false);
 
-	assertTrue(reaper.insert(reapable2, 2));
+        // enable a repeatable rendezvous before checking the reapable queue
+        enableRendezvous("reaper1", true);
+        // enable a repeatable rendezvous when synchronizing on a timed out reapoer element so we can check that
+        // the element is the one we expect.
+        enableRendezvous("reaper element", true);
+        // enable a repeatable rendezvous before processing a timed out reapable
+        // enableRendezvous("reaper2", true);
+        // enable a repeatable rendezvous before scheduling a reapable in the worker queue for cancellation
+        // enableRendezvous("reaper3", true);
+        // enable a repeatable rendezvous before rescheduling a reapable in the worker queue for cancellation
+        // enableRendezvous("reaper4", true);
+        // enable a repeatable rendezvous before interrupting a cancelled reapable
+        // enableRendezvous("reaper5", true);
+        // enable a repeatable rendezvous before marking a worker thread as a zombie
+        // enableRendezvous("reaper6", true);
+        // enable a repeatable rendezvous before marking a reapable as rollback only from the reaper thread
+        // enableRendezvous("reaper7", true);
+        // enable a repeatable rendezvous before checking the worker queue
+        enableRendezvous("reaperworker1", true);
+        // enable a repeatable rendezvous before marking a reapable as cancel
+        // enableRendezvous("reaperworker2", true);
+        // enable a repeatable rendezvous before calling cancel
+        // enableRendezvous("reaperworker3", true);
+        // enable a repeatable rendezvous before marking a reapable as rollback only from the worker thread
+        // enableRendezvous("reaperworker4", true);
 
-	assertTrue(reaper.insert(reapable3, 3));
+        // enable a repeatable rendezvous for each of the test reapables which we have marked to
+        // perform a rendezvous
 
-	assertTrue(reaper.insert(reapable4, 4));
+        enableRendezvous(uid0, true);
+        enableRendezvous(uid2, true);
 
-	// make sure they were all registered
+        // STAGE I
+        // insert two reapables so they timeout at 1 second intervals then stall the first one and
+        // check progress of cancellations and rollbacks for both
 
-	assertEquals(4, reaper.numberOfTransactions());
-	assertEquals(4, reaper.numberOfTimeouts());
+        reaper.insert(reapable0, 1);
 
-	// n.b. the reaper will not operate in dynamic mode by default
-	// so we have to allow an extra checkPeriod millisecs for it
-	// to detect timeouts (it may go back to sleep a few
-	// milliseconds before a transaction times out). also by
-	// default the reaper waits 500 msecs for a cancel to take
-	// effect before interrupting and 500 msecs for an interrupt
-	// to take effect before making a wedged worker a zombie. so
-	// these need to be factored into this thread's delays when
-	// tetsing the state of the reapables.
+        reaper.insert(reapable1, 1);
 
-	// wait at most 2 seconds for the first reapable to be cancelled
+        //assertTrue(reaper.insert(reapable2, 1));
 
-	int count = 0;
+        //assertTrue(reaper.insert(reapable3, 1));
 
-	while (!reapable1.getCancelTried() && count < 20) {
-	    count++;
-	    Thread.sleep(100);
-	}
+        // latch the reaper before it tries to process the queue
 
-	assertTrue(count < 20);
+        triggerRendezvous("reaper1");
 
-	// ensure that the second one gets cancelled even if  the
-	// first one is wedged
+        // make sure they were all registered
+        // the transactions queue should be
+        // UID0 RUNNING
+        // UID1 RUNNING
 
-	count = 0;
+        assertEquals(2, reaper.numberOfTransactions());
+        assertEquals(2, reaper.numberOfTimeouts());
 
-	while (reapable2.getRunning() && count < 15) {
-	    count++;
-	    Thread.sleep(100);
-	}
+        // wait long enough to ensure both reapables have timed out
 
-	assertTrue(count < 15);
+        triggerWait(1000);
 
-	// ensure also that the second one gave up at the cancel and
-	// not the mark for rollback
+        // now let the reaper dequeue the first reapable, process it and queue it for the worker thread
+        // to deal with
 
-	assertTrue(reapable2.getCancelTried());
-	assertTrue(!reapable2.getRollbackTried());
+        triggerRendezvous("reaper1");
 
-	// ensure that the first one responded to the interrupt and
-	// marks itself for rollback only
+        // latch the reaper at the reaper element check
 
-	count = 0;
+        triggerRendezvous("reaper element");
 
-	while (reapable1.getRunning() && count < 10) {
-	    count++;
-	    Thread.sleep(100);
-	}
+        // check that we have dequeued reapable0
 
-	assertTrue(count < 10);
-	// the first one should not be running because it marks itself ActionStatus.ABORTED
-	// - the reaper should not attempt to roll it back in this case
-	assertTrue(!reapable1.getRollbackTried());
+        assertTrue(checkAndClearFlag(reapable0));
 
-	// check that the third one refuses the cancel and gets marked
-	// for rollback instead
+        // unlatch the reaper so it can process the element
 
-	count = 0;
+        triggerRendezvous("reaper element");
 
-	while (!reapable3.getCancelTried() && count < 25) {
-	    count++;
-	    Thread.sleep(100);
-	}
+        // latch the reaper before it tests the queue again
 
-	assertTrue(count < 25);
+        triggerRendezvous("reaper1");
 
-	// ensure that it gets marked for rollback
+        // latch the reaperworker before it tries to dequeue from the worker queue
 
-	count = 0;
+        triggerRendezvous("reaperworker1");
 
-	while (reapable3.getRunning() && count < 10) {
-	    count++;
-	    Thread.sleep(100);
-	}
+        // the transactions queue should be
+        // UID1 RUNNING
+        // UID0 CANCEL
 
-	assertTrue(count < 10);
+        assertEquals(2, reaper.numberOfTransactions());
+        assertEquals(2, reaper.numberOfTimeouts());
 
-	assertTrue(reapable3.getRollbackTried());
+        // now let the worker dequeue a reapable and proceed to call cancel
 
-	// ensure the fourth one gets cancelled and marked for rolback
-	// even though it does not play ball
+        triggerRendezvous("reaperworker1");
 
-	count = 0;
+        // latch the first reapable inside cancel
 
-	while (reapable4.getRunning() && count < 25) {
-	    count++;
-	    Thread.sleep(100);
-	}
+        triggerRendezvous(uid0);
 
-	assertTrue(count < 25);
+        // now let the reaper check the queue for the second reapable, dequeue it and add it to the
+        // worker queue
 
-	assertTrue(reapable4.getCancelTried());
-	assertTrue(reapable4.getRollbackTried());
+        triggerRendezvous("reaper1");
 
-	// we should have a zombie worker so check that the thread
-	// which tried the cancel is still runnning then sleep a bit
-	// to give it time to complete and check it has exited
+        // latch the reaper at the reaper element check
 
-	Thread worker = reapable4.getCancelThread();
+        triggerRendezvous("reaper element");
 
-	assertTrue(worker.isAlive());
+        // check that we have dequeued reapable1
 
-	count = 0;
+        assertTrue(checkAndClearFlag(reapable1));
 
-	while (worker.isAlive() && count < 30) {
-	    count++;
-	    Thread.sleep(100);
-	}
+        // unlatch the reaper so it can process the element
 
-	assertTrue(count < 30);
-    }
+        triggerRendezvous("reaper element");
 
-    public class SlowReapable implements Reapable
-    {
-	public SlowReapable(Uid uid, int callDelay, int interruptDelay, boolean doCancel, boolean doRollback, boolean doComplete)
-	{
-	    this.uid = uid;
-            this.callDelay = callDelay;
-            this.interruptDelay = interruptDelay;
-            this.doCancel = doCancel;
-            this.doRollback = doRollback;
-            this.doComplete = doComplete;
-	    cancelTried = false;
-	    rollbackTried = false;
-	    running = true;
-        }
+        // latch the reaper before it tests the queue again
 
-	public boolean running()
-	{
-	    return getRunning();
-	}
+        triggerRendezvous("reaper1");
 
-	public boolean preventCommit()
-	{
-	    setRollbackTried();
-	    clearRunning();
-	    return doRollback;
-	}
+        // the transactions queue should be
+        // UID0 CANCEL
+        // UID1 SCHEDULE_CANCEL
 
-	public int cancel()
-	{
-	    boolean interrupted = false;
+        assertEquals(2, reaper.numberOfTransactions());
+        assertEquals(2, reaper.numberOfTimeouts());
 
-	    setCancelTried();
+        // ensure we wait long enough for the cancel to time out
 
-	    // track the worker trying to do the cancel so we can
-	    // detect if it becomes a zombie
+        triggerWait(500);
 
-	    setCancelThread(Thread.currentThread());
+        // now let the reaper check the queue and interrupt the cancel for UID1
 
-	    if (callDelay > 0) {
-		try {
-		    Thread.sleep(callDelay);
-		} catch (InterruptedException e) {
-		    interrupted = true;
-		}
-	    }
-	    if (interrupted && interruptDelay > 0) {
-		try {
-		    Thread.sleep(interruptDelay);
-		} catch (InterruptedException e) {
-		}
-	    }
+        triggerRendezvous("reaper1");
 
-	    if (doCancel) {
-		clearRunning();
-		return ActionStatus.ABORTED;
-	    } else {
-		return ActionStatus.RUNNING;
-	    }
-	}
+        // latch the reaper at the reaper element check
 
-	public Uid get_uid()
-	{
-	    return uid;
-	}
+        triggerRendezvous("reaper element");
 
-	private Uid uid;
-        private int callDelay; // in milliseconds
-        private int interruptDelay; // in milliseconds
-        private boolean doCancel;
-        private boolean doRollback;
-        private boolean doComplete;
-	private boolean cancelTried;
-	private boolean rollbackTried;
-	private boolean running;
-	private Thread cancelThread;
+        // check that we have dequeued reapable0
 
-	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;
-	}
+        assertTrue(checkAndClearFlag(reapable0));
+
+        // unlatch the reaper so it can process the element
+
+        triggerRendezvous("reaper element");
+
+        // latch the reaper before it tests the queue again
+
+        triggerRendezvous("reaper1");
+
+        // unlatch the first reapable inside cancel
+
+        triggerRendezvous(uid0);
+
+        // latch the worker as it is about to process the queue again
+
+        triggerRendezvous("reaperworker1");
+
+        // the transactions queue should be
+        // UID1 SCHEDULE_CANCEL
+
+        assertEquals(1, reaper.numberOfTransactions());
+        assertEquals(1, reaper.numberOfTimeouts());
+
+        // let the worker clear and cancel the 2nd reapable
+
+        triggerRendezvous("reaperworker1");
+
+        // latch the worker before it reads the worker queue
+
+        triggerRendezvous("reaperworker1");
+
+        // the transactions queue should be empty
+
+        assertEquals(0, reaper.numberOfTransactions());
+        assertEquals(0, reaper.numberOfTimeouts());
+
+        // ensure that cancel was tried on reapable1 and that set rollback only was not tried on either
+        // we know cancel was tried on reapable0 because we got through the rendezvous
+
+        assertTrue(reapable1.getCancelTried());
+        assertTrue(!reapable0.getRollbackTried());
+        assertTrue(!reapable1.getRollbackTried());
+        assertTrue(checkAndClearFlag("interrupted"));
+
+        // STAGE II
+        // now use the next pair of reapables to check that a wedged reaperworker gets tuirned into a zombie and
+        // a new worker gets created to cancel the remaining reapables.
+        // insert reapables so they timeout at 1 second intervals then
+        // check progress of cancellations and rollbacks
+
+        reaper.insert(reapable2, 1);
+
+        reaper.insert(reapable3, 1);
+
+        // make sure they were all registered
+        // the transactions queue should be
+        // UID2 RUNNING
+        // UID3 RUNNING
+
+        assertEquals(2, reaper.numberOfTransactions());
+        assertEquals(2, reaper.numberOfTimeouts());
+
+        // wait long enough to ensure both reapables have timed out
+
+        triggerWait(1000);
+
+        // now let the reaper dequeue the first reapable, process it and queue it for the worker thread
+        // to deal with
+
+        triggerRendezvous("reaper1");
+
+        // latch the reaper at the reaper element check
+
+        triggerRendezvous("reaper element");
+
+        // check that we have dequeued reapable2
+
+        assertTrue(checkAndClearFlag(reapable2));
+
+        // unlatch the reaper so it can process the element
+
+        triggerRendezvous("reaper element");
+
+        // latch the reaper before it tests the queue again
+
+        triggerRendezvous("reaper1");
+
+        // the transactions queue should be
+        // UID3 RUNNING
+        // UID2 CANCEL
+
+        assertEquals(2, reaper.numberOfTransactions());
+        assertEquals(2, reaper.numberOfTimeouts());
+
+        // now let the worker dequeue the fourth reapable and proceed to call cancel
+
+        triggerRendezvous("reaperworker1");
+
+        // latch the third reapable inside cancel
+
+        triggerRendezvous(uid2);
+
+        // now let the reaper check the queue for the fourth reapable, dequeue it and add it to the
+        // worker queue
+
+        triggerRendezvous("reaper1");
+
+        // latch the reaper at the reaper element check
+
+        triggerRendezvous("reaper element");
+
+        // check that we have dequeued reapable3
+
+        assertTrue(checkAndClearFlag(reapable3));
+
+        // unlatch the reaper so it can process the element
+
+        triggerRendezvous("reaper element");
+
+        // latch the reaper before it tests the queue again
+
+        triggerRendezvous("reaper1");
+
+        // the transactions queue should be
+        // UID2 CANCEL
+        // UID3 SCHEDULE_CANCEL
+
+        assertEquals(2, reaper.numberOfTransactions());
+        assertEquals(2, reaper.numberOfTimeouts());
+
+        // ensure we wait long enough for the cancel to time out
+
+        triggerWait(500);
+
+        // now let the reaper check the queue and interrupt the cancel for UID3
+
+        triggerRendezvous("reaper1");
+
+        // latch the reaper at the reaper element check
+
+        triggerRendezvous("reaper element");
+
+        // check that we have dequeued reapable2
+
+        assertTrue(checkAndClearFlag(reapable2));
+
+        // unlatch the reaper so it can process the element
+
+        triggerRendezvous("reaper element");
+
+        // latch the reaper before it tests the queue again
+
+        triggerRendezvous("reaper1");
+
+        assertTrue(checkAndClearFlag("interrupted"));
+
+        // ensure we wait long enough for the cancel to time out
+
+        triggerWait(500);
+
+        // the transactions queue should be
+        // UID3 SCHEDULE_CANCEL
+        // UID2 CANCEL_INTERRUPTED
+
+        assertEquals(2, reaper.numberOfTransactions());
+        assertEquals(2, reaper.numberOfTimeouts());
+
+        // let the reaper check the queue and reschedule the fourth reapable
+
+        triggerRendezvous("reaper1");
+
+        // latch the reaper at the reaper element check
+
+        triggerRendezvous("reaper element");
+
+        // check that we have dequeued reapable3
+
+        assertTrue(checkAndClearFlag(reapable3));
+
+        // unlatch the reaper so it can process the element
+
+        triggerRendezvous("reaper element");
+
+        // latch the reaper before it tests the queue again
+
+        triggerRendezvous("reaper1");
+
+        // the transactions queue should be
+        // UID2 CANCEL_INTERRUPTED
+        // UID3 SCHEDULE_CANCEL
+
+        assertEquals(2, reaper.numberOfTransactions());
+        assertEquals(2, reaper.numberOfTimeouts());
+
+        // let the reaper check the queue and mark the reaper worker as a zombie
+
+        triggerRendezvous("reaper1");
+
+        // latch the reaper at the reaper element check
+
+        triggerRendezvous("reaper element");
+
+        // check that we have dequeued reapable2
+
+        assertTrue(checkAndClearFlag(reapable2));
+
+        // unlatch the reaper so it can process the element
+
+        triggerRendezvous("reaper element");
+
+        // latch the reaper before it tests the queue again
+
+        triggerRendezvous("reaper1");
+
+        // the reaper should have marked the thread as a zombie 
+        
+        assertTrue(checkAndClearFlag("zombied"));
+
+        // the transactions queue should be
+        // UID3 SCHEDULE_CANCEL
+
+        assertEquals(1, reaper.numberOfTransactions());
+        assertEquals(1, reaper.numberOfTimeouts());
+
+        // unlatch the third reapable inside cancel
+
+        triggerRendezvous(uid2);
+
+        // latch the new worker as it is about to process the queue again
+
+        triggerRendezvous("reaperworker1");
+
+        // let the worker clear and cancel the 2nd reapable
+
+        triggerRendezvous("reaperworker1");
+
+        // latch the worker before it reads the worker queue
+
+        triggerRendezvous("reaperworker1");
+
+        // the transactions queue should be empty
+
+        assertEquals(0, reaper.numberOfTransactions());
+        assertEquals(0, reaper.numberOfTimeouts());
+
+        // ensure that cancel was tried on reapable3 and that set rollback only was tried on reapable2
+        // and reapable3 we know cancel was tried on reapable2 because we got through the rendezvous
+
+        assertTrue(reapable3.getCancelTried());
+        assertTrue(reapable2.getRollbackTried());
+        assertTrue(reapable3.getRollbackTried());
     }
 }

Modified: labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/reaper/ReaperTestCase3.java
===================================================================
--- labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/reaper/ReaperTestCase3.java	2010-05-13 11:36:42 UTC (rev 32860)
+++ labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/reaper/ReaperTestCase3.java	2010-05-13 11:44:31 UTC (rev 32861)
@@ -20,228 +20,345 @@
  */
 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 class ReaperTestCase3  extends ReaperTestCaseControl
 {
-    public static Test suite()
+    public void testReaperWait() throws Exception
     {
-	return new TestSuite(ReaperTestCase3.class);
-    }
+        TransactionReaper reaper = TransactionReaper.transactionReaper();
 
-    public void testReaperWait () throws Exception
-    {
-	TransactionReaper.create(500);
-	TransactionReaper reaper = TransactionReaper.transactionReaper();
+        // give the reaper worker time to start too
 
-	// give the reaper worker time to start too
+        Thread.sleep(1000);
 
-	Thread.sleep(1000);
+        // create test reapables some of which will not respond immediately to cancel requests
 
-	// 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
+        Uid uid0 = new Uid();
+        Uid uid1 = new Uid();
+        Uid uid2 = new Uid();
+        Uid uid3 = new Uid();
 
-	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);
+        // reapable0 will return CANCELLED from cancel and will rendezvous inside the cancel call
+        // so we can delay it. prevent_commit should not get called so we don't care about the arguments
+        TestReapable reapable0 = new TestReapable(uid0, true, false, false, false);
+        // reapable1 will return CANCELLED from cancel and will not rendezvous inside the cancel call
+        // prevent_commit should not get called so we don't care about the arguments
+        TestReapable reapable1 = new TestReapable(uid1, true, false, false, false);
+        // reapable2 will return CANCELLED from cancel and will not rendezvous inside the cancel call
+        // prevent_commit should not get called so we don't care about the arguments
+        TestReapable reapable2 = new TestReapable(uid2, true, false, false, false);
+        // reapable3 will return CANCELLED from cancel and will not rendezvous inside the cancel call
+        // prevent_commit should not get called so we don't care about the arguments
+        TestReapable reapable3 = new TestReapable(uid3, true, false, false, false);
+        // enable a repeatable rendezvous before checking the reapable queue
+        enableRendezvous("reaper1", true);
+        // enable a repeatable rendezvous before processing a timed out reapable
+        // enableRendezvous("reaper2", true);
+        // enable a repeatable rendezvous before scheduling a reapable in the worker queue for cancellation
+        // enableRendezvous("reaper3", true);
+        // enable a repeatable rendezvous before rescheduling a reapable in the worker queue for cancellation
+        // enableRendezvous("reaper4", true);
+        // enable a repeatable rendezvous before interrupting a cancelled reapable
+        // enableRendezvous("reaper5", true);
+        // enable a repeatable rendezvous before marking a worker thread as a zombie
+        // enableRendezvous("reaper6", true);
+        // enable a repeatable rendezvous before marking a reapable as rollback only from the reaper thread
+        // enableRendezvous("reaper7", true);
+        // enable a repeatable rendezvous before checking the worker queue
+        enableRendezvous("reaperworker1", true);
+        // enable a repeatable rendezvous before marking a reapable as cancel
+        // enableRendezvous("reaperworker2", true);
+        // enable a repeatable rendezvous before calling cancel
+        // enableRendezvous("reaperworker3", true);
+        // enable a repeatable rendezvous before marking a reapable as rollback only from the worker thread
+        // enableRendezvous("reaperworker4", true);
 
-	// insert reapables so they timeout at 1 second intervals then
-	// check progress of cancellations and rollbacks
+        // enable a repeatable rendezvous for each of the test reapables which we have marked to
+        // perform a rendezvous
 
-	assertTrue(reaper.insert(reapable1, 1));
+        // enableRendezvous(uid0, true);
 
-	assertTrue(reaper.insert(reapable2, 2));
+        // insert reapables so they timeout at 1 second intervals then
+        // check progress of cancellations and rollbacks
 
-	assertTrue(reaper.insert(reapable3, 3));
+        reaper.insert(reapable0, 1);
 
-	assertTrue(reaper.insert(reapable4, 4));
+        reaper.insert(reapable1, 2);
 
-	// make sure they were all registered
+        reaper.insert(reapable2, 3);
 
-	assertEquals(4, reaper.numberOfTransactions());
-	assertEquals(4, reaper.numberOfTimeouts());
+        reaper.insert(reapable3, 4);
 
-	// 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());
+        // latch the reaper before it checks the queue
+
+        triggerRendezvous("reaper1");
+
+        // make sure they were all registered
+
+        assertEquals(4, reaper.numberOfTransactions());
+        assertEquals(4, reaper.numberOfTimeouts());
+
+        // ensure the first reapable is ready
+
+        triggerWait(1000);
+
+        // let the reaper process the first reapable then latch it again before it checks the queue
+
+        triggerRendezvous("reaper1");
+
+        triggerRendezvous("reaper1");
+
+        // latch the worker before it checks the worker queue
+
+        triggerRendezvous("reaperworker1");
+
+        // let the worker process the first reapable then latch it again before it checks the queue
+
+        triggerRendezvous("reaperworker1");
+
+        triggerRendezvous("reaperworker1");
+
+        // force a termination waiting for the normal timeout periods
+        // byteman rules will ensure that the reaper and reaperworker rendezvous get deleted
+        // under this call
+
+        TransactionReaper.terminate(true);
+
+        assertEquals(0, reaper.numberOfTransactions());
+
+        assertTrue(reapable0.getCancelTried());
+        assertTrue(reapable1.getCancelTried());
+        assertTrue(reapable2.getCancelTried());
+        assertTrue(reapable3.getCancelTried());
     }
-    
-    public void testReaperForce () throws Exception
+
+    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
+        // create test reapables some of which will not respond immediately to cancel requests
 
-        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);
+        Uid uid0 = new Uid();
+        Uid uid1 = new Uid();
+        Uid uid2 = new Uid();
+        Uid uid3 = new Uid();
 
-        // insert reapables so they timeout at 1 second intervals then
-        // check progress of cancellations and rollbacks
+        // reapable0 will return CANCELLED from cancel and will rendezvous inside the cancel call
+        // so we can delay it. prevent_commit should not get called so we don't care about the arguments
+        TestReapable reapable0 = new TestReapable(uid0, true, false, false, false);
+        // reapable1 will return CANCELLED from cancel and will not rendezvous inside the cancel call
+        // prevent_commit should not get called so we don't care about the arguments
+        TestReapable reapable1 = new TestReapable(uid1, true, false, false, false);
+        // reapable2 will return CANCELLED from cancel and will not rendezvous inside the cancel call
+        // prevent_commit should not get called so we don't care about the arguments
+        TestReapable reapable2 = new TestReapable(uid2, true, false, false, false);
+        // reapable3 will return CANCELLED from cancel and will not rendezvous inside the cancel call
+        // prevent_commit should not get called so we don't care about the arguments
+        TestReapable reapable3 = new TestReapable(uid3, true, false, false, false);
+        // enable a repeatable rendezvous before checking the reapable queue
+        enableRendezvous("reaper1", true);
+        // enable a repeatable rendezvous before processing a timed out reapable
+        // enableRendezvous("reaper2", true);
+        // enable a repeatable rendezvous before scheduling a reapable in the worker queue for cancellation
+        // enableRendezvous("reaper3", true);
+        // enable a repeatable rendezvous before rescheduling a reapable in the worker queue for cancellation
+        // enableRendezvous("reaper4", true);
+        // enable a repeatable rendezvous before interrupting a cancelled reapable
+        // enableRendezvous("reaper5", true);
+        // enable a repeatable rendezvous before marking a worker thread as a zombie
+        // enableRendezvous("reaper6", true);
+        // enable a repeatable rendezvous before marking a reapable as rollback only from the reaper thread
+        // enableRendezvous("reaper7", true);
+        // enable a repeatable rendezvous before checking the worker queue
+        enableRendezvous("reaperworker1", true);
+        // enable a repeatable rendezvous before marking a reapable as cancel
+        // enableRendezvous("reaperworker2", true);
+        // enable a repeatable rendezvous before calling cancel
+        // enableRendezvous("reaperworker3", true);
+        // enable a repeatable rendezvous before marking a reapable as rollback only from the worker thread
+        // enableRendezvous("reaperworker4", true);
 
-        assertTrue(reaper.insert(reapable1, 1));
+        // enable a repeatable rendezvous for each of the test reapables which we have marked to
+        // perform a rendezvous
 
-        assertTrue(reaper.insert(reapable2, 2));
+        // enableRendezvous(uid0, true);
 
-        assertTrue(reaper.insert(reapable3, 3));
+        reaper.insert(reapable0, 1);
 
-        assertTrue(reaper.insert(reapable4, 4));
+        reaper.insert(reapable1, 2);
 
+        reaper.insert(reapable2, 3);
+
+        reaper.insert(reapable3, 4);
+
+        // latch the reaper before it checks the queue
+
+        triggerRendezvous("reaper1");
+
         // make sure they were all registered
 
         assertEquals(4, reaper.numberOfTransactions());
         assertEquals(4, reaper.numberOfTimeouts());
 
+        // ensure the first reapable is ready
+
+        triggerWait(1000);
+
+        // let the reaper process the first reapable then latch it again before it checks the queue
+
+        triggerRendezvous("reaper1");
+
+        triggerRendezvous("reaper1");
+
+        // latch the worker before it checks the worker queue
+
+        triggerRendezvous("reaperworker1");
+
+        // let the worker process the first reapable then latch it again before it checks the queue
+
+        triggerRendezvous("reaperworker1");
+
+        triggerRendezvous("reaperworker1");
+
         // force a termination and don't wait for the normal timeout periods
-        
+        // byteman rules will ensure that the reaper and reaperworker rendezvous gte deleted
+        // under this call
+
         TransactionReaper.terminate(false);
-        
+
         assertEquals(0, reaper.numberOfTransactions());
-        
+
+        assertTrue(reapable0.getCancelTried());
         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!
-         */
+        * 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;
+        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;
+            cancelTried = false;
+            rollbackTried = false;
+            running = true;
         }
 
-	public boolean running()
-	{
-	    return getRunning();
-	}
+        public boolean running()
+        {
+            return getRunning();
+        }
 
-	public boolean preventCommit()
-	{
-	    setRollbackTried();
-	    clearRunning();
-	    return doRollback;
-	}
+        public boolean preventCommit()
+        {
+            setRollbackTried();
+            clearRunning();
+            return doRollback;
+        }
 
-	public int cancel()
-	{
-	    boolean interrupted = false;
+        public int cancel()
+        {
+            boolean interrupted = false;
 
-	    setCancelTried();
+            setCancelTried();
 
-	    // track the worker trying to do the cancel so we can
-	    // detect if it becomes a zombie
+            // track the worker trying to do the cancel so we can
+            // detect if it becomes a zombie
 
-	    setCancelThread(Thread.currentThread());
+            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 (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;
-	    }
-	}
+            if (doCancel) {
+                clearRunning();
+                return ActionStatus.ABORTED;
+            } else {
+                return ActionStatus.RUNNING;
+            }
+        }
 
-	public Uid get_uid()
-	{
-	    return uid;
-	}
+        public Uid get_uid()
+        {
+            return uid;
+        }
 
-	private Uid 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;
+        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;
-	}
+        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;
+        }
     }
 }

Modified: labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/transaction/arjunacore/subordinate/SubordinateAtomicAction.java
===================================================================
--- labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/transaction/arjunacore/subordinate/SubordinateAtomicAction.java	2010-05-13 11:36:42 UTC (rev 32860)
+++ labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/transaction/arjunacore/subordinate/SubordinateAtomicAction.java	2010-05-13 11:44:31 UTC (rev 32861)
@@ -61,7 +61,7 @@
 		// if it has a non-negative timeout, add it to the reaper.
 
 		if (timeout > AtomicAction.NO_TIMEOUT)
-			TransactionReaper.transactionReaper(true).insert(this, timeout);
+			TransactionReaper.transactionReaper().insert(this, timeout);
 	}
 
 	/**

Modified: labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaJTS/jtax/classes/com/arjuna/ats/internal/jta/transaction/jts/subordinate/jca/SubordinateAtomicTransaction.java
===================================================================
--- labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaJTS/jtax/classes/com/arjuna/ats/internal/jta/transaction/jts/subordinate/jca/SubordinateAtomicTransaction.java	2010-05-13 11:36:42 UTC (rev 32860)
+++ labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaJTS/jtax/classes/com/arjuna/ats/internal/jta/transaction/jts/subordinate/jca/SubordinateAtomicTransaction.java	2010-05-13 11:44:31 UTC (rev 32861)
@@ -54,7 +54,7 @@
 		
 		if (timeout > 0)
 		{
-			TransactionReaper reaper = TransactionReaper.transactionReaper(true);
+			TransactionReaper reaper = TransactionReaper.transactionReaper();
 			
 			reaper.insert(super.getControlWrapper(), timeout);
 		}

Modified: labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaJTS/jts/classes/com/arjuna/ats/internal/jts/interposition/ServerFactory.java
===================================================================
--- labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaJTS/jts/classes/com/arjuna/ats/internal/jts/interposition/ServerFactory.java	2010-05-13 11:36:42 UTC (rev 32860)
+++ labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaJTS/jts/classes/com/arjuna/ats/internal/jts/interposition/ServerFactory.java	2010-05-13 11:44:31 UTC (rev 32861)
@@ -247,9 +247,6 @@
 		{
 			TransactionReaper reaper = TransactionReaper.transactionReaper();
 
-			if (reaper == null)
-				reaper = TransactionReaper.create();
-
 			reaper.insert(new ServerControlWrapper((ControlImple) tranControl), time_out);
 		}
 

Modified: labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaJTS/jts/classes/com/arjuna/ats/internal/jts/orbspecific/TransactionFactoryImple.java
===================================================================
--- labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaJTS/jts/classes/com/arjuna/ats/internal/jts/orbspecific/TransactionFactoryImple.java	2010-05-13 11:36:42 UTC (rev 32860)
+++ labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaJTS/jts/classes/com/arjuna/ats/internal/jts/orbspecific/TransactionFactoryImple.java	2010-05-13 11:44:31 UTC (rev 32861)
@@ -177,9 +177,6 @@
 
 				TransactionReaper reaper = TransactionReaper.transactionReaper();
 
-				if (reaper == null)
-					reaper = TransactionReaper.create();
-
 				reaper.insert(new ControlWrapper((ControlImple) tranControl), theTimeout);
 			}
 
@@ -659,7 +656,7 @@
 
 		TransactionReaper reaper = TransactionReaper.transactionReaper();
 
-		if (reaper == null)
+		if (reaper.checkingPeriod() == Long.MAX_VALUE)
 			info.reaperTimeout = 0;
 		else
 			info.reaperTimeout = (int) reaper.checkingPeriod();
@@ -716,16 +713,8 @@
 
 						TransactionReaper reaper = TransactionReaper.transactionReaper();
 
-						/*
-						 * If the reaper has not been created yet, then all
-						 * transactions so far must have 0 timeout.
-						 */
+						info.timeout = reaper.getTimeout(ctx);
 
-						if (reaper == null)
-							info.timeout = 0;
-						else
-							info.timeout = (int) reaper.getTimeout(ctx);
-
 						info.numberOfThreads = ctx.getImplHandle().activeThreads();
 
 						return info;

Modified: labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaJTS/jts/classes/com/arjuna/ats/internal/jts/orbspecific/coordinator/ArjunaTransactionImple.java
===================================================================
--- labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaJTS/jts/classes/com/arjuna/ats/internal/jts/orbspecific/coordinator/ArjunaTransactionImple.java	2010-05-13 11:36:42 UTC (rev 32860)
+++ labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaJTS/jts/classes/com/arjuna/ats/internal/jts/orbspecific/coordinator/ArjunaTransactionImple.java	2010-05-13 11:44:31 UTC (rev 32861)
@@ -2138,21 +2138,14 @@
 						 * versions, there's a configurable option.
 						 */
 
-						if (TransactionReaper.transactionReaper() != null)
-						{
-						    if (_propagateRemainingTimeout)
-                            {
-                                long timeInMills = TransactionReaper.transactionReaper().getRemainingTimeoutMills(control);
-                                context.timeout = (int)(timeInMills/1000L);
-                            }
-                            else
-                            {
-                                context.timeout = TransactionReaper.transactionReaper().getTimeout(control);
-                            }
+					    if (_propagateRemainingTimeout)
+                        {
+                            long timeInMills = TransactionReaper.transactionReaper().getRemainingTimeoutMills(control);
+                            context.timeout = (int)(timeInMills/1000L);
                         }
 						else
                         {
-                            context.timeout = 0;
+                            context.timeout = TransactionReaper.transactionReaper().getTimeout(control);
                         }
                     }
 

Modified: labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaJTS/jts/classes/com/arjuna/ats/jts/OTSManager.java
===================================================================
--- labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaJTS/jts/classes/com/arjuna/ats/jts/OTSManager.java	2010-05-13 11:36:42 UTC (rev 32860)
+++ labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/ArjunaJTS/jts/classes/com/arjuna/ats/jts/OTSManager.java	2010-05-13 11:44:31 UTC (rev 32861)
@@ -129,53 +129,47 @@
 	/*
 	 * Just in case control is a top-level transaction, and has
 	 * been registered with the reaper, we need to get it removed.
-	 *
-	 * Don't bother if the reaper has not been created.
 	 */
-    
-	if (TransactionReaper.transactionReaper() != null)
+    Coordinator coord = null;
+
+    try
+    {
+	coord = control.get_coordinator();
+    }
+    catch (Exception e)
+    {
+	coord = null;  // nothing else we can do!
+    }
+
+    if (coord != null)
+    {
+	try
 	{
-	    Coordinator coord = null;
-	
-	    try
+	    if (coord.is_top_level_transaction())
 	    {
-		coord = control.get_coordinator();
-	    }
-	    catch (Exception e)
-	    {
-		coord = null;  // nothing else we can do!
-	    }
+		/*
+		 * Transaction is local, but was registered as
+		 * a Control. If this is a performance hit then
+		 * add explicit add/removes for local instances.
+		 */
 
-	    if (coord != null)
-	    {
-		try
+		if (jtsLogger.logger.isDebugEnabled())
 		{
-		    if (coord.is_top_level_transaction())
-		    {
-			/*
-			 * Transaction is local, but was registered as
-			 * a Control. If this is a performance hit then
-			 * add explicit add/removes for local instances.
-			 */
-
-			if (jtsLogger.logger.isDebugEnabled())
-			{
-			    jtsLogger.logger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC,
-						   com.arjuna.ats.jts.logging.FacilityCode.FAC_OTS, "OTS::destroyControl - removing control from reaper.");
-			}
-
-            // wrap the control so it gets compared against reaper list entries using the correct test
-            PseudoControlWrapper wrapper = new PseudoControlWrapper(control);
-			TransactionReaper.transactionReaper().remove(wrapper);
-		    }
+		    jtsLogger.logger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC,
+					   com.arjuna.ats.jts.logging.FacilityCode.FAC_OTS, "OTS::destroyControl - removing control from reaper.");
 		}
-		catch (Exception e)
-		{
-		}
 
-		coord = null;
+        // wrap the control so it gets compared against reaper list entries using the correct test
+        PseudoControlWrapper wrapper = new PseudoControlWrapper(control);
+		TransactionReaper.transactionReaper().remove(wrapper);
 	    }
 	}
+	catch (Exception e)
+	{
+	}
+
+	coord = null;
+    }
 	
 	/*
 	 * Watch out for conflicts with multiple threads deleting

Modified: labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/atsintegration/classes/com/arjuna/ats/jbossatx/jta/TransactionManagerService.java
===================================================================
--- labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/atsintegration/classes/com/arjuna/ats/jbossatx/jta/TransactionManagerService.java	2010-05-13 11:36:42 UTC (rev 32860)
+++ labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/atsintegration/classes/com/arjuna/ats/jbossatx/jta/TransactionManagerService.java	2010-05-13 11:44:31 UTC (rev 32861)
@@ -193,7 +193,7 @@
         PropertyManagerFactory.getPropertyManager("com.arjuna.ats.propertymanager", "arjuna").addManagementPlugin(new PropertyServiceJMXPlugin());
 
         // Associate transaction reaper with our context classloader.
-        TransactionReaper.create() ;
+        TransactionReaper.transactionReaper() ;
 
         /** Register propagation context manager **/
         try

Modified: labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/atsintegration/classes/com/arjuna/ats/jbossatx/jts/TransactionManagerService.java
===================================================================
--- labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/atsintegration/classes/com/arjuna/ats/jbossatx/jts/TransactionManagerService.java	2010-05-13 11:36:42 UTC (rev 32860)
+++ labs/jbosstm/branches/JBOSSTS_4_6_1_GA_CP/atsintegration/classes/com/arjuna/ats/jbossatx/jts/TransactionManagerService.java	2010-05-13 11:44:31 UTC (rev 32861)
@@ -200,7 +200,7 @@
         PropertyManagerFactory.getPropertyManager("com.arjuna.ats.propertymanager", "arjuna").addManagementPlugin(new PropertyServiceJMXPlugin());
 
         // Associate transaction reaper with our context classloader.
-        TransactionReaper.create() ;
+        TransactionReaper.transactionReaper() ;
 
 		/** Register propagation context manager **/
         try



More information about the jboss-svn-commits mailing list