You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by su...@apache.org on 2021/03/06 20:36:30 UTC

[groovy] branch GROOVY-9964 updated: GROOVY-9964: Parallelize parsing

This is an automated email from the ASF dual-hosted git repository.

sunlan pushed a commit to branch GROOVY-9964
in repository https://gitbox.apache.org/repos/asf/groovy.git


The following commit(s) were added to refs/heads/GROOVY-9964 by this push:
     new 3f9f753  GROOVY-9964: Parallelize parsing
3f9f753 is described below

commit 3f9f753c2ec591e3d39f7e2b242bc1132144bd94
Author: Daniel Sun <su...@apache.org>
AuthorDate: Sun Mar 7 04:36:13 2021 +0800

    GROOVY-9964: Parallelize parsing
---
 .../groovy/parser/antlr4/Antlr4ParserPlugin.java   | 81 ++++++++++++++++++++--
 .../apache/groovy/parser/antlr4/AstBuilder.java    | 22 +++---
 .../java/org/codehaus/groovy/ast/ModuleNode.java   | 14 ++--
 3 files changed, 93 insertions(+), 24 deletions(-)

diff --git a/src/main/java/org/apache/groovy/parser/antlr4/Antlr4ParserPlugin.java b/src/main/java/org/apache/groovy/parser/antlr4/Antlr4ParserPlugin.java
index 819165e..562b06e 100644
--- a/src/main/java/org/apache/groovy/parser/antlr4/Antlr4ParserPlugin.java
+++ b/src/main/java/org/apache/groovy/parser/antlr4/Antlr4ParserPlugin.java
@@ -18,8 +18,11 @@
  */
 package org.apache.groovy.parser.antlr4;
 
+import groovy.lang.GroovyRuntimeException;
 import org.codehaus.groovy.GroovyBugError;
 import org.codehaus.groovy.ast.ModuleNode;
+import org.codehaus.groovy.control.CompilationFailedException;
+import org.codehaus.groovy.control.ErrorCollector;
 import org.codehaus.groovy.control.ParserPlugin;
 import org.codehaus.groovy.control.SourceUnit;
 import org.codehaus.groovy.control.io.StringReaderSource;
@@ -28,6 +31,12 @@ import org.codehaus.groovy.syntax.Reduction;
 
 import java.io.IOException;
 import java.io.Reader;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
 
 /**
  * A parser plugin for the new parser.
@@ -49,12 +58,74 @@ public class Antlr4ParserPlugin implements ParserPlugin {
         return null;
     }
 
+    private static final ExecutorService THREAD_POOL = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), new ThreadFactory() {
+        private int seq = 0;
+        @Override
+        public Thread newThread(Runnable r) {
+            Thread t = new Thread(r, "parser-thread-" + seq++);
+            t.setDaemon(true);
+            return t;
+        }
+    });
+
     @Override
     public ModuleNode buildAST(final SourceUnit sourceUnit, final ClassLoader classLoader, final Reduction cst) {
-        AstBuilder builder = new AstBuilder(sourceUnit,
-                sourceUnit.getConfiguration().isGroovydocEnabled(),
-                sourceUnit.getConfiguration().isRuntimeGroovydocEnabled()
-        );
-        return builder.buildAST();
+        CountDownLatch countDownLatch = new CountDownLatch(1);
+        AtomicReference<ModuleNode> moduleNodeAtomicReference = new AtomicReference<>();
+        ExceptionHolder<CompilationFailedException> exceptionHolder = new ExceptionHolder<>(countDownLatch);
+        submitParseTask(sourceUnit, countDownLatch, moduleNodeAtomicReference, exceptionHolder, "LL");
+        submitParseTask(sourceUnit, countDownLatch, moduleNodeAtomicReference, exceptionHolder, "SLL");
+
+        ModuleNode moduleNode;
+        try {
+            countDownLatch.await();
+            moduleNode = moduleNodeAtomicReference.get();
+        } catch (InterruptedException e) {
+            throw new GroovyRuntimeException(e);
+        }
+
+        if (null != moduleNode) {
+            moduleNode.setContext(sourceUnit);
+            return moduleNode;
+        }
+        throw exceptionHolder.get();
+    }
+
+    private void submitParseTask(SourceUnit sourceUnit, CountDownLatch countDownLatch, AtomicReference<ModuleNode> moduleNodeAtomicReference, ExceptionHolder<CompilationFailedException> exceptionHolder, String predictionModeName) {
+        THREAD_POOL.submit(() -> {
+            SourceUnit su = new SourceUnit(sourceUnit.getName(), sourceUnit.getSource(), sourceUnit.getConfiguration(), sourceUnit.getClassLoader(), new ErrorCollector(sourceUnit.getConfiguration()));
+            AstBuilder builder = new AstBuilder(su,
+                    su.getConfiguration().isGroovydocEnabled(),
+                    su.getConfiguration().isRuntimeGroovydocEnabled()
+            );
+
+            try {
+                moduleNodeAtomicReference.set(builder.buildAST(predictionModeName));
+                countDownLatch.countDown();
+            } catch (CompilationFailedException ex) {
+                exceptionHolder.set(ex);
+            }
+        });
+    }
+
+    private static class ExceptionHolder<T extends Exception> {
+        private final AtomicReference<T> ar = new AtomicReference<>();
+        private final AtomicInteger count = new AtomicInteger(0);
+        private final CountDownLatch countDownLatch;
+
+        private ExceptionHolder(CountDownLatch countDownLatch) {
+            this.countDownLatch = countDownLatch;
+        }
+
+        public final void set(T compilationFailedException) {
+            ar.set(compilationFailedException);
+            if (2 == count.incrementAndGet()) {
+                countDownLatch.countDown();
+            }
+        }
+
+        public T get() {
+            return ar.get();
+        }
     }
 }
diff --git a/src/main/java/org/apache/groovy/parser/antlr4/AstBuilder.java b/src/main/java/org/apache/groovy/parser/antlr4/AstBuilder.java
index 669be8a..720e6fa 100644
--- a/src/main/java/org/apache/groovy/parser/antlr4/AstBuilder.java
+++ b/src/main/java/org/apache/groovy/parser/antlr4/AstBuilder.java
@@ -371,21 +371,19 @@ public class AstBuilder extends GroovyParserBaseVisitor<Object> {
         return charStream;
     }
 
-    private GroovyParserRuleContext buildCST() throws CompilationFailedException {
+    private GroovyParserRuleContext buildCST(final String predictionModeName) throws CompilationFailedException {
         GroovyParserRuleContext result;
 
         try {
             // parsing have to wait util clearing is complete.
             AtnManager.READ_LOCK.lock();
             try {
-                result = buildCST(PredictionMode.SLL);
-            } catch (Throwable t) {
-                // if some syntax error occurred in the lexer, no need to retry the powerful LL mode
-                if (t instanceof GroovySyntaxError && GroovySyntaxError.LEXER == ((GroovySyntaxError) t).getSource()) {
-                    throw t;
+                PredictionMode predictionMode = PredictionMode.valueOf(predictionModeName);
+                if (PredictionMode.SLL == predictionMode) {
+                    result = doBuildCST(PredictionMode.SLL);
+                } else {
+                    result = doBuildCST(PredictionMode.LL);
                 }
-
-                result = buildCST(PredictionMode.LL);
             } finally {
                 AtnManager.READ_LOCK.unlock();
             }
@@ -396,10 +394,10 @@ public class AstBuilder extends GroovyParserBaseVisitor<Object> {
         return result;
     }
 
-    private GroovyParserRuleContext buildCST(final PredictionMode predictionMode) {
+    private GroovyParserRuleContext doBuildCST(final PredictionMode predictionMode) {
         parser.getInterpreter().setPredictionMode(predictionMode);
 
-        if (PredictionMode.SLL.equals(predictionMode)) {
+        if (PredictionMode.SLL == predictionMode) {
             this.removeErrorListeners();
         } else {
             parser.getInputStream().seek(0);
@@ -423,9 +421,9 @@ public class AstBuilder extends GroovyParserBaseVisitor<Object> {
         return cfe;
     }
 
-    public ModuleNode buildAST() {
+    public ModuleNode buildAST(final String predictionModeName) {
         try {
-            return (ModuleNode) this.visit(this.buildCST());
+            return (ModuleNode) this.visit(this.buildCST(predictionModeName));
         } catch (Throwable t) {
             throw convertException(t);
         }
diff --git a/src/main/java/org/codehaus/groovy/ast/ModuleNode.java b/src/main/java/org/codehaus/groovy/ast/ModuleNode.java
index fedec5a..39bda06 100644
--- a/src/main/java/org/codehaus/groovy/ast/ModuleNode.java
+++ b/src/main/java/org/codehaus/groovy/ast/ModuleNode.java
@@ -25,6 +25,7 @@ import org.codehaus.groovy.classgen.GeneratorContext;
 import org.codehaus.groovy.control.SourceUnit;
 import org.codehaus.groovy.runtime.InvokerHelper;
 import org.codehaus.groovy.transform.BaseScriptASTTransformation;
+import org.objectweb.asm.Opcodes;
 
 import java.io.File;
 import java.net.URI;
@@ -46,18 +47,13 @@ import static org.codehaus.groovy.ast.tools.GeneralUtils.param;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.params;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.stmt;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.varX;
-import static org.objectweb.asm.Opcodes.ACC_ABSTRACT;
-import static org.objectweb.asm.Opcodes.ACC_FINAL;
-import static org.objectweb.asm.Opcodes.ACC_INTERFACE;
-import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
-import static org.objectweb.asm.Opcodes.ACC_STATIC;
 
 /**
  * Represents a module, which consists typically of a class declaration
  * but could include some imports, some statements and multiple classes
  * intermixed with statements like scripts in Python or Ruby
  */
-public class ModuleNode extends ASTNode {
+public class ModuleNode extends ASTNode implements Opcodes {
 
     private List<ClassNode> classes = new LinkedList<>();
     private final List<MethodNode> methods = new ArrayList<>();
@@ -403,7 +399,7 @@ public class ModuleNode extends ASTNode {
     private MethodNode handleMainMethodIfPresent(final List<MethodNode> methods) {
         boolean found = false;
         MethodNode result = null;
-        for (Iterator<MethodNode> iter = methods.iterator(); iter.hasNext(); ) {
+        for (Iterator<MethodNode> iter = methods.iterator(); iter.hasNext();) {
             MethodNode node = iter.next();
             if (node.getName().equals("main")) {
                 if (node.isStatic() && node.getParameters().length == 1) {
@@ -508,4 +504,8 @@ public class ModuleNode extends ASTNode {
     public BlockStatement getStatementBlock() {
         return statementBlock;
     }
+
+    public void setContext(SourceUnit sourceUnit) {
+        this.context = sourceUnit;
+    }
 }