import java.util.*; public aspect ASyncContainsAll { //TODO should be a map with WEAK keys actually, for memory safety Set synched = Collections.newSetFromMap(new IdentityHashMap()); after() returning(Collection c): !within(ASyncContainsAll) && call(* Collections.synch*(..)) { synched.add(c); } Object around(Collection t, Collection a): !within(ASyncContainsAll) && call(* Collection.*All(Collection)) && target(t) && args(a) { if(t!=a && //we don't invoke c.containsAll(c) (in this case we have the lock on c already) synched.contains(t) && synched.contains(a) && //t and a were both synchronized !Thread.holdsLock(a)) { //we don't have the lock on a //give error System.err.println("Must synchronize argument collection at: " + thisJoinPointStaticPart.getSourceLocation()); //actually *do* synchronize and proceed synchronized(a) { return proceed(t,a); } } //in any other case, just proceed as usual return proceed(t,a); } } class ASyncContainsAllTest { public static void main(String[] args) { List l1 = Collections.synchronizedList(new ArrayList()); List l2 = Collections.synchronizedList(new ArrayList()); l1.addAll(l2); } }