[jboss-svn-commits] JBL Code SVN: r28535 - in labs/jbosstm/trunk: ArjunaCore/arjuna/tests/byteman-scripts and 2 other directories.

jboss-svn-commits at lists.jboss.org jboss-svn-commits at lists.jboss.org
Tue Jul 28 08:25:10 EDT 2009


Author: adinn
Date: 2009-07-28 08:25:10 -0400 (Tue, 28 Jul 2009)
New Revision: 28535

Modified:
   labs/jbosstm/trunk/ArjunaCore/arjuna/build.xml
   labs/jbosstm/trunk/ArjunaCore/arjuna/tests/byteman-scripts/recovery.txt
   labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/recovery/RecoveryManagerStartStopTest.java
   labs/jbosstm/trunk/ext/Readme
   labs/jbosstm/trunk/ext/byteman.jar
Log:
added extra byteman rules to ensure recovery start stop test waits until threads have actually exited; also imported patched byteman jar to get fixes for issues 16 and 17 -- fixes for JBTM-576

Modified: labs/jbosstm/trunk/ArjunaCore/arjuna/build.xml
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/build.xml	2009-07-28 12:09:08 UTC (rev 28534)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/build.xml	2009-07-28 12:25:10 UTC (rev 28535)
@@ -37,6 +37,16 @@
         </propertyfile>
     </target>
 
+    <!-- one of the recovery tests implements a byteman helper class which extends the default
+         helper so we need to add the byteman library to the classpath when compiling the tests -->
+    <target name="compile-tests">
+        <compile-tests.macro>
+            <additional.classpath>
+                <fileset dir="${global.ext.lib.dir}" includes="${global.ext.libs} byteman.jar"/>
+            </additional.classpath>
+        </compile-tests.macro>
+    </target>
+    
     <target name="run.tests">
         <run.tests.macro>
             <tests>

Modified: labs/jbosstm/trunk/ArjunaCore/arjuna/tests/byteman-scripts/recovery.txt
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/tests/byteman-scripts/recovery.txt	2009-07-28 12:09:08 UTC (rev 28534)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/tests/byteman-scripts/recovery.txt	2009-07-28 12:25:10 UTC (rev 28535)
@@ -48,12 +48,14 @@
 RULE RecoveryManagerStartStopTest create rendezvous
 CLASS com.hp.mwtests.ts.arjuna.recovery.RecoveryManagerStartStopTest
 METHOD testStartStop()
+HELPER com.hp.mwtests.ts.arjuna.recovery.RecoveryManagerStartStopTest$JoinHelper
 AT ENTRY
 BIND NOTHING
 IF TRUE
 DO debug("create rendezvous for PR listener run"),
    createRendezvous("PR listener run", 2),
-   createRendezvous("RecoveryManagerStartStopTest Connection run", 3)
+   createRendezvous("RecoveryManagerStartStopTest Connection run", 3),
+   createJoin("Listener Connection", 2)
 ENDRULE
 
 # rendezvous with the PR listener to make sure it has started before
@@ -95,3 +97,45 @@
    rendezvous("RecoveryManagerStartStopTest Connection run")
 ENDRULE
 
+# The listener code gets notified that its connection thread is exiting but does not
+# bother to join the thread at shutdown since it would also be forced to join all other
+# threads during normal operation. it avoids doing this because it is waiting in an
+# accept and so can only be woken via an interrupt -- a pointless waste of CPU. however for
+# testing we want to be sure that the connection thread has actually exited. so we
+# use a rule and rule helper class to join the connection threads from the listener
+# thread. the connection threads register themselves when they run the listener
+# callback by calling the helper method joinEnlist(key). the listener thread joins
+# N enlisted threads by calling the helper method joinWait(key, N). The Object
+# supplied as key must match.
+#
+# the helper methods may be good candidates for inclusion in the default
+# helper API. for now they are implemented on an inner class of the test
+# class
+#
+# joinEnlist(o) adds the current thread to a set identified by o and returns,
+# allowing the enlisting thread to exit.
+#
+# joinWait(o, n) waits on the set identified by o until it has N members at
+# which point it clears the set and calls join on each of the threads in the set.
+
+RULE listener join wait
+CLASS com.arjuna.ats.internal.arjuna.recovery.Listener
+METHOD run()
+HELPER com.hp.mwtests.ts.arjuna.recovery.RecoveryManagerStartStopTest$JoinHelper
+AT RETURN
+BIND listener : Listener = $0
+IF isJoin("Listener Connection", 2)
+DO debug("Listener Connection calling joinWait 2 " + Thread.currentThread()),
+   joinWait("Listener Connection", 2),
+   debug("Listener Connection called joinWait 2 " + Thread.currentThread())
+ENDRULE
+
+RULE connection join enlist
+CLASS com.arjuna.ats.internal.arjuna.recovery.Listener
+METHOD removeConnection
+HELPER com.hp.mwtests.ts.arjuna.recovery.RecoveryManagerStartStopTest$JoinHelper
+AT RETURN
+BIND listener : Listener = $0
+IF joinEnlist("Listener Connection")
+DO debug("Listener Connection joinEnlist " + Thread.currentThread())
+ENDRULE

Modified: labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/recovery/RecoveryManagerStartStopTest.java
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/recovery/RecoveryManagerStartStopTest.java	2009-07-28 12:09:08 UTC (rev 28534)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/recovery/RecoveryManagerStartStopTest.java	2009-07-28 12:25:10 UTC (rev 28535)
@@ -22,19 +22,21 @@
 package com.hp.mwtests.ts.arjuna.recovery;
 
 import com.arjuna.ats.arjuna.recovery.RecoveryManager;
-import com.arjuna.ats.internal.arjuna.recovery.PeriodicRecovery;
 import com.arjuna.ats.arjuna.common.Environment;
 
 import java.net.Socket;
 import java.net.InetAddress;
-import java.net.ServerSocket;
 import java.net.SocketException;
 import java.io.*;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.HashMap;
+import java.util.LinkedList;
 
 import org.junit.Test;
 import static org.junit.Assert.*;
+import org.jboss.byteman.rule.helper.Helper;
+import org.jboss.byteman.rule.Rule;
 
 /**
  * test to ensure that the recovery manager cleans up all its threads when terminated
@@ -248,4 +250,219 @@
             }
         }
     }
+
+    /**
+     * helper class for use in byteman rules to ensure that the listener class
+     * actually joins the connections it closes -- it does not need to as far as the
+     * TS code is concerned but we cannot check that the test has run correctly without
+     * adding this extra join
+     */
+
+    public static class JoinHelper extends Helper
+    {
+        public JoinHelper(Rule rule)
+        {
+            super(rule);
+        }
+
+        public boolean createJoin(Object key, int max)
+        {
+            if (max <= 0) {
+                return false;
+            }
+
+            synchronized(joinerMap) {
+                if (joinerMap.get(key) != null) {
+                    return false;
+                }
+                joinerMap.put(key, new Joiner(max));
+            }
+
+            return true;
+        }
+
+        public boolean isJoin(Object key, int max)
+        {
+            synchronized(joinerMap) {
+                Joiner joiner = joinerMap.get(key);
+
+                if (joiner == null || joiner.getMax() != max) {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+
+        public boolean joinEnlist(Object key)
+        {
+            Joiner joiner;
+            synchronized (joinerMap)
+            {
+                joiner = joinerMap.get(key);
+            }
+
+            if (joiner == null) {
+                return false;
+            }
+
+            Thread current = Thread.currentThread();
+
+            switch (joiner.addChild(current)) {
+                case DUPLICATE:
+                case EXCESS:
+                {
+                    // failed to add  child
+                    return false;
+                }
+                case ADDED:
+                case FILLED:
+                {
+                    // added child but parent was not waiting so leave joiner in the map for parent to find
+                    return true;
+                }
+                case DONE:
+                default:
+                {
+                    // added child and parent was waiting so remove joiner from map now
+                    synchronized (joinerMap) {
+                        joinerMap.remove(joiner);
+                    }
+                    return true;
+                }
+            }
+        }
+
+        public boolean joinWait(Object key, int count)
+        {
+            Joiner joiner;
+            synchronized (joinerMap)
+            {
+                joiner = joinerMap.get(key);
+            }
+
+            if (joiner == null || joiner.getMax() != count) {
+                return false;
+            }
+
+            Thread current = Thread.currentThread();
+
+            if (joiner.joinChildren(current)) {
+                // successfully joined all child threads so remove joiner form map
+                synchronized (joinerMap) {
+                    joinerMap.remove(joiner);
+                }
+                return true;
+            } else {
+                // hmm, another thread must have done the join so leave it do the remove
+                return true;
+            }
+        }
+
+    }
+
+    private static HashMap<Object, Joiner> joinerMap = new HashMap<Object, Joiner>();
+
+    /**
+     * status values returned from child add method
+     */
+    private enum Status {
+        /**
+         * a DUPLICATE status is returned when a child fails to add itself to the join list because it is already present
+         */
+        DUPLICATE,
+        /**
+         * an EXCESS status is returned when a child fails to add itself to a join list because it already contains the
+         * expected number of children
+         */
+        EXCESS,
+        /**
+         * an ADDED status is returned when a child successfully adds itself to the join list but without reaching
+         * the expected number of children
+         */
+        ADDED,
+        /**
+         * a FILLED status is returned when a child successfully adds itself to the join list reaching the expected
+         * number of children but there is no parent thread waiting for the children
+         */
+        FILLED,
+        /**
+         * a DONE  status is returned when a child successfully adds itself to the join list reaching the expected
+         * number of children and there is a parent thread waiting for the children
+         */
+        DONE
+    }
+
+    private static class Joiner
+    {
+
+        private List<Thread> children;
+        private int max;
+        private Thread parent;
+
+        public Joiner(int max)
+        {
+            this.max = max;
+            this.children = new LinkedList<Thread>();
+            this.parent =  null;
+        }
+
+        public int getMax()
+        {
+            return max;
+        }
+
+        public synchronized Status addChild(Thread thread)
+        {
+            if (children.contains(thread)) {
+                return Status.DUPLICATE;
+            }
+
+            int size = children.size();
+
+            if (size == max) {
+                return Status.EXCESS;
+            }
+
+            children.add(thread);
+            size++;
+
+            if (size == max) {
+                if (parent ==  null) {
+                    return Status.FILLED;
+                } else {
+                    return Status.DONE;
+                }
+            }
+            return Status.ADDED;
+        }
+
+        public boolean joinChildren(Thread thread)
+        {
+            synchronized (this) {
+                if (parent != null) {
+                    return false;
+                }
+                while (children.size() < max) {
+                    try {
+                        wait();
+                    } catch (InterruptedException e) {
+                        // do nothing
+                    }
+                }
+            }
+            // since we are the parent and the waiting is over we don't need to stay synchronized
+            for (int i = 0; i < max;) {
+                Thread child = children.get(i);
+                try {
+                    child.join();
+                } catch (InterruptedException e) {
+                    // try again
+                    break;
+                }
+                i++;
+            }
+            return true;
+        }
+    }
 }

Modified: labs/jbosstm/trunk/ext/Readme
===================================================================
--- labs/jbosstm/trunk/ext/Readme	2009-07-28 12:09:08 UTC (rev 28534)
+++ labs/jbosstm/trunk/ext/Readme	2009-07-28 12:25:10 UTC (rev 28535)
@@ -34,4 +34,4 @@
 xalan.jar
 xercesImpl.jar		Xerces 2.3.0
 xmlParserAPIs.jar	Xerces 2.3.0
-byteman.jar		1.0.3.SNAPSHOT (15 July 2009)
+byteman.jar		1.0.3.SNAPSHOT (28 July 2009)

Modified: labs/jbosstm/trunk/ext/byteman.jar
===================================================================
(Binary files differ)



More information about the jboss-svn-commits mailing list