diff --git a/teavm-classlib/pom.xml b/teavm-classlib/pom.xml
index c3407f71d..9f638f16a 100644
--- a/teavm-classlib/pom.xml
+++ b/teavm-classlib/pom.xml
@@ -53,6 +53,7 @@
process-test-classes
false
+ 1
diff --git a/teavm-core/src/main/java/org/teavm/common/FiniteExecutor.java b/teavm-core/src/main/java/org/teavm/common/FiniteExecutor.java
new file mode 100644
index 000000000..24263f4e9
--- /dev/null
+++ b/teavm-core/src/main/java/org/teavm/common/FiniteExecutor.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2014 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.common;
+
+import java.util.concurrent.Executor;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+public interface FiniteExecutor extends Executor {
+ void complete();
+
+ void executeFast(Runnable runnable);
+}
diff --git a/teavm-core/src/main/java/org/teavm/common/SimpleFiniteExecutor.java b/teavm-core/src/main/java/org/teavm/common/SimpleFiniteExecutor.java
new file mode 100644
index 000000000..953746cf4
--- /dev/null
+++ b/teavm-core/src/main/java/org/teavm/common/SimpleFiniteExecutor.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2014 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.common;
+
+import java.util.LinkedList;
+import java.util.Queue;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+public class SimpleFiniteExecutor implements FiniteExecutor {
+ private Queue queue = new LinkedList<>();
+
+ @Override
+ public void execute(Runnable command) {
+ queue.add(command);
+ }
+
+ @Override
+ public void executeFast(Runnable runnable) {
+ execute(runnable);
+ }
+
+ @Override
+ public void complete() {
+ while (!queue.isEmpty()) {
+ queue.remove().run();
+ }
+ }
+}
diff --git a/teavm-core/src/main/java/org/teavm/common/ThreadPoolFiniteExecutor.java b/teavm-core/src/main/java/org/teavm/common/ThreadPoolFiniteExecutor.java
new file mode 100644
index 000000000..455915e8c
--- /dev/null
+++ b/teavm-core/src/main/java/org/teavm/common/ThreadPoolFiniteExecutor.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2014 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.common;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Queue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+public class ThreadPoolFiniteExecutor implements FiniteExecutor {
+ private List threads = new ArrayList<>();
+ private BlockingQueue queue = new LinkedBlockingQueue<>();
+ private AtomicInteger runningTasks = new AtomicInteger();
+ private final Object monitor = new Object();
+ private AtomicReference thrownException = new AtomicReference<>();
+ private ThreadLocal> localQueueues = new ThreadLocal<>();
+
+ public ThreadPoolFiniteExecutor(int numThreads) {
+ for (int i = 0; i < numThreads; ++i) {
+ Thread thread = new Thread() {
+ @Override public void run() {
+ takeTask();
+ }
+ };
+ threads.add(thread);
+ thread.start();
+ }
+ }
+
+ @Override
+ public void execute(Runnable command) {
+ runningTasks.incrementAndGet();
+ try {
+ queue.put(command);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+
+ @Override
+ public void executeFast(Runnable runnable) {
+ localQueueues.get().add(runnable);
+ }
+
+ @Override
+ public void complete() {
+ synchronized (monitor) {
+ try {
+ monitor.wait();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ return;
+ }
+ if (thrownException.get() != null) {
+ throw thrownException.get();
+ }
+ if (runningTasks.get() == 0) {
+ return;
+ }
+ }
+ }
+
+ private void takeTask() {
+ Queue localQueue = new ArrayDeque<>();
+ localQueueues.set(localQueue);
+ try {
+ while (true) {
+ Runnable task = queue.take();
+ try {
+ task.run();
+ while (!localQueue.isEmpty()) {
+ localQueue.remove().run();
+ }
+ } catch (RuntimeException e) {
+ thrownException.set(e);
+ } finally {
+ if (runningTasks.decrementAndGet() == 0 || thrownException.get() != null) {
+ synchronized (monitor) {
+ monitor.notifyAll();
+ }
+ }
+ }
+ }
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+
+ public void stop() {
+ for (Thread thread : threads) {
+ thread.interrupt();
+ }
+ }
+}
diff --git a/teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java b/teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java
index d806d2994..ac8056755 100644
--- a/teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java
+++ b/teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java
@@ -18,10 +18,10 @@ package org.teavm.dependency;
import java.util.Collection;
import java.util.HashSet;
import java.util.concurrent.*;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicReference;
import org.teavm.common.ConcurrentCachedMapper;
+import org.teavm.common.FiniteExecutor;
import org.teavm.common.Mapper;
+import org.teavm.common.SimpleFiniteExecutor;
import org.teavm.common.ConcurrentCachedMapper.KeyListener;
import org.teavm.model.*;
@@ -34,31 +34,21 @@ public class DependencyChecker {
static final boolean shouldLog = System.getProperty("org.teavm.logDependencies", "false").equals("true");
private ClassHolderSource classSource;
private ClassLoader classLoader;
- private ScheduledThreadPoolExecutor executor;
+ private FiniteExecutor executor;
private ConcurrentMap abstractMethods = new ConcurrentHashMap<>();
private ConcurrentCachedMapper methodCache;
private ConcurrentCachedMapper fieldCache;
private ConcurrentMap achievableClasses = new ConcurrentHashMap<>();
private ConcurrentMap initializedClasses = new ConcurrentHashMap<>();
- private AtomicReference exceptionOccured = new AtomicReference<>();
- private AtomicInteger activeTaskCount = new AtomicInteger(0);
- private final Object activeTaskMonitor = new Object();
public DependencyChecker(ClassHolderSource classSource, ClassLoader classLoader) {
- this(classSource, classLoader, Runtime.getRuntime().availableProcessors());
+ this(classSource, classLoader, new SimpleFiniteExecutor());
}
- public DependencyChecker(ClassHolderSource classSource, ClassLoader classLoader, int numThreads) {
+ public DependencyChecker(ClassHolderSource classSource, ClassLoader classLoader, FiniteExecutor executor) {
this.classSource = classSource;
this.classLoader = classLoader;
- executor = new ScheduledThreadPoolExecutor(numThreads);
- executor.setThreadFactory(new ThreadFactory() {
- @Override public Thread newThread(Runnable r) {
- Thread thread = new Thread(r);
- thread.setDaemon(true);
- return thread;
- }
- });
+ this.executor = executor;
methodCache = new ConcurrentCachedMapper<>(new Mapper() {
@Override public MethodGraph map(MethodReference preimage) {
return createMethodGraph(preimage);
@@ -98,58 +88,15 @@ public class DependencyChecker {
}
public void schedulePropagation(final DependencyConsumer consumer, final String type) {
- schedule(new Runnable() {
+ executor.executeFast(new Runnable() {
@Override public void run() {
consumer.consume(type);
}
});
}
- void schedule(final Runnable runnable) {
- synchronized (activeTaskMonitor) {
- activeTaskCount.incrementAndGet();
- }
- try {
- executor.execute(new Runnable() {
- @Override public void run() {
- try {
- runnable.run();
- } catch (RuntimeException e) {
- activeTaskMonitor.notifyAll();
- exceptionOccured.compareAndSet(null, e);
- executor.shutdownNow();
- }
- synchronized (activeTaskMonitor) {
- if (activeTaskCount.decrementAndGet() == 0) {
- activeTaskMonitor.notifyAll();
- }
- }
- }
- });
- } catch (RejectedExecutionException e) {
- throw exceptionOccured.get();
- }
- }
-
- public void checkDependencies() {
- while (true) {
- try {
- synchronized (activeTaskMonitor) {
- if (activeTaskCount.get() == 0 || exceptionOccured.get() != null) {
- break;
- }
- activeTaskMonitor.wait();
- }
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- break;
- }
- }
- RuntimeException e = exceptionOccured.get();
- if (e != null) {
- throw exceptionOccured.get();
- }
- executor.shutdown();
+ public FiniteExecutor getExecutor() {
+ return executor;
}
boolean achieveClass(String className) {
@@ -226,7 +173,7 @@ public class DependencyChecker {
}
final MethodGraph graph = new MethodGraph(parameterNodes, paramCount, resultNode, this);
final MethodHolder currentMethod = method;
- schedule(new Runnable() {
+ executor.execute(new Runnable() {
@Override public void run() {
DependencyGraphBuilder graphBuilder = new DependencyGraphBuilder(DependencyChecker.this);
graphBuilder.buildGraph(currentMethod, graph);
diff --git a/teavm-core/src/main/java/org/teavm/javascript/JavascriptBuilder.java b/teavm-core/src/main/java/org/teavm/javascript/JavascriptBuilder.java
index 4938ffcbd..ccbe610ac 100644
--- a/teavm-core/src/main/java/org/teavm/javascript/JavascriptBuilder.java
+++ b/teavm-core/src/main/java/org/teavm/javascript/JavascriptBuilder.java
@@ -21,10 +21,10 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import org.teavm.codegen.*;
+import org.teavm.common.FiniteExecutor;
import org.teavm.dependency.DependencyChecker;
import org.teavm.javascript.ast.ClassNode;
import org.teavm.model.*;
-import org.teavm.model.resource.ClasspathClassHolderSource;
import org.teavm.model.util.*;
import org.teavm.optimization.ClassSetOptimizer;
@@ -35,25 +35,19 @@ import org.teavm.optimization.ClassSetOptimizer;
public class JavascriptBuilder {
private ClassHolderSource classSource;
private DependencyChecker dependencyChecker;
+ private FiniteExecutor executor;
private ClassLoader classLoader;
private boolean minifying = true;
- private boolean bytecodeLogging = true;
+ private boolean bytecodeLogging;
private OutputStream logStream = System.out;
private Map entryPoints = new HashMap<>();
private Map exportedClasses = new HashMap<>();
- public JavascriptBuilder(ClassHolderSource classSource, ClassLoader classLoader) {
+ JavascriptBuilder(ClassHolderSource classSource, ClassLoader classLoader, FiniteExecutor executor) {
this.classSource = classSource;
this.classLoader = classLoader;
- dependencyChecker = new DependencyChecker(classSource, classLoader);
- }
-
- public JavascriptBuilder(ClassLoader classLoader) {
- this(new ClasspathClassHolderSource(classLoader), classLoader);
- }
-
- public JavascriptBuilder() {
- this(JavascriptBuilder.class.getClassLoader());
+ dependencyChecker = new DependencyChecker(classSource, classLoader, executor);
+ this.executor = executor;
}
public boolean isMinifying() {
@@ -107,7 +101,7 @@ public class JavascriptBuilder {
ValueType.object("java.lang.Class"))));
dependencyChecker.attachMethodGraph(new MethodReference("java.lang.String", new MethodDescriptor("",
ValueType.arrayOf(ValueType.CHARACTER), ValueType.VOID)));
- dependencyChecker.checkDependencies();
+ executor.complete();
ListableClassHolderSource classSet = dependencyChecker.cutUnachievableClasses();
Decompiler decompiler = new Decompiler(classSet, classLoader);
ClassSetOptimizer optimizer = new ClassSetOptimizer();
diff --git a/teavm-core/src/main/java/org/teavm/javascript/JavascriptBuilderFactory.java b/teavm-core/src/main/java/org/teavm/javascript/JavascriptBuilderFactory.java
new file mode 100644
index 000000000..98f59399b
--- /dev/null
+++ b/teavm-core/src/main/java/org/teavm/javascript/JavascriptBuilderFactory.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2014 Alexey Andreev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.teavm.javascript;
+
+import org.teavm.common.FiniteExecutor;
+import org.teavm.common.SimpleFiniteExecutor;
+import org.teavm.model.ClassHolderSource;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+public class JavascriptBuilderFactory {
+ ClassHolderSource classSource;
+ ClassLoader classLoader;
+ FiniteExecutor executor = new SimpleFiniteExecutor();
+
+ public ClassHolderSource getClassSource() {
+ return classSource;
+ }
+
+ public void setClassSource(ClassHolderSource classSource) {
+ this.classSource = classSource;
+ }
+
+ public ClassLoader getClassLoader() {
+ return classLoader;
+ }
+
+ public void setClassLoader(ClassLoader classLoader) {
+ this.classLoader = classLoader;
+ }
+
+ public FiniteExecutor getExecutor() {
+ return executor;
+ }
+
+ public void setExecutor(FiniteExecutor executor) {
+ this.executor = executor;
+ }
+
+ public JavascriptBuilder create() {
+ return new JavascriptBuilder(classSource, classLoader, executor);
+ }
+}
diff --git a/teavm-maven-plugin/src/main/java/org/teavm/maven/BuildJavascriptJUnitMojo.java b/teavm-maven-plugin/src/main/java/org/teavm/maven/BuildJavascriptJUnitMojo.java
index 071f9a066..8f584f058 100644
--- a/teavm-maven-plugin/src/main/java/org/teavm/maven/BuildJavascriptJUnitMojo.java
+++ b/teavm-maven-plugin/src/main/java/org/teavm/maven/BuildJavascriptJUnitMojo.java
@@ -34,7 +34,11 @@ import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.project.MavenProject;
import org.junit.Test;
+import org.teavm.common.FiniteExecutor;
+import org.teavm.common.SimpleFiniteExecutor;
+import org.teavm.common.ThreadPoolFiniteExecutor;
import org.teavm.javascript.JavascriptBuilder;
+import org.teavm.javascript.JavascriptBuilderFactory;
import org.teavm.model.*;
import org.teavm.model.resource.ClasspathClassHolderSource;
@@ -68,6 +72,9 @@ public class BuildJavascriptJUnitMojo extends AbstractMojo {
@Parameter
private boolean minifying = true;
+ @Parameter
+ private int numThreads = 1;
+
public void setProject(MavenProject project) {
this.project = project;
}
@@ -88,8 +95,13 @@ public class BuildJavascriptJUnitMojo extends AbstractMojo {
this.minifying = minifying;
}
+ public void setNumThreads(int numThreads) {
+ this.numThreads = numThreads;
+ }
+
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
+ Runnable finalizer = null;
try {
ClassLoader classLoader = prepareClassLoader();
getLog().info("Searching for tests in the directory `" + testFiles.getAbsolutePath() + "'");
@@ -146,14 +158,29 @@ public class BuildJavascriptJUnitMojo extends AbstractMojo {
}
int methodsGenerated = 0;
log.info("Generating test files");
+ FiniteExecutor executor = new SimpleFiniteExecutor();
+ if (numThreads != 1) {
+ int threads = numThreads != 0 ? numThreads : Runtime.getRuntime().availableProcessors();
+ final ThreadPoolFiniteExecutor threadedExecutor = new ThreadPoolFiniteExecutor(threads);
+ finalizer = new Runnable() {
+ @Override public void run() {
+ threadedExecutor.stop();
+ }
+ };
+ executor = threadedExecutor;
+ }
for (MethodReference method : testMethods) {
log.debug("Building test for " + method);
- decompileClassesForTest(classLoader, method, fileNames.get(method));
+ decompileClassesForTest(classLoader, method, fileNames.get(method), executor);
++methodsGenerated;
}
log.info("Test files successfully generated for " + methodsGenerated + " method(s)");
} catch (IOException e) {
throw new MojoFailureException("IO error occured generating JavaScript files", e);
+ } finally {
+ if (finalizer != null) {
+ finalizer.run();
+ }
}
}
@@ -189,9 +216,13 @@ public class BuildJavascriptJUnitMojo extends AbstractMojo {
}
}
- private void decompileClassesForTest(ClassLoader classLoader, MethodReference methodRef, String targetName)
- throws IOException {
- JavascriptBuilder builder = new JavascriptBuilder(classLoader);
+ private void decompileClassesForTest(ClassLoader classLoader, MethodReference methodRef, String targetName,
+ FiniteExecutor executor) throws IOException {
+ JavascriptBuilderFactory builderFactory = new JavascriptBuilderFactory();
+ builderFactory.setClassLoader(classLoader);
+ builderFactory.setClassSource(new ClasspathClassHolderSource(classLoader));
+ builderFactory.setExecutor(executor);
+ JavascriptBuilder builder = builderFactory.create();
builder.setMinifying(minifying);
File file = new File(outputDir, targetName);
try (Writer innerWriter = new OutputStreamWriter(new FileOutputStream(file), "UTF-8")) {
diff --git a/teavm-maven-plugin/src/main/java/org/teavm/maven/BuildJavascriptMojo.java b/teavm-maven-plugin/src/main/java/org/teavm/maven/BuildJavascriptMojo.java
index fc8867773..ac1f1f377 100644
--- a/teavm-maven-plugin/src/main/java/org/teavm/maven/BuildJavascriptMojo.java
+++ b/teavm-maven-plugin/src/main/java/org/teavm/maven/BuildJavascriptMojo.java
@@ -30,10 +30,13 @@ import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.project.MavenProject;
+import org.teavm.common.ThreadPoolFiniteExecutor;
import org.teavm.javascript.JavascriptBuilder;
+import org.teavm.javascript.JavascriptBuilderFactory;
import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodReference;
import org.teavm.model.ValueType;
+import org.teavm.model.resource.ClasspathClassHolderSource;
/**
*
@@ -71,6 +74,9 @@ public class BuildJavascriptMojo extends AbstractMojo {
@Parameter
private boolean bytecodeLogging;
+ @Parameter(required = false)
+ private int numThreads = 1;
+
public void setProject(MavenProject project) {
this.project = project;
}
@@ -99,13 +105,31 @@ public class BuildJavascriptMojo extends AbstractMojo {
this.mainPageIncluded = mainPageIncluded;
}
+ public void setNumThreads(int numThreads) {
+ this.numThreads = numThreads;
+ }
+
@Override
public void execute() throws MojoExecutionException {
Log log = getLog();
+ Runnable finalizer = null;
try {
ClassLoader classLoader = prepareClassLoader();
log.info("Building JavaScript file");
- JavascriptBuilder builder = new JavascriptBuilder(classLoader);
+ JavascriptBuilderFactory builderFactory = new JavascriptBuilderFactory();
+ builderFactory.setClassLoader(classLoader);
+ builderFactory.setClassSource(new ClasspathClassHolderSource(classLoader));
+ if (numThreads != 1) {
+ int threads = numThreads != 0 ? numThreads : Runtime.getRuntime().availableProcessors();
+ final ThreadPoolFiniteExecutor executor = new ThreadPoolFiniteExecutor(threads);
+ finalizer = new Runnable() {
+ @Override public void run() {
+ executor.stop();
+ }
+ };
+ builderFactory.setExecutor(executor);
+ }
+ JavascriptBuilder builder = builderFactory.create();
builder.setMinifying(minifying);
builder.setBytecodeLogging(bytecodeLogging);
MethodDescriptor mainMethodDesc = new MethodDescriptor("main", ValueType.arrayOf(
@@ -133,6 +157,10 @@ public class BuildJavascriptMojo extends AbstractMojo {
throw new MojoExecutionException("Unexpected error occured", e);
} catch (IOException e) {
throw new MojoExecutionException("IO error occured", e);
+ } finally {
+ if (finalizer != null) {
+ finalizer.run();
+ }
}
}