Parallelize parsing of bytecode when updating TypeOracle.

Change-Id: Ic39c7ac0147f665061c9a2cd31b06e4d87d12a1c
diff --git a/dev/core/src/com/google/gwt/dev/javac/CompilationUnitTypeOracleUpdater.java b/dev/core/src/com/google/gwt/dev/javac/CompilationUnitTypeOracleUpdater.java
index af507d5..2c95c17 100644
--- a/dev/core/src/com/google/gwt/dev/javac/CompilationUnitTypeOracleUpdater.java
+++ b/dev/core/src/com/google/gwt/dev/javac/CompilationUnitTypeOracleUpdater.java
@@ -51,6 +51,9 @@
 import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger;
 import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger.Event;
 import com.google.gwt.thirdparty.guava.common.annotations.VisibleForTesting;
+import com.google.gwt.thirdparty.guava.common.base.Function;
+import com.google.gwt.thirdparty.guava.common.collect.Collections2;
+import com.google.gwt.thirdparty.guava.common.collect.Maps;
 import com.google.gwt.thirdparty.guava.common.collect.Sets;
 
 import java.io.PrintWriter;
@@ -65,6 +68,10 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
 
 /**
  * Builds or rebuilds a {@link com.google.gwt.core.ext.typeinfo.TypeOracle} from a set of
@@ -319,8 +326,21 @@
   }
 
   private final Set<String> resolvedTypeSourceNames = Sets.newHashSet();
-  private final Map<String, JRealClassType> typesByInternalName =
-      new HashMap<String, JRealClassType>();
+  private final Map<String, JRealClassType> typesByInternalName = Maps.newHashMap();
+  /**
+   * An executor service to parallelize some of the update process.
+   */
+  private static ExecutorService executor =
+      Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(),
+          new ThreadFactory() {
+            public Thread newThread(Runnable r) {
+              Thread t = new Thread(r);
+              // Make sure this executor lets the whole process terminate correctly even if there
+              // are still live threads.
+              t.setDaemon(true);
+              return t;
+            }
+          });
 
   public CompilationUnitTypeOracleUpdater(TypeOracle typeOracle) {
     super(typeOracle);
@@ -345,6 +365,27 @@
     Event visitClassFileEvent = SpeedTracerLogger.start(
         CompilerEventType.TYPE_ORACLE_UPDATER, "phase", "Visit Class Files");
     TypeOracleBuildContext context = getContext(argsLookup);
+    // Parse bytecode in parallel if there are many units to process by calling
+    // {@code TypeData.getCollectClassData()} in parallel.
+    try {
+      executor.<Void>invokeAll(Collections2.transform(typeDataList,
+          new Function<TypeData, Callable<Void>>() {
+            @Override
+            public Callable<Void> apply(final TypeData typeData) {
+              return new Callable<Void>() {
+                @Override
+                public Void call() {
+                  typeData.getCollectClassData();
+                  return null;
+                }
+              };
+            }
+          }));
+    } catch (InterruptedException e) {
+      // InterruptedException can be safely ignored here as the threads interrupted are only
+      // precomputing data in parallel that will otherwise be computed later sequentially if
+      // the threads are aborted.
+    }
 
     for (TypeData typeData : typeDataList) {
       CollectClassData classData = typeData.getCollectClassData();