forked from phoedos/pmd
Improve DeadlockTest
This commit is contained in:
parent
733ac4bba0
commit
9dcb697f13
@ -4,12 +4,19 @@
|
||||
|
||||
package net.sourceforge.pmd.lang.java.symbols;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertAll;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.Timeout;
|
||||
|
||||
import net.sourceforge.pmd.lang.java.JavaParsingHelper;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTClassType;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
|
||||
|
||||
/**
|
||||
@ -38,7 +45,21 @@ class DeadlockTest {
|
||||
/*
|
||||
Deadlock:
|
||||
t1 -> locks parse for Outer.Inner and waits for parse lock for Outer
|
||||
├─ t1 waiting on : ParseLock{name=LazyClassSignature:net/sourceforge/pmd/lang/java/symbols/DeadlockTest$Outer$Inner[<T:Ljava/lang/Object;>Ljava/lang/Object;],status=NOT_PARSED}
|
||||
└─ t1 locked : ParseLock{name=LazyClassSignature:net/sourceforge/pmd/lang/java/symbols/DeadlockTest$Outer$Inner[<T:Ljava/lang/Object;>Ljava/lang/Object;],status=NOT_PARSED}
|
||||
└─ t1 waiting on : ParseLock{name=LazyClassSignature:net/sourceforge/pmd/lang/java/symbols/DeadlockTest$Outer[<T:Ljava/lang/Object;>Ljava/lang/Object;Lnet/sourceforge/pmd/lang/java/symbols/DeadlockTest$GenericInterface<Lnet/sourceforge/pmd/lang/java/symbols/DeadlockTest$Outer<TT;>;Lnet/sourceforge/pmd/lang/java/symbols/DeadlockTest$GenericClass<TT;>;>;],status=BEING_PARSED}
|
||||
t2 -> locks parse for Outer, locks parse for GenericInterface and then waits for parse lock for Outer.Inner
|
||||
├─ t2 waiting on : ParseLock{name=LazyClassSignature:net/sourceforge/pmd/lang/java/symbols/DeadlockTest$Outer[<T:Ljava/lang/Object;>Ljava/lang/Object;Lnet/sourceforge/pmd/lang/java/symbols/DeadlockTest$GenericInterface<Lnet/sourceforge/pmd/lang/java/symbols/DeadlockTest$Outer<TT;>;Lnet/sourceforge/pmd/lang/java/symbols/DeadlockTest$GenericClass<TT;>;>;],status=NOT_PARSED}
|
||||
└─ t2 locked : ParseLock{name=LazyClassSignature:net/sourceforge/pmd/lang/java/symbols/DeadlockTest$Outer[<T:Ljava/lang/Object;>Ljava/lang/Object;Lnet/sourceforge/pmd/lang/java/symbols/DeadlockTest$GenericInterface<Lnet/sourceforge/pmd/lang/java/symbols/DeadlockTest$Outer<TT;>;Lnet/sourceforge/pmd/lang/java/symbols/DeadlockTest$GenericClass<TT;>;>;],status=NOT_PARSED}
|
||||
├─ t2 waiting on : ParseLock{name=LazyClassSignature:net/sourceforge/pmd/lang/java/symbols/DeadlockTest[null],status=NOT_PARSED}
|
||||
├─ t2 locked : ParseLock{name=LazyClassSignature:net/sourceforge/pmd/lang/java/symbols/DeadlockTest[null],status=NOT_PARSED}
|
||||
├─ t2 released : ParseLock{name=LazyClassSignature:net/sourceforge/pmd/lang/java/symbols/DeadlockTest[null],status=FULL}
|
||||
├─ t2 waiting on : ParseLock{name=LazyClassSignature:net/sourceforge/pmd/lang/java/symbols/DeadlockTest$Outer[<T:Ljava/lang/Object;>Ljava/lang/Object;Lnet/sourceforge/pmd/lang/java/symbols/DeadlockTest$GenericInterface<Lnet/sourceforge/pmd/lang/java/symbols/DeadlockTest$Outer<TT;>;Lnet/sourceforge/pmd/lang/java/symbols/DeadlockTest$GenericClass<TT;>;>;],status=BEING_PARSED}
|
||||
├─ t2 locked : ParseLock{name=LazyClassSignature:net/sourceforge/pmd/lang/java/symbols/DeadlockTest$Outer[<T:Ljava/lang/Object;>Ljava/lang/Object;Lnet/sourceforge/pmd/lang/java/symbols/DeadlockTest$GenericInterface<Lnet/sourceforge/pmd/lang/java/symbols/DeadlockTest$Outer<TT;>;Lnet/sourceforge/pmd/lang/java/symbols/DeadlockTest$GenericClass<TT;>;>;],status=BEING_PARSED}
|
||||
├─ t2 released : ParseLock{name=LazyClassSignature:net/sourceforge/pmd/lang/java/symbols/DeadlockTest$Outer[<T:Ljava/lang/Object;>Ljava/lang/Object;Lnet/sourceforge/pmd/lang/java/symbols/DeadlockTest$GenericInterface<Lnet/sourceforge/pmd/lang/java/symbols/DeadlockTest$Outer<TT;>;Lnet/sourceforge/pmd/lang/java/symbols/DeadlockTest$GenericClass<TT;>;>;],status=BEING_PARSED}
|
||||
└─ t2 waiting on : ParseLock{name=LazyClassSignature:net/sourceforge/pmd/lang/java/symbols/DeadlockTest$GenericClass[<T:Ljava/lang/Object;>Lnet/sourceforge/pmd/lang/java/symbols/DeadlockTest$GenericBaseClass<Lnet/sourceforge/pmd/lang/java/symbols/DeadlockTest$Outer$Inner<TT;>;>;],status=NOT_PARSED}
|
||||
├─ t2 locked : ParseLock{name=LazyClassSignature:net/sourceforge/pmd/lang/java/symbols/DeadlockTest$GenericClass[<T:Ljava/lang/Object;>Lnet/sourceforge/pmd/lang/java/symbols/DeadlockTest$GenericBaseClass<Lnet/sourceforge/pmd/lang/java/symbols/DeadlockTest$Outer$Inner<TT;>;>;],status=NOT_PARSED}
|
||||
└─ t2 waiting on : ParseLock{name=LazyClassSignature:net/sourceforge/pmd/lang/java/symbols/DeadlockTest$Outer$Inner[<T:Ljava/lang/Object;>Ljava/lang/Object;],status=NOT_PARSED}
|
||||
|
||||
|
||||
In order to reproduce the deadlock reliably, add the following piece into ParseLock, just at the beginning
|
||||
@ -51,20 +72,50 @@ class DeadlockTest {
|
||||
} catch (InterruptedException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
And then, introduce a bug again. One way to make the test fail is:
|
||||
Comment out the method "public int getTypeParameterCount()", so that it is inherited again.
|
||||
Add the following method:
|
||||
@Override
|
||||
public boolean isGeneric() {
|
||||
parseLock.ensureParsed();
|
||||
return signature.isGeneric();
|
||||
}
|
||||
*/
|
||||
|
||||
List<Throwable> exceptions = new ArrayList<>();
|
||||
Thread.UncaughtExceptionHandler exceptionHandler = (t, e) -> {
|
||||
exceptions.add(e);
|
||||
e.printStackTrace();
|
||||
};
|
||||
|
||||
Thread t1 = new Thread(() -> {
|
||||
ASTCompilationUnit class1 = JavaParsingHelper.DEFAULT.parse(
|
||||
"package net.sourceforge.pmd.lang.java.symbols;\n"
|
||||
+ "import net.sourceforge.pmd.lang.java.symbols.DeadlockTest.Outer;\n"
|
||||
+ " class Class1 {\n"
|
||||
+ " public static <T> Outer.Inner<T> newInner(Outer<T> grid) {\n"
|
||||
+ " public static <X> Outer.Inner<X> newInner(Outer<X> grid) {\n"
|
||||
+ " return null;\n"
|
||||
+ " }\n"
|
||||
+ " }\n"
|
||||
);
|
||||
assertNotNull(class1);
|
||||
|
||||
// Outer.Inner<X> = return type of method "newInner"
|
||||
List<ASTClassType> classTypes = class1.descendants(ASTClassType.class).toList();
|
||||
ASTClassType outerInner = classTypes.get(0);
|
||||
assertGenericClassType(outerInner, "Inner", "X", "T");
|
||||
|
||||
// Outer = qualifier of Outer.Inner<X>
|
||||
ASTClassType outer = classTypes.get(1);
|
||||
assertEquals("Outer", outer.getSimpleName());
|
||||
assertNull(outer.getTypeArguments());
|
||||
|
||||
// Outer<X> = formal parameter type of method newInner
|
||||
ASTClassType outerFormalParam = classTypes.get(3);
|
||||
assertGenericClassType(outerFormalParam, "Outer", "X", "T");
|
||||
}, "t1");
|
||||
t1.setUncaughtExceptionHandler(exceptionHandler);
|
||||
|
||||
Thread t2 = new Thread(() -> {
|
||||
ASTCompilationUnit class2 = JavaParsingHelper.DEFAULT.parse(
|
||||
@ -75,12 +126,32 @@ class DeadlockTest {
|
||||
+ " }\n"
|
||||
);
|
||||
assertNotNull(class2);
|
||||
|
||||
// Outer<M> = type of field "theOuter"
|
||||
ASTClassType firstClassType = class2.descendants(ASTClassType.class).first();
|
||||
assertNotNull(firstClassType);
|
||||
assertGenericClassType(firstClassType, "Outer", "M", "T");
|
||||
}, "t2");
|
||||
t2.setUncaughtExceptionHandler(exceptionHandler);
|
||||
|
||||
t1.start();
|
||||
t2.start();
|
||||
|
||||
t1.join();
|
||||
t2.join();
|
||||
|
||||
assertAll(exceptions.stream()
|
||||
.map(e -> () -> {
|
||||
throw e;
|
||||
}));
|
||||
}
|
||||
|
||||
private static void assertGenericClassType(ASTClassType classType, String simpleName, String actualTypeParamName, String originalTypeParamName) {
|
||||
assertEquals(simpleName, classType.getSimpleName());
|
||||
assertEquals(1, classType.getTypeArguments().size());
|
||||
assertEquals(actualTypeParamName, ((ASTClassType) classType.getTypeArguments().get(0)).getSimpleName());
|
||||
JTypeParameterOwnerSymbol symbol = (JTypeParameterOwnerSymbol) classType.getTypeMirror().getSymbol();
|
||||
assertEquals(1, symbol.getTypeParameterCount());
|
||||
assertEquals(originalTypeParamName, symbol.getTypeParameters().get(0).getName());
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user