[jboss-svn-commits] JBL Code SVN: r30330 - in labs/jbossrules/trunk: drools-core/src/main/java/org/drools/rule and 1 other directory.
jboss-svn-commits at lists.jboss.org
jboss-svn-commits at lists.jboss.org
Tue Nov 24 21:59:08 EST 2009
Author: tirelli
Date: 2009-11-24 21:59:08 -0500 (Tue, 24 Nov 2009)
New Revision: 30330
Modified:
labs/jbossrules/trunk/drools-compiler/src/test/java/org/drools/integrationtests/MultithreadTest.java
labs/jbossrules/trunk/drools-core/src/main/java/org/drools/rule/CompositeClassLoader.java
labs/jbossrules/trunk/drools-core/src/main/java/org/drools/rule/JavaDialectRuntimeData.java
Log:
JBRULES-2276 JBRULES-2225 : fixing deadlock on classloaders
Modified: labs/jbossrules/trunk/drools-compiler/src/test/java/org/drools/integrationtests/MultithreadTest.java
===================================================================
--- labs/jbossrules/trunk/drools-compiler/src/test/java/org/drools/integrationtests/MultithreadTest.java 2009-11-24 18:50:31 UTC (rev 30329)
+++ labs/jbossrules/trunk/drools-compiler/src/test/java/org/drools/integrationtests/MultithreadTest.java 2009-11-25 02:59:08 UTC (rev 30330)
@@ -19,14 +19,17 @@
package org.drools.integrationtests;
import java.io.InputStreamReader;
+import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
import java.util.Vector;
import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.atomic.AtomicInteger;
import junit.framework.TestCase;
+import org.drools.Cheese;
import org.drools.Child;
import org.drools.GrandParent;
import org.drools.Order;
@@ -34,7 +37,12 @@
import org.drools.RuleBase;
import org.drools.RuleBaseFactory;
import org.drools.StatefulSession;
+import org.drools.StatelessSession;
+import org.drools.compiler.DrlParser;
+import org.drools.compiler.DroolsParserException;
import org.drools.compiler.PackageBuilder;
+import org.drools.lang.descr.PackageDescr;
+import org.drools.rule.Package;
/**
* This is a test case for multi-thred issues
@@ -69,7 +77,7 @@
builder.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "test_MultithreadRulebaseSharing.drl" ) ) );
RuleBase ruleBase = RuleBaseFactory.newRuleBase();
ruleBase.addPackage( builder.getPackage() );
- ruleBase = SerializationHelper.serializeObject(ruleBase);
+ ruleBase = SerializationHelper.serializeObject( ruleBase );
final Thread[] t = new Thread[THREAD_COUNT];
final RulebaseRunner[] r = new RulebaseRunner[THREAD_COUNT];
for ( int i = 0; i < t.length; i++ ) {
@@ -202,7 +210,7 @@
errorList.isEmpty() );
} catch ( Exception e ) {
e.printStackTrace();
- fail( "No exception should have been raised: "+e.getMessage());
+ fail( "No exception should have been raised: " + e.getMessage() );
}
}
@@ -214,45 +222,163 @@
final RuleBase ruleBase = RuleBaseFactory.newRuleBase();
ruleBase.addPackage( packageBuilder.getPackage() );
final Vector errors = new Vector();
-
+
final Thread t[] = new Thread[THREAD_COUNT];
- for(int j=0;j<10;j++)
- {
- for (int i = 0; i < t.length; i++) {
+ for ( int j = 0; j < 10; j++ ) {
+ for ( int i = 0; i < t.length; i++ ) {
t[i] = new Thread() {
public void run() {
try {
final int ITERATIONS = 300;
StatefulSession session = ruleBase.newStatefulSession();
List results = new ArrayList();
- session.setGlobal( "results", results );
- for( int k = 0; k < ITERATIONS; k++ ) {
+ session.setGlobal( "results",
+ results );
+ for ( int k = 0; k < ITERATIONS; k++ ) {
session.insert( new Order() );
}
session.fireAllRules();
session.dispose();
- if( results.size() != ITERATIONS ) {
- errors.add( "Rules did not fired correctly. Expected: "+ITERATIONS+". Actual: "+results.size() );
+ if ( results.size() != ITERATIONS ) {
+ errors.add( "Rules did not fired correctly. Expected: " + ITERATIONS + ". Actual: " + results.size() );
}
- } catch( Exception ex ) {
+ } catch ( Exception ex ) {
ex.printStackTrace();
errors.add( ex );
}
}
-
+
};
t[i].start();
}
- for (int i = 0; i < t.length; i++) {
+ for ( int i = 0; i < t.length; i++ ) {
t[i].join();
}
}
- if( !errors.isEmpty() ) {
- fail(" Errors occured during execution ");
+ if ( !errors.isEmpty() ) {
+ fail( " Errors occured during execution " );
}
} catch ( Exception e ) {
e.printStackTrace();
fail( "Should not raise exception" );
}
}
+
+ class Runner
+ implements
+ Runnable {
+ private final long TIME_SPAN;
+ private final StatelessSession session;
+ private final AtomicInteger count;
+
+ public Runner(long BASE_TIME,
+ StatelessSession session,
+ final AtomicInteger count) {
+ this.TIME_SPAN = BASE_TIME;
+ this.session = session;
+ this.count = count;
+ }
+
+ public void run() {
+ System.out.println( Thread.currentThread().getName() + " starting..." );
+ try {
+ count.incrementAndGet();
+ long time = System.currentTimeMillis();
+ while ( (System.currentTimeMillis() - time) < TIME_SPAN ) {
+ //System.out.println( Thread.currentThread().getName() + ": added package at " + (System.currentTimeMillis() - time) );
+ for ( int j = 0; j < 100; j++ ) {
+ session.execute( getFacts() );
+ }
+ //System.out.println( Thread.currentThread().getName() + ": executed rules at " + (System.currentTimeMillis() - time) );
+ }
+ } catch ( Exception ex ) {
+ ex.printStackTrace();
+ }
+ if ( count.decrementAndGet() == 0 ) {
+ synchronized ( MultithreadTest.this ) {
+ MultithreadTest.this.notifyAll();
+ }
+ }
+ System.out.println( Thread.currentThread().getName() + " exiting..." );
+ }
+
+ private Cheese[] getFacts() {
+ final int SIZE = 100;
+ Cheese[] facts = new Cheese[SIZE];
+
+ for ( int i = 0; i < facts.length; i++ ) {
+ facts[i] = new Cheese();
+ facts[i].setPrice( i );
+ facts[i].setOldPrice( i );
+ }
+ return facts;
+ }
+ }
+
+ public void testSharedPackagesThreadDeadLock() throws Exception {
+ final int THREADS = Integer.parseInt( System.getProperty( "test.threads",
+ "10" ) );
+ final long BASE_TIME = Integer.parseInt( System.getProperty( "test.time",
+ "15" ) ) * 1000;
+
+ final AtomicInteger count = new AtomicInteger( 0 );
+
+ final Package[] pkgs = buildPackages();
+ for ( int i = 0; i < THREADS; i++ ) {
+ RuleBase ruleBase = createRuleBase( pkgs );
+ StatelessSession session = createSession( ruleBase );
+ new Thread( new Runner( BASE_TIME,
+ session,
+ count ) ).start();
+ }
+ synchronized ( this ) {
+ wait();
+ }
+ }
+
+ private RuleBase createRuleBase(Package[] pkgs) {
+ RuleBase ruleBase = RuleBaseFactory.newRuleBase();
+ for ( Package pkg : pkgs ) {
+ ruleBase.addPackage( pkg );
+ }
+ return ruleBase;
+ }
+
+ private StatelessSession createSession(RuleBase ruleBase) {
+ StatelessSession session = ruleBase.newStatelessSession();
+ return session;
+ }
+
+ private Package[] buildPackages() throws Exception {
+ final String KEY = "REPLACE";
+ final int SIZE = 100;
+ final Package[] pkgs = new Package[SIZE];
+ final String DRL = "package org.drools\n" + " no-loop true\n" + " dialect \"java\"\n" + "rule \"" + KEY + "\"\n" + "salience 1\n" + "when\n" + " $fact:Cheese(price == " + KEY + ", oldPrice not in (11,5))\n" + // thread-lock
+ "then\n" + " //$fact.excludeProduct(" + KEY + ", 1, null, null);\n" + "end\n";
+ System.out.print( "Building " + pkgs.length + " packages" );
+ for ( int i = 0; i < pkgs.length; i++ ) {
+ pkgs[i] = getPackage( DRL.replaceAll( KEY,
+ Integer.toString( i ) ) );
+ System.out.print( "." );
+ }
+ System.out.println();
+ return pkgs;
+ }
+
+ private static Package getPackage(String drl) throws Exception {
+ PackageBuilder pkgBuilder = new PackageBuilder();
+ pkgBuilder.addPackageFromDrl( new StringReader( drl ) );
+ if ( pkgBuilder.hasErrors() ) {
+ StringBuilder sb = new StringBuilder();
+ for ( Object obj : pkgBuilder.getErrors() ) {
+ if ( sb.length() > 0 ) {
+ sb.append( '\n' );
+ }
+ sb.append( obj );
+ }
+ throw new DroolsParserException( sb.toString() );
+ }
+ return pkgBuilder.getPackage();
+ }
+
}
Modified: labs/jbossrules/trunk/drools-core/src/main/java/org/drools/rule/CompositeClassLoader.java
===================================================================
--- labs/jbossrules/trunk/drools-core/src/main/java/org/drools/rule/CompositeClassLoader.java 2009-11-24 18:50:31 UTC (rev 30329)
+++ labs/jbossrules/trunk/drools-core/src/main/java/org/drools/rule/CompositeClassLoader.java 2009-11-25 02:59:08 UTC (rev 30330)
@@ -1,15 +1,19 @@
package org.drools.rule;
import java.io.InputStream;
-import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+/**
+ * A classloader that loads from a (dynamic) list of sub-classloaders.
+ */
public class CompositeClassLoader extends ClassLoader
implements
DroolsClassLoader {
- private final List<ClassLoader> classLoaders = new ArrayList<ClassLoader>();
+ /* Assumption: modifications are really rare, but iterations are frequent. */
+ private final List<ClassLoader> classLoaders = new CopyOnWriteArrayList<ClassLoader>();
private boolean hasParent = false;
public CompositeClassLoader(final ClassLoader parentClassLoader) {
@@ -19,8 +23,12 @@
}
}
- public void addClassLoader(final ClassLoader classLoader) {
- // don't add duplicate ClasslLaders;
+ public synchronized void addClassLoader(final ClassLoader classLoader) {
+ /* NB: we need synchronized here even though we use a COW list:
+ * two threads may try to add the same new class loader, so we need
+ * to protect over a bigger area than just a single iteration.
+ */
+ // don't add duplicate ClassLoaders;
for ( final ClassLoader cl : this.classLoaders ) {
if ( cl == classLoader ) {
return;
@@ -30,16 +38,19 @@
}
- public void removeClassLoader(final ClassLoader classLoader) {
+ public synchronized void removeClassLoader(final ClassLoader classLoader) {
+ /* synchronized to protect against concurrent runs of
+ * addClassLoader(x) and removeClassLoader(x).
+ */
classLoaders.remove( classLoader );
}
/**
* Search the list of child ClassLoaders
*/
- public Class fastFindClass(final String name) {
+ public Class<?> fastFindClass(final String name) {
for ( final ClassLoader classLoader : this.classLoaders ) {
- final Class cls = ((DroolsClassLoader) classLoader).fastFindClass( name );
+ final Class<?> cls = ((DroolsClassLoader) classLoader).fastFindClass( name );
if ( cls != null ) {
return cls;
}
@@ -51,10 +62,10 @@
* This ClassLoader never has classes of it's own, so only search the child ClassLoaders
* and the parent ClassLoader if one is provided
*/
- public synchronized Class loadClass(final String name,
+ public Class<?> loadClass(final String name,
final boolean resolve) throws ClassNotFoundException {
// search the child ClassLoaders
- Class cls = fastFindClass( name );
+ Class<?> cls = fastFindClass( name );
// still not found so search the parent ClassLoader
if ( this.hasParent && cls == null ) {
@@ -94,10 +105,9 @@
/**
* This ClassLoader never has classes of it's own, so only search the child ClassLoaders
*/
- protected Class findClass(final String name) throws ClassNotFoundException {
- final Class cls = fastFindClass( name );
+ protected Class<?> findClass(final String name) throws ClassNotFoundException {
+ final Class<?> cls = fastFindClass( name );
-
if ( cls == null ) {
throw new ClassNotFoundException( name );
}
Modified: labs/jbossrules/trunk/drools-core/src/main/java/org/drools/rule/JavaDialectRuntimeData.java
===================================================================
--- labs/jbossrules/trunk/drools-core/src/main/java/org/drools/rule/JavaDialectRuntimeData.java 2009-11-24 18:50:31 UTC (rev 30329)
+++ labs/jbossrules/trunk/drools-core/src/main/java/org/drools/rule/JavaDialectRuntimeData.java 2009-11-25 02:59:08 UTC (rev 30330)
@@ -448,9 +448,9 @@
return cls;
}
- public synchronized Class loadClass(final String name,
+ public Class<?> loadClass(final String name,
final boolean resolve) throws ClassNotFoundException {
- Class cls = fastFindClass( name );
+ Class<?> cls = fastFindClass( name );
if ( cls == null ) {
final ClassLoader parent = getParent();
@@ -468,7 +468,7 @@
return cls;
}
- protected Class findClass(final String name) throws ClassNotFoundException {
+ protected Class<?> findClass(final String name) throws ClassNotFoundException {
return fastFindClass( name );
}
More information about the jboss-svn-commits
mailing list