INRIA / INRIA/spoon

Deadlock when evaluating 'defaultExpression' for public-static-primitive fields

Open
#3,074 4 comments 0 reactions 0 assignees View on GitHub
bug
Dominant language
Java
Stars
2k
Forks
392
Avg merge
11h 24m
Merged PRs (30d)
36

Description

It started to happen after introducing changes in src/main/java/spoon/support/visitor/java/JavaReflectionTreeBuilder.java (https://github.com/INRIA/spoon/commit/91f9fbdcecce19f3b70ca82c7deef39f900075ae#diff-6503b0d420b634022fd59f8a1826a39b).
In earlier versions of Java Native Access (before the version 5.0.0) there was a problem with dead Lock (Dead Lock in class initialization - https://github.com/java-native-access/jna/issues/652).

The problem is in com.sun.jna.Pointer and com.sun.jna.Native classes.
These classes have static interdependency, resulting in deadlock during multithread class initialization.

Spoon tries to evaluate values for public-static-primitive fields and stumbles upon this problem in JavaReflectionTreeBuilder::visitField. And also gets locked up!

The used compile dependency 'net.java.dev.jna:jna:4.5.0'.
The code on which the deadlock is repeated:

ClassN1.java:
```
public class ClassN1
{
public void test(com.sun.jna.Pointer ptr)
{
System.out.println(ptr.getPointer(5));
}
}
```

ClassN2.java:
```
public class ClassN2
{
public void test(com.sun.jna.Structure str)
{
System.out.println(str.getPointer());
}
}
```

Main.java:
```
import spoon.Launcher;
import spoon.reflect.code.CtInvocation;
import spoon.reflect.visitor.CtScanner;

public class Main
{
public static void main(String[] args)
{
final Launcher launcher = new Launcher();
launcher.addInputResource("./src/main/java/ClassN1.java");
launcher.addInputResource("./src/main/java/ClassN2.java");
launcher.buildModel();

class UserScanner extends CtScanner
{
@Override
public void visitCtInvocation(CtInvocation invocation)
{
super.visitCtInvocation(invocation);
invocation.getExecutable().getExecutableDeclaration();
}
}

UserScanner scanner = new UserScanner();

Thread thread1 = new Thread(){
public void run()
{
launcher.getFactory().Class().get(ClassN1.class).accept(scanner);
}
};
Thread thread2 = new Thread(){
public void run()
{
launcher.getFactory().Class().get(ClassN2.class).accept(scanner);
}
};

thread1.start();
thread2.start();
}
}

```

Locked up stacktrace of one of the threads:

```
Thread-0
Main$1.run()
spoon.support.reflect.declaration.CtClassImpl.accept()
spoon.reflect.visitor.CtScanner.visitCtClass()
spoon.reflect.visitor.CtScanner.scan()
spoon.reflect.visitor.CtScanner.scan()
spoon.reflect.visitor.CtScanner.scan()
spoon.support.reflect.declaration.CtMethodImpl.accept()
spoon.reflect.visitor.CtScanner.visitCtMethod()
spoon.reflect.visitor.CtScanner.scan()
spoon.reflect.visitor.CtScanner.scan()
spoon.support.reflect.code.CtBlockImpl.accept()
spoon.reflect.visitor.CtScanner.visitCtBlock()
spoon.reflect.visitor.CtScanner.scan()
spoon.reflect.visitor.CtScanner.scan()
spoon.reflect.visitor.CtScanner.scan()
spoon.support.reflect.code.CtInvocationImpl.accept()
Main$1UserScanner.visitCtInvocation()
spoon.reflect.visitor.CtScanner.visitCtInvocation()
spoon.reflect.visitor.CtScanner.scan()
spoon.reflect.visitor.CtScanner.scan()
spoon.reflect.visitor.CtScanner.scan()
spoon.support.reflect.code.CtInvocationImpl.accept()
Main$1UserScanner.visitCtInvocation()
spoon.support.reflect.reference.CtExecutableReferenceImpl.getExecutableDeclaration()
spoon.support.reflect.reference.CtTypeReferenceImpl.getTypeDeclaration()
spoon.reflect.factory.TypeFactory.get()
spoon.support.visitor.java.JavaReflectionTreeBuilder.scan()
spoon.support.visitor.java.JavaReflectionTreeBuilder.visitClass()
spoon.support.visitor.java.JavaReflectionVisitorImpl.visitClass()
spoon.support.visitor.java.JavaReflectionTreeBuilder.visitField()
java.lang.reflect.Field.get() ( <- freeze)
```

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.