[jboss-jira] [JBoss JIRA] (DROOLS-2524) Concurrent modification when initialising packages in KnowledgeBuilderImpl

Mario Fusco (JIRA) issues at jboss.org
Thu May 3 07:58:00 EDT 2018


Mario Fusco created DROOLS-2524:
-----------------------------------

             Summary: Concurrent modification when initialising packages in KnowledgeBuilderImpl
                 Key: DROOLS-2524
                 URL: https://issues.jboss.org/browse/DROOLS-2524
             Project: Drools
          Issue Type: Bug
          Components: core engine
    Affects Versions: 7.2.0.Final, 7.3.0.Final, 7.7.0.Final
            Reporter: Max Zerzouri
            Assignee: Mario Fusco
         Attachments: Main.java, main.drl

When parallel compilation is enabled, common structures are modified concurrently by {{KnowledgeBuilderImpl.initPackage}}, resulting in the following stack trace in Java 9/10.

{code:java}
Exception in thread "main" java.util.ConcurrentModificationException: java.util.ConcurrentModificationException
        at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
        at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:488)
        at java.base/java.util.concurrent.ForkJoinTask.getThrowableException(ForkJoinTask.java:600)
        at java.base/java.util.concurrent.ForkJoinTask.reportException(ForkJoinTask.java:678)
        at java.base/java.util.concurrent.ForkJoinTask.invoke(ForkJoinTask.java:737)
        at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateParallel(ForEachOps.java:159)
        at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateParallel(ForEachOps.java:173)
        at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:233)
        at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:497)
        at org.drools.compiler.builder.impl.KnowledgeBuilderImpl.compileRulesLevel(KnowledgeBuilderImpl.java:1238)
        at org.drools.compiler.builder.impl.KnowledgeBuilderImpl.compileRules(KnowledgeBuilderImpl.java:1218)
        at org.drools.compiler.builder.impl.KnowledgeBuilderImpl.compileKnowledgePackages(KnowledgeBuilderImpl.java:1056)
        at org.drools.compiler.builder.impl.KnowledgeBuilderImpl.addPackage(KnowledgeBuilderImpl.java:1047)
        at org.drools.compiler.builder.impl.KnowledgeBuilderImpl.addPackageFromDrl(KnowledgeBuilderImpl.java:553)
        at org.drools.compiler.builder.impl.KnowledgeBuilderImpl.addKnowledgeResource(KnowledgeBuilderImpl.java:773)
        at org.drools.compiler.builder.impl.KnowledgeBuilderImpl.add(KnowledgeBuilderImpl.java:2360)
        at org.drools.compiler.builder.impl.KnowledgeBuilderImpl.add(KnowledgeBuilderImpl.java:2349)
        at Main.attempt(Main.java:48)
        at Main.main(Main.java:29)
Caused by: java.util.ConcurrentModificationException
        at java.base/java.util.HashMap.computeIfAbsent(HashMap.java:1139)
        at org.drools.compiler.builder.impl.KnowledgeBuilderImpl.initPackage(KnowledgeBuilderImpl.java:1146)
        at org.drools.compiler.builder.impl.KnowledgeBuilderImpl.createPackageRegistry(KnowledgeBuilderImpl.java:1109)
        at org.drools.compiler.builder.impl.KnowledgeBuilderImpl.lambda$getOrCreatePackageRegistry$0(KnowledgeBuilderImpl.java:1105)
        at java.base/java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1751)
        at org.drools.compiler.builder.impl.KnowledgeBuilderImpl.getOrCreatePackageRegistry(KnowledgeBuilderImpl.java:1105)
        at org.drools.compiler.builder.impl.TypeDeclarationCache.createTypeDeclarationForBean(TypeDeclarationCache.java:262)
        at org.drools.compiler.builder.impl.TypeDeclarationCache.getAndRegisterTypeDeclaration(TypeDeclarationCache.java:90)
        at org.drools.compiler.builder.impl.TypeDeclarationBuilder.getAndRegisterTypeDeclaration(TypeDeclarationBuilder.java:69)
        at org.drools.compiler.builder.impl.KnowledgeBuilderImpl.getAndRegisterTypeDeclaration(KnowledgeBuilderImpl.java:1860)
        at org.drools.compiler.rule.builder.PatternBuilder.processClassObjectType(PatternBuilder.java:305)
        at org.drools.compiler.rule.builder.PatternBuilder.build(PatternBuilder.java:181)
        at org.drools.compiler.rule.builder.PatternBuilder.build(PatternBuilder.java:151)
        at org.drools.compiler.rule.builder.PatternBuilder.build(PatternBuilder.java:133)
        at org.drools.compiler.rule.builder.GroupElementBuilder.build(GroupElementBuilder.java:66)
        at org.drools.compiler.rule.builder.RuleBuilder.build(RuleBuilder.java:105)
        at org.drools.compiler.builder.impl.KnowledgeBuilderImpl.addRule(KnowledgeBuilderImpl.java:1281)
        at org.drools.compiler.builder.impl.KnowledgeBuilderImpl.lambda$compileRulesLevel$3(KnowledgeBuilderImpl.java:1242)
        at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:183)
        at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:177)
        at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1492)
        at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
        at java.base/java.util.stream.ForEachOps$ForEachTask.compute(ForEachOps.java:290)
        at java.base/java.util.concurrent.CountedCompleter.exec(CountedCompleter.java:746)
        at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
        at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1603)
        at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:177)
{code}

Looking at the code, it looks like additional concurrency-unsafe modifications are performed, since it accesses the mentioned {{HashMap}} to get a shared {{LinkedList}} which it then modifies.

Note that the stack trace above is obtained on Java 9/10, as JDK9 added the extra check to {{HashMap.computeIfAbsent}}, but the code relies on race conditions in any JDK version.

Attached a sample Java class source that can trigger the stack trace reliably on my machine (using OpenJDK 10)—should hopefully be reproducible using JDK9/JDK10 as long as the common ForkJoinPool has more than 1 thread.

Also attached a "drl" file with the rules generated by the sample class (not needed when testing, as the contents are generated by the sample class).



--
This message was sent by Atlassian JIRA
(v7.5.0#75005)



More information about the jboss-jira mailing list