Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Not synchronized com.mysema.query.types.TemplateFactory cache #341

Closed
IonPrimer opened this issue Feb 11, 2013 · 2 comments
Closed

Not synchronized com.mysema.query.types.TemplateFactory cache #341

IonPrimer opened this issue Feb 11, 2013 · 2 comments
Milestone

Comments

@IonPrimer
Copy link

@IonPrimer IonPrimer commented Feb 11, 2013

In class

com.mysema.query.types.TemplateFactory

we have static factory: TemplateFactory DEFAULT with cache in Map:

private final Map<String,Template> cache = new HashMap<String,Template>();

By calling:

TemplateFactory.DEFAULT.create(name);

we can create infinite loop in HashMap, because HashMap cache is not synchronized. Instead default HashMap we should use ConcurrentHashMap.

We can try to reproduce this error by running app:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import com.mysema.query.types.TemplateFactory;

public class TermplateInfiniteLoop {

static String templates[] = createTemplates();

public static void main( String[] args ) {
    ExecutorService executorService = Executors.newFixedThreadPool( 3 );
    executorService.execute( new Runner() );
    executorService.execute( new Runner() );
    executorService.execute( new Runner() );
    executorService.shutdown();
}

private static class Runner implements Runnable{
    public void run() {
        for( int i = 0; i < 100000; i++ ){
            TemplateFactory.DEFAULT.create(templates[i%templates.length]);
        }
    }
}

/**
 * Generates array of strings: "\0a", "\0\0a", "\0\0\0a" etc. all with the same hashCode
 * @return
 */
private static String[] createTemplates() {
    String tab[] = new String[10000];
    StringBuilder builder = new StringBuilder();
    for( int i = 0; i < tab.length; i++ ){
        builder.append( '\0' );
        tab[i] = builder.toString() + 'a';
    }
    return tab;
}
}

Important is that all strings have the same hashCode, so they will be stored in the same HashMap bucket. Infinite loop stack trace:

at java.util.HashMap.getEntry(HashMap.java:448) at java.util.HashMap.containsKey(HashMap.java:434) at com.mysema.query.types.TemplateFactory.create(TemplateFactory.java:52) at TermplateInfiniteLoop$Runner.run(TermplateInfiniteLoop.java:23) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603) at java.lang.Thread.run(Thread.java:722)

Tested on several versions of querydsl-code, and java versions 1.6.x

@timowest
Copy link
Member

@timowest timowest commented Feb 12, 2013

Thanks for the bug report. Fixed as suggested.

@timowest
Copy link
Member

@timowest timowest commented Feb 20, 2013

Released in 3.0.0.BETA2

@timowest timowest closed this Feb 20, 2013
@timowest timowest added this to the 3.0.0 milestone Apr 14, 2014
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
2 participants