Bug 337855 - some operations on a world do not allow multithreading through the same instance
Summary: some operations on a world do not allow multithreading through the same instance
Status: RESOLVED FIXED
Alias: None
Product: AspectJ
Classification: Tools
Component: Compiler (show other bugs)
Version: 1.6.11   Edit
Hardware: PC Windows 7
: P3 normal (vote)
Target Milestone: 1.6.11   Edit
Assignee: aspectj inbox CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2011-02-22 11:06 EST by Andrew Clement CLA
Modified: 2011-03-23 10:54 EDT (History)
2 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Andrew Clement CLA 2011-02-22 11:06:59 EST
Normally the world is only used by a single thread, when either compile time weaving or loadtime weaving.  Multiple threads are allowed to have their own world instances but there are problems if multiple threads attempt to share a world.  One such case was shown by this test program from Thorsten Gast:

--- sample program start ---
import java.util.concurrent.CountDownLatch;

import org.aspectj.weaver.ReferenceType;
import org.aspectj.weaver.ResolvedType;
import org.aspectj.weaver.TypeFactory;
import org.aspectj.weaver.World;
import org.aspectj.weaver.reflect.Java15ReflectionBasedReferenceTypeDelegate;
import org.aspectj.weaver.reflect.ReflectionWorld;

public class Aspectj {

   public static void main(String[] args) {
       new Aspectj();
   }

   static World inAWorld = new ReflectionWorld(Aspectj.class.getClassLoader());

   static ReferenceType aBaseType;
   static {
       aBaseType = new ReferenceType("test", inAWorld);
       Java15ReflectionBasedReferenceTypeDelegate delegate = new Java15ReflectionBasedReferenceTypeDelegate();
       delegate.initialize(aBaseType, String.class, Aspectj.class.getClassLoader(), inAWorld);
       aBaseType.setDelegate(delegate);
   }

   public Aspectj() {
       int N = 25;
       CountDownLatch startSignal = new CountDownLatch(1);
       CountDownLatch doneSignal = new CountDownLatch(N);

       for (int i = 0; i < N; ++i)
           new Thread(new Worker(startSignal, doneSignal)).start();

       startSignal.countDown();
       try {
           doneSignal.await();
       } catch  (InterruptedException e){
           e.printStackTrace();
       }
   }

   class Worker implements Runnable {
       private final CountDownLatch startSignal;
       private final CountDownLatch doneSignal;

       Worker(CountDownLatch startSignal, CountDownLatch doneSignal) {
           this.startSignal = startSignal;
           this.doneSignal = doneSignal;
       }

       public void run() {
           try {
               startSignal.await();
               doWork();
               doneSignal.countDown();
           } catch (InterruptedException ex) {
               ex.printStackTrace();
           }
       }

       void doWork() {
           for (int i = 0; i < 10; i++) {
               // using TypeFactory
               TypeFactory.createParameterizedType(aBaseType, new ResolvedType[0], inAWorld);

               // or creating ReferenceTypes directly
               //new ReferenceType(aBaseType, new ResolvedType[0], inAWorld);
           }
       }
   }

}
--- sample program end ---

This can fail with:

   at java.util.ArrayList.add(ArrayList.java:352)
       at org.aspectj.weaver.ReferenceType.addDependentType(ReferenceType.java:115)
       at org.aspectj.weaver.ReferenceType.<init>(ReferenceType.java:95)
       at org.aspectj.weaver.TypeFactory.createParameterizedType(TypeFactory.java:43)

I believe the sharing of a world across threads can occur in a Spring environment, so this needs sorting out.
Comment 1 Andrew Clement CLA 2011-02-22 11:09:38 EST
I fixed the stack trace here by synchronizing one of the methods involved.  However, that got me thinking about type resolution and multiple threads.  It could well happen that multiple threads attempt resolution for the same type at the same time - without some care they will both create new ResolvedType objects (different ones for the same thing) and put them into the world.  Whichever is last will become the current one but it seems better to avoid building duplicates if we can.  To police this I added a synchronized block in resolution around the code that builds new instances.  This block won't be hit for resolution cases that find what they are looking for in the typemap cache, it will only be hit if there is a cache miss - so shouldn't cause too much of a problem.
Comment 2 Thorsten Gast CLA 2011-02-22 11:41:38 EST
I tested this issue with aspectj-DEVELOPMENT-20110221172044.jar and it looks good for me. Currently I could only test it with the sample porgram I posted in the mailing list. Testing this within our spring project would be some effort.
Comment 3 Andrew Clement CLA 2011-02-22 13:25:21 EST
thanks for trying it out. let me know if there are issues with your spring setup.  This change just made it into 1.6.11.m2 that I built this morning.
Comment 4 Eric Sirianni CLA 2011-03-22 15:10:17 EDT
FYI - I am hitting a similar issue with aspectjweaver-1.6.8.  However, I am getting a NullPointerException instead of an IndexOutOfBoundsException.

https://jira.springsource.org/browse/SPR-8070

I tried to write a simple multithreaded test to reproduce the issue like Thorsten did but was unsuccessful.

Andy - if you have a minute, maybe you can take a peek at SPR-8070 and decide based on eyeballing it if you think it is the same issue.  In the meantime, I will try 1.6.11.m2 to see if it fixes our issue.  Thanks.
Comment 5 Eric Sirianni CLA 2011-03-22 16:09:43 EDT
I still get the NullPointerException with aspectjweaver-1.6.11

Here are the new line numbers for the stacktrace if that helps.  Should I open up a new bug for this or is it OK to piggyback on this one?

Caused by: java.lang.NullPointerException: null
        at org.aspectj.weaver.World.resolve(World.java:278) ~[aspectjweaver.jar:1.6.11]
        at org.aspectj.weaver.World.resolve(World.java:218) ~[aspectjweaver.jar:1.6.11]
        at org.aspectj.weaver.World.resolve(World.java:253) ~[aspectjweaver.jar:1.6.11]
        at org.aspectj.weaver.TypeFactory.createParameterizedType(TypeFactory.java:42) ~[aspectjweaver.jar:1.6.11]
        at org.aspectj.weaver.reflect.JavaLangTypeToResolvedTypeConverter.fromType(JavaLangTypeToResolvedTypeConverter.java:88) ~[aspectjweaver.jar:1.6.11]
        at org.aspectj.weaver.reflect.Java15ReflectionBasedReferenceTypeDelegate.getSuperclass(Java15ReflectionBasedReferenceTypeDelegate.java:148) ~[aspectjweaver.jar:1.6.11]
        at org.aspectj.weaver.ReferenceType.getSuperclass(ReferenceType.java:906) ~[aspectjweaver.jar:1.6.11]
        at org.aspectj.weaver.patterns.KindedPointcut.fastMatch(KindedPointcut.java:144) ~[aspectjweaver.jar:1.6.11]
        at org.aspectj.weaver.internal.tools.PointcutExpressionImpl.couldMatchJoinPointsInType(PointcutExpressionImpl.java:82) ~[aspectjweaver.jar:1.6.11]
        at org.springframework.aop.aspectj.AspectJExpressionPointcut.matches(AspectJExpressionPointcut.java:233) ~[org.springframework.aop.jar:3.0.3.RELEASE]
Comment 6 Andrew Clement CLA 2011-03-23 10:54:06 EDT
lets open a new bug