From: aclement Date: Fri, 4 Sep 2009 18:42:08 +0000 (+0000) Subject: 281654: concurrency problem X-Git-Tag: PRE_J5~1 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=0e2043d0f6ed939e360c78767d9f6e330cc77340;p=aspectj.git 281654: concurrency problem --- diff --git a/bcel-builder/src/org/aspectj/apache/bcel/util/NonCachingClassLoaderRepository.java b/bcel-builder/src/org/aspectj/apache/bcel/util/NonCachingClassLoaderRepository.java index 401e86e76..489475632 100644 --- a/bcel-builder/src/org/aspectj/apache/bcel/util/NonCachingClassLoaderRepository.java +++ b/bcel-builder/src/org/aspectj/apache/bcel/util/NonCachingClassLoaderRepository.java @@ -70,177 +70,193 @@ import org.aspectj.apache.bcel.classfile.ClassParser; import org.aspectj.apache.bcel.classfile.JavaClass; /** - * The repository maintains information about which classes have - * been loaded. - * - * It loads its data from the ClassLoader implementation - * passed into its constructor. - * + * The repository maintains information about which classes have been loaded. + * + * It loads its data from the ClassLoader implementation passed into its constructor. + * * @see org.aspectj.apache.bcel.Repository - * - * @version $Id: NonCachingClassLoaderRepository.java,v 1.4 2008/08/26 15:02:51 aclement Exp $ + * + * @version $Id: NonCachingClassLoaderRepository.java,v 1.5 2009/09/04 18:42:08 aclement Exp $ * @author M. Dahm * @author David Dixon-Peugh * */ -public class NonCachingClassLoaderRepository - implements Repository -{ - private static java.lang.ClassLoader bootClassLoader = null; - - private ClassLoaderReference loaderRef; - private Map loadedClasses = - new SoftHashMap(); // CLASSNAME X JAVACLASS - +public class NonCachingClassLoaderRepository implements Repository { + private static java.lang.ClassLoader bootClassLoader = null; + + private final ClassLoaderReference loaderRef; + private final Map loadedClasses = new SoftHashMap(); // CLASSNAME X JAVACLASS + public static class SoftHashMap extends AbstractMap { - private Map map; - private ReferenceQueue rq = new ReferenceQueue(); - - public SoftHashMap(Map map) { this.map = map; } - public SoftHashMap() { this(new HashMap()); } - public SoftHashMap(Map map, boolean b) { this(map); } - - class SpecialValue extends SoftReference { - private final Object key; - SpecialValue(Object k,Object v) { - super(v,rq); - this.key = k; - } - } - - private void processQueue() { + private Map map; + private ReferenceQueue rq = new ReferenceQueue(); + + public SoftHashMap(Map map) { + this.map = map; + } + + public SoftHashMap() { + this(new HashMap()); + } + + public SoftHashMap(Map map, boolean b) { + this(map); + } + + class SpecialValue extends SoftReference { + private final Object key; + + SpecialValue(Object k, Object v) { + super(v, rq); + this.key = k; + } + } + + private void processQueue() { SpecialValue sv = null; - while ((sv = (SpecialValue)rq.poll())!=null) { + while ((sv = (SpecialValue) rq.poll()) != null) { map.remove(sv.key); } - } - - public Object get(Object key) { - SpecialValue value = (SpecialValue)map.get(key); - if (value==null) return null; - if (value.get()==null) { + } + + public Object get(Object key) { + SpecialValue value = (SpecialValue) map.get(key); + if (value == null) + return null; + if (value.get() == null) { // it got GC'd map.remove(value.key); return null; } else { return value.get(); } - } + } - public Object put(Object k, Object v) { + public Object put(Object k, Object v) { processQueue(); - return map.put(k, new SpecialValue(k,v)); - } + return map.put(k, new SpecialValue(k, v)); + } - public Set entrySet() { + public Set entrySet() { return map.entrySet(); - } - - public void clear() { + } + + public void clear() { processQueue(); Set keys = map.keySet(); for (Iterator iterator = keys.iterator(); iterator.hasNext();) { Object name = iterator.next(); map.remove(name); } - } - - public int size() { + } + + public int size() { processQueue(); return map.size(); - } - - public Object remove(Object k) { + } + + public Object remove(Object k) { processQueue(); - SpecialValue value = (SpecialValue)map.remove(k); - if (value==null) return null; - if (value.get()!=null) { + SpecialValue value = (SpecialValue) map.remove(k); + if (value == null) + return null; + if (value.get() != null) { return value.get(); } return null; - } - } - - public NonCachingClassLoaderRepository(java.lang.ClassLoader loader) { - this.loaderRef = new DefaultClassLoaderReference((loader != null) ? loader : getBootClassLoader()); - } - - public NonCachingClassLoaderRepository(ClassLoaderReference loaderRef) { - this.loaderRef = loaderRef; - } - - private static synchronized java.lang.ClassLoader getBootClassLoader() { - if (bootClassLoader == null) { - bootClassLoader = new URLClassLoader(new URL[0]); - } - return bootClassLoader; - } - - /** - * Store a new JavaClass into this Repository. - */ - public void storeClass( JavaClass clazz ) { - loadedClasses.put( clazz.getClassName(), - clazz ); - clazz.setRepository( this ); - } - - /** - * Remove class from repository - */ - public void removeClass(JavaClass clazz) { - loadedClasses.remove(clazz.getClassName()); - } - - /** - * Find an already defined JavaClass. - */ - public JavaClass findClass( String className ) { - if ( loadedClasses.containsKey( className )) { - return (JavaClass) loadedClasses.get( className ); - } else { - return null; - } - } - - /** - * Lookup a JavaClass object from the Class Name provided. - */ - public JavaClass loadClass( String className ) - throws ClassNotFoundException - { - String classFile = className.replace('.', '/'); - - JavaClass RC = findClass( className ); - if (RC != null) { return RC; } - - try { - InputStream is = - loaderRef.getClassLoader().getResourceAsStream(classFile + ".class"); - - if(is == null) { - throw new ClassNotFoundException(className + " not found."); - } - - ClassParser parser = new ClassParser( is, className ); - RC = parser.parse(); - - storeClass( RC ); - - return RC; - } catch (IOException e) { - throw new ClassNotFoundException( e.toString() ); - } - } - - public JavaClass loadClass(Class clazz) throws ClassNotFoundException { - return loadClass(clazz.getName()); - } - - /** Clear all entries from cache. - */ - public void clear() { - loadedClasses.clear(); - } -} + } + } + + public NonCachingClassLoaderRepository(java.lang.ClassLoader loader) { + this.loaderRef = new DefaultClassLoaderReference((loader != null) ? loader : getBootClassLoader()); + } + + public NonCachingClassLoaderRepository(ClassLoaderReference loaderRef) { + this.loaderRef = loaderRef; + } + + private static synchronized java.lang.ClassLoader getBootClassLoader() { + if (bootClassLoader == null) { + bootClassLoader = new URLClassLoader(new URL[0]); + } + return bootClassLoader; + } + + /** + * Store a new JavaClass into this Repository. + */ + public void storeClass(JavaClass clazz) { + synchronized (loadedClasses) { + loadedClasses.put(clazz.getClassName(), clazz); + } + clazz.setRepository(this); + } + + /** + * Remove class from repository + */ + public void removeClass(JavaClass clazz) { + synchronized (loadedClasses) { + loadedClasses.remove(clazz.getClassName()); + } + } + + /** + * Find an already defined JavaClass. + */ + public JavaClass findClass(String className) { + synchronized (loadedClasses) { + if (loadedClasses.containsKey(className)) { + return (JavaClass) loadedClasses.get(className); + } else { + return null; + } + } + } + + /** + * Clear all entries from cache. + */ + public void clear() { + synchronized (loadedClasses) { + loadedClasses.clear(); + } + } + /** + * Lookup a JavaClass object from the Class Name provided. + */ + public JavaClass loadClass(String className) throws ClassNotFoundException { + + JavaClass javaClass = findClass(className); + if (javaClass != null) { + return javaClass; + } + + javaClass = loadJavaClass(className); + storeClass(javaClass); + + return javaClass; + } + + public JavaClass loadClass(Class clazz) throws ClassNotFoundException { + return loadClass(clazz.getName()); + } + + private JavaClass loadJavaClass(String className) throws ClassNotFoundException { + String classFile = className.replace('.', '/'); + try { + InputStream is = loaderRef.getClassLoader().getResourceAsStream(classFile + ".class"); + + if (is == null) { + throw new ClassNotFoundException(className + " not found."); + } + + ClassParser parser = new ClassParser(is, className); + return parser.parse(); + } catch (IOException e) { + throw new ClassNotFoundException(e.toString()); + } + } + +} diff --git a/bcel-builder/testsrc/org/aspectj/apache/bcel/classfile/tests/AllTests.java b/bcel-builder/testsrc/org/aspectj/apache/bcel/classfile/tests/AllTests.java index cf270b5b0..2979c12e3 100644 --- a/bcel-builder/testsrc/org/aspectj/apache/bcel/classfile/tests/AllTests.java +++ b/bcel-builder/testsrc/org/aspectj/apache/bcel/classfile/tests/AllTests.java @@ -15,24 +15,11 @@ package org.aspectj.apache.bcel.classfile.tests; import junit.framework.Test; import junit.framework.TestSuite; -import org.aspectj.apache.bcel.classfile.tests.AnnotationAccessFlagTest; -import org.aspectj.apache.bcel.classfile.tests.AnnotationDefaultAttributeTest; -import org.aspectj.apache.bcel.classfile.tests.ElementValueGenTest; -import org.aspectj.apache.bcel.classfile.tests.EnclosingMethodAttributeTest; -import org.aspectj.apache.bcel.classfile.tests.EnumAccessFlagTest; -import org.aspectj.apache.bcel.classfile.tests.FieldAnnotationsTest; -import org.aspectj.apache.bcel.classfile.tests.GeneratingAnnotatedClassesTest; -import org.aspectj.apache.bcel.classfile.tests.LocalVariableTypeTableTest; -import org.aspectj.apache.bcel.classfile.tests.MethodAnnotationsTest; -import org.aspectj.apache.bcel.classfile.tests.RuntimeVisibleAnnotationAttributeTest; -import org.aspectj.apache.bcel.classfile.tests.RuntimeVisibleParameterAnnotationAttributeTest; -import org.aspectj.apache.bcel.classfile.tests.VarargsTest; - public class AllTests { public static Test suite() { TestSuite suite = new TestSuite("Tests for BCEL"); - //$JUnit-BEGIN$ + // $JUnit-BEGIN$ suite.addTestSuite(Fundamentals.class); suite.addTestSuite(RuntimeVisibleParameterAnnotationAttributeTest.class); suite.addTestSuite(AnnotationDefaultAttributeTest.class); @@ -40,6 +27,7 @@ public class AllTests { suite.addTestSuite(MethodAnnotationsTest.class); suite.addTestSuite(RuntimeVisibleAnnotationAttributeTest.class); suite.addTestSuite(ClassloaderRepositoryTest.class); + suite.addTestSuite(NonCachingClassLoaderRepositoryTest.class); suite.addTestSuite(EnumAccessFlagTest.class); suite.addTestSuite(LocalVariableTypeTableTest.class); suite.addTestSuite(VarargsTest.class); @@ -54,7 +42,7 @@ public class AllTests { suite.addTestSuite(GenericSignatureParsingTest.class); suite.addTestSuite(GenericsErasureTesting.class); suite.addTestSuite(AnonymousClassTest.class); - //$JUnit-END$ + // $JUnit-END$ return suite; } } \ No newline at end of file diff --git a/bcel-builder/testsrc/org/aspectj/apache/bcel/classfile/tests/NonCachingClassLoaderRepositoryTest.java b/bcel-builder/testsrc/org/aspectj/apache/bcel/classfile/tests/NonCachingClassLoaderRepositoryTest.java new file mode 100644 index 000000000..c777f75de --- /dev/null +++ b/bcel-builder/testsrc/org/aspectj/apache/bcel/classfile/tests/NonCachingClassLoaderRepositoryTest.java @@ -0,0 +1,149 @@ +/* ==================================================================== + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2001 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, + * if any, must include the following acknowledgment: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowledgment may appear in the software itself, + * if and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Apache" and "Apache Software Foundation" and + * "Apache BCEL" must not be used to endorse or promote products + * derived from this software without prior written permission. For + * written permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache", + * "Apache BCEL", nor may "Apache" appear in their name, without + * prior written permission of the Apache Software Foundation. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + */ + +package org.aspectj.apache.bcel.classfile.tests; + +import java.util.ArrayList; +import java.util.List; + +import junit.framework.TestCase; + +import org.aspectj.apache.bcel.classfile.JavaClass; +import org.aspectj.apache.bcel.util.NonCachingClassLoaderRepository; + +/** + * @author Kristian Rosenvold + */ +public class NonCachingClassLoaderRepositoryTest extends TestCase { + + private final NonCachingClassLoaderRepository nonCachingClassLoaderRepository = new NonCachingClassLoaderRepository( + NonCachingClassLoaderRepositoryTest.class.getClassLoader()); + + protected void setUp() throws Exception { + super.setUp(); + } + + abstract class DoneChecker implements Runnable { + private volatile boolean success = false; + private volatile boolean done = false; + + public boolean isSuccess() { + return success; + } + + public boolean isDone() { + return done; + } + + protected void setDone(boolean successFully) { + success = successFully; + done = true; + } + + public abstract void run(); + } + + class Loader extends DoneChecker implements Runnable { + public void run() { + try { + JavaClass javaClass = nonCachingClassLoaderRepository.loadClass(NonCachingClassLoaderRepositoryTest.class + .getCanonicalName()); + nonCachingClassLoaderRepository.clear(); + setDone(true); + } catch (Throwable e) { + e.printStackTrace(System.out); + setDone(false); + } + } + } + + class Clearer extends DoneChecker implements Runnable { + public void run() { + try { + nonCachingClassLoaderRepository.clear(); + setDone(true); + } catch (Throwable e) { + e.printStackTrace(System.out); + setDone(false); + } + } + } + + public void testConcurrency() throws ClassNotFoundException, InterruptedException { + List loaders = new ArrayList(); + int i1 = 1000; + for (int i = 0; i < i1; i++) { + DoneChecker loader = new Loader(); + loaders.add(loader); + new Thread(loader).start(); + DoneChecker clearer = new Clearer(); + loaders.add(clearer); + new Thread(clearer).start(); + } + + for (int i = 0; i < i1 * 2; i++) { + DoneChecker loader = (DoneChecker) loaders.get(i); + while (!loader.isDone()) { + Thread.sleep(10); + } + assertTrue("Loader " + i + " is supposed to run successfully", loader.isSuccess()); + } + + } + + protected void tearDown() throws Exception { + super.tearDown(); + } + +}