Added multi-threaded support to processing of class files in dx.
Change-Id: Ideb54c414073a9651b21ce0697e6444fa80f146d
diff --git a/dx/src/com/android/dx/command/dexer/Main.java b/dx/src/com/android/dx/command/dexer/Main.java
index 363741a..204caa7 100644
--- a/dx/src/com/android/dx/command/dexer/Main.java
+++ b/dx/src/com/android/dx/command/dexer/Main.java
@@ -45,6 +45,9 @@
import java.util.ArrayList;
import java.util.Map;
import java.util.TreeMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;
@@ -139,6 +142,12 @@
*/
private static TreeMap<String, byte[]> outputResources;
+ /** thread pool object used for multi-threaded file processing */
+ private static ExecutorService threadPool;
+
+ /** true if any files are successfully processed */
+ private static boolean anyFilesProcessed;
+
/**
* This class is uninstantiable.
*/
@@ -212,12 +221,18 @@
outputDex.setDumpWidth(args.dumpWidth);
}
- boolean any = false;
+ anyFilesProcessed = false;
String[] fileNames = args.fileNames;
+ if (args.numThreads > 1) {
+ threadPool = Executors.newFixedThreadPool(args.numThreads);
+ }
+
try {
for (int i = 0; i < fileNames.length; i++) {
- any |= processOne(fileNames[i]);
+ if (processOne(fileNames[i])) {
+ anyFilesProcessed = true;
+ }
}
} catch (StopProcessing ex) {
/*
@@ -226,6 +241,15 @@
*/
}
+ if (args.numThreads > 1) {
+ try {
+ threadPool.shutdown();
+ threadPool.awaitTermination(600L, TimeUnit.SECONDS);
+ } catch (InterruptedException ex) {
+ throw new RuntimeException("Timed out waiting for threads.");
+ }
+ }
+
if (warnings != 0) {
DxConsole.err.println(warnings + " warning" +
((warnings == 1) ? "" : "s"));
@@ -237,7 +261,7 @@
return false;
}
- if (!(any || args.emptyOk)) {
+ if (!(anyFilesProcessed || args.emptyOk)) {
DxConsole.err.println("no classfiles specified");
return false;
}
@@ -263,7 +287,12 @@
opener = new ClassPathOpener(pathname, false,
new ClassPathOpener.Consumer() {
public boolean processFileBytes(String name, byte[] bytes) {
- return Main.processFileBytes(name, bytes);
+ if (args.numThreads > 1) {
+ threadPool.execute(new ParallelProcessor(name, bytes));
+ return false;
+ } else {
+ return Main.processFileBytes(name, bytes);
+ }
}
public void onException(Exception ex) {
if (ex instanceof StopProcessing) {
@@ -310,11 +339,15 @@
if (isClass) {
if (keepResources && args.keepClassesInJar) {
- outputResources.put(fixedName, bytes);
+ synchronized (outputResources) {
+ outputResources.put(fixedName, bytes);
+ }
}
return processClass(fixedName, bytes);
} else {
- outputResources.put(fixedName, bytes);
+ synchronized (outputResources) {
+ outputResources.put(fixedName, bytes);
+ }
return true;
}
}
@@ -335,7 +368,9 @@
try {
ClassDefItem clazz =
CfTranslator.translate(name, bytes, args.cfOptions);
- outputDex.add(clazz);
+ synchronized (outputDex) {
+ outputDex.add(clazz);
+ }
return true;
} catch (ParseException ex) {
DxConsole.err.println("\ntrouble processing:");
@@ -799,6 +834,9 @@
/** Options for dex.cf.* */
public CfOptions cfOptions;
+ /** number of threads to run with */
+ public int numThreads = 1;
+
/**
* Parses the given command-line arguments.
*
@@ -882,6 +920,9 @@
}
} else if (arg.equals("--no-locals")) {
localInfo = false;
+ } else if (arg.startsWith("--num-threads=")) {
+ arg = arg.substring(arg.indexOf('=') + 1);
+ numThreads = Integer.parseInt(arg);
} else {
System.err.println("unknown option: " + arg);
throw new UsageException();
@@ -927,4 +968,33 @@
cfOptions.warn = DxConsole.err;
}
}
+
+ /** Runnable helper class to process files in multiple threads */
+ private static class ParallelProcessor implements Runnable {
+
+ String path;
+ byte[] bytes;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param path {@code non-null;} filename of element. May not be a valid
+ * filesystem path.
+ * @param bytes {@code non-null;} file data
+ */
+ private ParallelProcessor(String path, byte bytes[]) {
+ this.path = path;
+ this.bytes = bytes;
+ }
+
+ /**
+ * Task run by each thread in the thread pool. Runs processFileBytes
+ * with the given path and bytes.
+ */
+ public void run() {
+ if (Main.processFileBytes(path, bytes)) {
+ anyFilesProcessed = true;
+ }
+ }
+ }
}
diff --git a/dx/src/com/android/dx/rop/code/RegisterSpec.java b/dx/src/com/android/dx/rop/code/RegisterSpec.java
index f1ac563..df5f338 100644
--- a/dx/src/com/android/dx/rop/code/RegisterSpec.java
+++ b/dx/src/com/android/dx/rop/code/RegisterSpec.java
@@ -60,16 +60,18 @@
*/
private static RegisterSpec intern(int reg, TypeBearer type,
LocalItem local) {
- theInterningItem.set(reg, type, local);
- RegisterSpec found = theInterns.get(theInterningItem);
+ synchronized (theInterns) {
+ theInterningItem.set(reg, type, local);
+ RegisterSpec found = theInterns.get(theInterningItem);
- if (found != null) {
+ if (found != null) {
+ return found;
+ }
+
+ found = theInterningItem.toRegisterSpec();
+ theInterns.put(found, found);
return found;
}
-
- found = theInterningItem.toRegisterSpec();
- theInterns.put(found, found);
- return found;
}
/**
diff --git a/dx/src/com/android/dx/rop/cst/CstType.java b/dx/src/com/android/dx/rop/cst/CstType.java
index 593adf8..077ad06 100644
--- a/dx/src/com/android/dx/rop/cst/CstType.java
+++ b/dx/src/com/android/dx/rop/cst/CstType.java
@@ -123,14 +123,16 @@
* @return {@code non-null;} an appropriately-constructed instance
*/
public static CstType intern(Type type) {
- CstType cst = interns.get(type);
+ synchronized (interns) {
+ CstType cst = interns.get(type);
- if (cst == null) {
- cst = new CstType(type);
- interns.put(type, cst);
+ if (cst == null) {
+ cst = new CstType(type);
+ interns.put(type, cst);
+ }
+
+ return cst;
}
-
- return cst;
}
/**
diff --git a/dx/src/com/android/dx/rop/type/Prototype.java b/dx/src/com/android/dx/rop/type/Prototype.java
index cbd5328..ec46ff9 100644
--- a/dx/src/com/android/dx/rop/type/Prototype.java
+++ b/dx/src/com/android/dx/rop/type/Prototype.java
@@ -55,7 +55,10 @@
throw new NullPointerException("descriptor == null");
}
- Prototype result = internTable.get(descriptor);
+ Prototype result;
+ synchronized (internTable) {
+ result = internTable.get(descriptor);
+ }
if (result != null) {
return result;
}
diff --git a/dx/src/com/android/dx/rop/type/Type.java b/dx/src/com/android/dx/rop/type/Type.java
index 244584d..eefd55b 100644
--- a/dx/src/com/android/dx/rop/type/Type.java
+++ b/dx/src/com/android/dx/rop/type/Type.java
@@ -292,7 +292,10 @@
* invalid syntax
*/
public static Type intern(String descriptor) {
- Type result = internTable.get(descriptor);
+ Type result;
+ synchronized (internTable) {
+ result = internTable.get(descriptor);
+ }
if (result != null) {
return result;
}