Concurrency Issue Loading Ops and OperatorImpl #1237

Closed
franciscospaeth opened this Issue Mar 5, 2015 · 2 comments

Projects

None yet

3 participants

@franciscospaeth

Whenever com.mysema.query.types.OperatorImpl and com.mysema.query.types.Ops are loaded simultaneously loaded a deadlock is reached.

The following code could be used to reproduce the issue:

import org.junit.Test;

import com.mysema.query.jpa.impl.JPAQuery;

public class CycleClassInitDependencyTest {

    @Test(timeout = 2000)
    public void test() throws InterruptedException {

        // If class is loaded before hand it will work for example:
        // System.out.println(Ops.DateTimeOps.DATE);

        // each thread wants
        Thread t1 = new Thread(new LoadClassRunnable("com.mysema.query.types.OperatorImpl"));
        Thread t2 = new Thread(new LoadClassRunnable("com.mysema.query.types.Ops"));
        t1.start();
        t2.start();

        // trying to create anything depending on Ops class for instance:
        // JPAQuery -> JPQLTemplates -> Ops
        new JPAQuery();

    }

    public class LoadClassRunnable implements Runnable {

        private String classToLoad;

        public LoadClassRunnable(String classToLoad) {
            this.classToLoad = classToLoad;
        }

        public void run() {
            try {
                Class.forName(classToLoad);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }

    }

}

A jstack relevant part of the execution of code exposed above is:

"OpslLoading        " prio=10 tid=0x00007f47a4003000 nid=0x2dd3 in Object.wait() [0x00007f47b7cfb000]
   java.lang.Thread.State: RUNNABLE
    at com.mysema.query.types.Ops.<clinit>(Ops.java:30)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:188)
    at CycleClassInitDependencyTest$LoadClassRunnable.run(CycleClassInitDependencyTest.java:37)
    at java.lang.Thread.run(Thread.java:722)

"OperatorImplLoading" prio=10 tid=0x00007f47a4001800 nid=0x2dd2 in Object.wait() [0x00007f47b7dfb000]
   java.lang.Thread.State: RUNNABLE
    at sun.misc.Unsafe.ensureClassInitialized(Native Method)
    at sun.reflect.UnsafeFieldAccessorFactory.newFieldAccessor(UnsafeFieldAccessorFactory.java:43)
    at sun.reflect.ReflectionFactory.newFieldAccessor(ReflectionFactory.java:140)
    at java.lang.reflect.Field.acquireFieldAccessor(Field.java:949)
    at java.lang.reflect.Field.getFieldAccessor(Field.java:930)
    at java.lang.reflect.Field.get(Field.java:372)
    at com.mysema.query.types.OperatorImpl.<clinit>(OperatorImpl.java:42)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:188)
    at CycleClassInitDependencyTest$LoadClassRunnable.run(CycleClassInitDependencyTest.java:37)
    at java.lang.Thread.run(Thread.java:722)

"Thread-0" prio=10 tid=0x00007f47ec205000 nid=0x2dd1 in Object.wait() [0x00007f47b7efc000]
   java.lang.Thread.State: RUNNABLE
    at com.mysema.query.types.Templates.<init>(Templates.java:50)
    at com.mysema.query.types.Templates.<init>(Templates.java:38)
    at com.mysema.query.types.Templates.<clinit>(Templates.java:27)
    at com.mysema.query.jpa.impl.JPAQuery.<init>(JPAQuery.java:35)
    at CycleClassInitDependencyTest.test(CycleClassInitDependencyTest.java:23)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.internal.runners.statements.FailOnTimeout$StatementThread.run(FailOnTimeout.java:74)
@Shredder121
Member

I have successfully reproduced this case, I will push your test case this afternoon.

@timowest I already tried some synchronization, but if one thread holds the lock for Ops, and the other for OperatorImpl. we have a problem (since they are dependent on each other).

@timowest
Member
timowest commented Mar 5, 2015

This could be solved by getting rid of the OPS cache in OperationImpl and reading the OperatorImpl in deserialization via the Operator.id, since it is always FQCN#staticField.

Querydsl 4.0.0 won't have that issue since the Operator instances are enums.

@timowest timowest changed the title from Concurrency Issue Loading Opts and OperatorImpl to Concurrency Issue Loading Ops and OperatorImpl Mar 5, 2015
@timowest timowest added this to the 3.6.3 milestone Mar 6, 2015
@timowest timowest closed this Mar 11, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment