[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