Rewrite ThreadUtils methods to use threading module

Bug: b/304992619
Change-Id: Id668f3dad01b854ccfd40c25436a3933d7142a95
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index 2e50083..36080ca 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -233,6 +233,7 @@
                 analysis.processNewlyLiveMethod(classInitializer, clazz, null, null);
               }
             },
+            appView.options().getThreadingModule(),
             executor);
       }
 
diff --git a/src/main/java/com/android/tools/r8/GlobalSyntheticsGenerator.java b/src/main/java/com/android/tools/r8/GlobalSyntheticsGenerator.java
index 41f4db1..ef754ba 100644
--- a/src/main/java/com/android/tools/r8/GlobalSyntheticsGenerator.java
+++ b/src/main/java/com/android/tools/r8/GlobalSyntheticsGenerator.java
@@ -301,6 +301,7 @@
               throwExceptionCode,
               apiReferenceStubberEventConsumer);
         },
+        appView.options().getThreadingModule(),
         executorService);
   }
 
diff --git a/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java b/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java
index adcb0ae..40079b6 100644
--- a/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java
+++ b/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java
@@ -71,7 +71,8 @@
       // Finding super types is really fast so no need to pay the overhead of threading if the
       // number of classes is low.
       if (classes.size() > 2) {
-        ThreadUtils.processItems(classes, this::processClass, executorService);
+        ThreadUtils.processItems(
+            classes, this::processClass, appView.options().getThreadingModule(), executorService);
       } else {
         classes.forEach(this::processClass);
       }
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
index bd41479..e6c4db1 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -316,6 +316,7 @@
         virtualFiles,
         virtualFile ->
             rewriteJumboStringsAndComputeDebugRepresentation(virtualFile, lazyDexStrings),
+        appView.options().getThreadingModule(),
         executorService);
   }
 
@@ -335,6 +336,7 @@
               fileTiming.end();
               return fileTiming;
             },
+            appView.options().getThreadingModule(),
             executorService);
     merger.add(timings);
     merger.end();
@@ -908,7 +910,10 @@
 
   private void setCallSiteContexts(ExecutorService executorService) throws ExecutionException {
     ThreadUtils.processItems(
-        appView.appInfo().classes(), this::setCallSiteContexts, executorService);
+        appView.appInfo().classes(),
+        this::setCallSiteContexts,
+        appView.options().getThreadingModule(),
+        executorService);
   }
 
   private void setCallSiteContexts(DexProgramClass clazz) {
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriterExperimental.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriterExperimental.java
index 8324c91..890bb17 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriterExperimental.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriterExperimental.java
@@ -59,6 +59,7 @@
             virtualFile ->
                 rewriteJumboStringsAndComputeDebugRepresentationWithExternalStringIds(
                     virtualFile, lazyDexStrings, lastFile.getObjectMapping()),
+            appView.options().getThreadingModule(),
             executorService));
     return timings;
   }
diff --git a/src/main/java/com/android/tools/r8/graph/fixup/ConcurrentMethodFixup.java b/src/main/java/com/android/tools/r8/graph/fixup/ConcurrentMethodFixup.java
index ce17027..5462dd1 100644
--- a/src/main/java/com/android/tools/r8/graph/fixup/ConcurrentMethodFixup.java
+++ b/src/main/java/com/android/tools/r8/graph/fixup/ConcurrentMethodFixup.java
@@ -60,7 +60,10 @@
 
     timing.begin("Process strongly connected components");
     ThreadUtils.processItems(
-        connectedComponents, this::processConnectedProgramComponents, executorService);
+        connectedComponents,
+        this::processConnectedProgramComponents,
+        appView.options().getThreadingModule(),
+        executorService);
     timing.end();
     timing.end();
   }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
index 5a97ab6..ce123a1 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
@@ -303,13 +303,13 @@
               method -> {
                 IncompleteHorizontalClassMergerCode code =
                     (IncompleteHorizontalClassMergerCode) method.getDefinition().getCode();
-                method
-                    .setCode(
-                        code.toCfCode(
-                            appView.withClassHierarchy(), method, horizontalClassMergerGraphLens),
-                        appView);
+                method.setCode(
+                    code.toCfCode(
+                        appView.withClassHierarchy(), method, horizontalClassMergerGraphLens),
+                    appView);
               });
         },
+        appView.options().getThreadingModule(),
         executorService);
   }
 
@@ -328,6 +328,7 @@
                   .hasNext()
               : "Expected no incomplete code";
         },
+        appView.options().getThreadingModule(),
         executorService);
     return true;
   }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/SyntheticInitializerConverter.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/code/SyntheticInitializerConverter.java
index 269bf93..d07b48e 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/SyntheticInitializerConverter.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/code/SyntheticInitializerConverter.java
@@ -61,7 +61,10 @@
     if (!classInitializers.isEmpty()) {
       IRConverter converter = new IRConverter(createAppViewForConversion());
       ThreadUtils.processItems(
-          classInitializers, method -> processMethod(method, converter), executorService);
+          classInitializers,
+          method -> processMethod(method, converter),
+          appView.options().getThreadingModule(),
+          executorService);
     }
   }
 
@@ -72,6 +75,7 @@
       ThreadUtils.processItems(
           instanceInitializers,
           clazz -> processInstanceInitializers(clazz, converter),
+          appView.options().getThreadingModule(),
           executorService);
     }
   }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/deadlock/SingleCallerInformation.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/deadlock/SingleCallerInformation.java
index 8696342..af868d9 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/deadlock/SingleCallerInformation.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/deadlock/SingleCallerInformation.java
@@ -73,7 +73,10 @@
 
     public Builder analyze(ExecutorService executorService) throws ExecutionException {
       ThreadUtils.processItems(
-          appView.appInfo()::forEachMethod, this::processMethod, executorService);
+          appView.appInfo()::forEachMethod,
+          this::processMethod,
+          appView.options().getThreadingModule(),
+          executorService);
       return this;
     }
 
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java
index 2161735..e033b0b 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java
@@ -36,6 +36,7 @@
 import com.android.tools.r8.ir.conversion.PostMethodProcessor;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackDelayed;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.threading.ThreadingModule;
 import com.android.tools.r8.utils.SetUtils;
 import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.Timing;
@@ -180,10 +181,13 @@
 
   private void enqueueMethodsForReprocessing(
       AppInfoWithLiveness appInfo, ExecutorService executorService) throws ExecutionException {
-    ThreadUtils.processItems(appInfo.classes(), this::processClass, executorService);
+    ThreadingModule threadingModule = appView.options().getThreadingModule();
+    ThreadUtils.processItems(
+        appInfo.classes(), this::processClass, threadingModule, executorService);
     ThreadUtils.processItems(
         appInfo.getSyntheticItems().getPendingSyntheticClasses(),
         this::processClass,
+        threadingModule,
         executorService);
     processFieldsNeverRead(appInfo);
     processFieldsNeverWritten(appInfo);
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java
index d89ae34..3dce430 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java
@@ -183,6 +183,7 @@
                 methodProcessor,
                 methodProcessingContext,
                 MethodConversionOptions.forLirPhase(appView)),
+        appView.options().getThreadingModule(),
         executorService);
     timing.end();
   }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteBuilderShrinker.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteBuilderShrinker.java
index d5aa0af..0dcf961 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteBuilderShrinker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteBuilderShrinker.java
@@ -226,6 +226,7 @@
                 appView, builder, dynamicMethod, converter, conversionOptions);
           }
         },
+        appView.options().getThreadingModule(),
         executorService);
     builders.clear();
     timing.end(); // Remove dead builder references
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java
index 75ca687..fad733e 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java
@@ -259,6 +259,7 @@
                 methodProcessor,
                 methodProcessingContext,
                 MethodConversionOptions.forLirPhase(appView)),
+        appView.options().getThreadingModule(),
         executorService);
     timing.end();
   }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java
index 4cd864a..c30ca76 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java
@@ -160,6 +160,7 @@
       ThreadUtils.processItems(
           wave,
           clazz -> convertClass(clazz, instructionDesugaringEventConsumerForWave),
+          appView.options().getThreadingModule(),
           executorService);
       methodProcessor.awaitMethodProcessing();
 
@@ -185,6 +186,7 @@
                 interfaceProcessor.processMethod(method, instructionDesugaringEventConsumerForWave);
               }
             },
+            appView.options().getThreadingModule(),
             executorService);
 
         // Verify there is nothing to finalize once method processing finishes.
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 73e94aa..b47b900 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -336,6 +336,7 @@
                 DexEncodedMethod::isAbstract, method -> method.convertToThrowNullMethod(appView));
           }
         },
+        appView.options().getThreadingModule(),
         executorService);
   }
 
@@ -356,7 +357,10 @@
   public void processSimpleSynthesizeMethods(
       List<ProgramMethod> methods, ExecutorService executorService) throws ExecutionException {
     ThreadUtils.processItems(
-        methods, this::processAndFinalizeSimpleSynthesizedMethod, executorService);
+        methods,
+        this::processAndFinalizeSimpleSynthesizedMethod,
+        options.getThreadingModule(),
+        executorService);
   }
 
   private void processAndFinalizeSimpleSynthesizedMethod(ProgramMethod method) {
@@ -445,6 +449,7 @@
                   methodProcessor,
                   methodProcessingContext,
                   conversionOptions),
+          appView.options().getThreadingModule(),
           executorService);
     }
   }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/OneTimeMethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/OneTimeMethodProcessor.java
index 3a1d213..6fed019 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/OneTimeMethodProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/OneTimeMethodProcessor.java
@@ -7,6 +7,7 @@
 import com.android.tools.r8.contexts.CompilationContext.ProcessorContext;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.threading.ThreadingModule;
 import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.ThreadUtils.WorkLoad;
 import com.android.tools.r8.utils.collections.ProgramMethodSet;
@@ -88,13 +89,15 @@
     }
   }
 
-  public void forEachWaveWithExtension(MethodAction consumer, ExecutorService executorService)
+  public void forEachWaveWithExtension(
+      MethodAction consumer, ThreadingModule threadingModule, ExecutorService executorService)
       throws ExecutionException {
     while (!wave.isEmpty()) {
       ThreadUtils.processItems(
           wave,
           (method, ignored) ->
               consumer.accept(method, processorContext.createMethodProcessingContext(method)),
+          threadingModule,
           executorService,
           WorkLoad.HEAVY);
       prepareForWaveExtensionProcessing();
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java
index cf04a02..a4b8cc3 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java
@@ -18,6 +18,7 @@
 import com.android.tools.r8.ir.conversion.callgraph.PartialCallGraphBuilder;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackDelayed;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.threading.ThreadingModule;
 import com.android.tools.r8.utils.DeterminismChecker;
 import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.Timing;
@@ -173,6 +174,7 @@
   <E extends Exception> void forEachMethod(
       MethodAction<E> consumer,
       OptimizationFeedbackDelayed feedback,
+      ThreadingModule threadingModule,
       ExecutorService executorService,
       Timing timing)
       throws ExecutionException {
@@ -193,6 +195,7 @@
                   time.end();
                   return time;
                 },
+                threadingModule,
                 executorService);
         merger.add(timings);
         feedback.updateVisibleOptimizationInfo();
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryD8L8IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryD8L8IRConverter.java
index b840b0d..e1a4c6f 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryD8L8IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryD8L8IRConverter.java
@@ -34,6 +34,7 @@
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
 import com.android.tools.r8.position.MethodPosition;
 import com.android.tools.r8.profile.rewriting.ProfileCollectionAdditions;
+import com.android.tools.r8.threading.ThreadingModule;
 import com.android.tools.r8.utils.ExceptionUtils;
 import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.ThreadUtils;
@@ -332,6 +333,7 @@
       throws ExecutionException {
     // Prepare desugaring by collecting all the synthetic methods required on program classes.
     ProgramAdditions programAdditions = new ProgramAdditions();
+    ThreadingModule threadingModule = appView.options().getThreadingModule();
     ThreadUtils.processItems(
         appView.appInfo().classes(),
         clazz -> {
@@ -340,8 +342,9 @@
               method ->
                   instructionDesugaring.prepare(method, desugaringEventConsumer, programAdditions));
         },
+        threadingModule,
         executorService);
-    programAdditions.apply(executorService);
+    programAdditions.apply(threadingModule, executorService);
   }
 
   @SuppressWarnings("BadImport")
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryMethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryMethodProcessor.java
index 51e38b0..8d33d03 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryMethodProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryMethodProcessor.java
@@ -143,6 +143,7 @@
                   time.end();
                   return time;
                 },
+                appView.options().getThreadingModule(),
                 executorService);
         merger.add(timings);
         waveDoneAction.notifyWaveDone(wave, executorService);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java
index 148e7df..1a4a2ec 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java
@@ -191,6 +191,7 @@
                     methodProcessingContext,
                     MethodConversionOptions.forLirPhase(appView)),
             feedback,
+            appView.options().getThreadingModule(),
             executorService,
             timing);
         timing.end();
@@ -248,6 +249,7 @@
         clazz ->
             clazz.forEachProgramMethod(
                 m -> finalizeLirMethodToOutputFormat(m, deadCodeRemover, appView, rewriterUtils)),
+        appView.options().getThreadingModule(),
         executorService);
     appView
         .getSyntheticItems()
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/callgraph/CallGraphBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/callgraph/CallGraphBuilder.java
index cc1f711..8d836c1 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/callgraph/CallGraphBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/callgraph/CallGraphBuilder.java
@@ -23,7 +23,11 @@
 
   @Override
   void populateGraph(ExecutorService executorService) throws ExecutionException {
-    ThreadUtils.processItems(appView.appInfo().classes(), this::processClass, executorService);
+    ThreadUtils.processItems(
+        appView.appInfo().classes(),
+        this::processClass,
+        appView.options().getThreadingModule(),
+        executorService);
   }
 
   private void processClass(DexProgramClass clazz) {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/callgraph/PartialCallGraphBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/callgraph/PartialCallGraphBuilder.java
index afab94b..787709f 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/callgraph/PartialCallGraphBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/callgraph/PartialCallGraphBuilder.java
@@ -24,7 +24,8 @@
 
   @Override
   void populateGraph(ExecutorService executorService) throws ExecutionException {
-    ThreadUtils.processItems(seeds, this::processMethod, executorService);
+    ThreadUtils.processItems(
+        seeds, this::processMethod, appView.options().getThreadingModule(), executorService);
   }
 
   private void processMethod(ProgramMethod method) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfClassSynthesizerDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/CfClassSynthesizerDesugaringCollection.java
index b40016f..f785b37 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfClassSynthesizerDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfClassSynthesizerDesugaringCollection.java
@@ -82,6 +82,7 @@
                 processorContext.createClassSynthesisDesugaringContext(synthesizer);
             synthesizer.synthesizeClasses(classSynthesisDesugaringContext, eventConsumer);
           },
+          appView.options().getThreadingModule(),
           executorService);
     }
   }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/ProgramAdditions.java b/src/main/java/com/android/tools/r8/ir/desugar/ProgramAdditions.java
index 932ea7c..31bd1da 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/ProgramAdditions.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/ProgramAdditions.java
@@ -9,6 +9,7 @@
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.threading.ThreadingModule;
 import com.android.tools.r8.utils.ThreadUtils;
 import java.util.ArrayList;
 import java.util.Comparator;
@@ -37,7 +38,8 @@
         });
   }
 
-  public void apply(ExecutorService executorService) throws ExecutionException {
+  public void apply(ThreadingModule threadingModule, ExecutorService executorService)
+      throws ExecutionException {
     ThreadUtils.processMap(
         additions,
         (holderType, methodMap) -> {
@@ -47,6 +49,7 @@
           newDirectMethods.sort(Comparator.comparing(DexEncodedMethod::getReference));
           holder.getMethodCollection().addDirectMethods(newDirectMethods);
         },
+        threadingModule,
         executorService);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
index 74b0447..8666c0b 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
@@ -522,6 +522,7 @@
             }
           }
         },
+        appView.options().getThreadingModule(),
         executorService);
     newExtraInterfaceSignatures.forEach(
         (clazz, extraInterfaceSignatures) -> {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodProcessorFacade.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodProcessorFacade.java
index 02f581d..3c3197b 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodProcessorFacade.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodProcessorFacade.java
@@ -53,6 +53,7 @@
     ThreadUtils.processItems(
         ListUtils.filter(programClasses, clazz -> shouldProcess(clazz, flavour)),
         clazz -> classProcessor.process(clazz, eventConsumer),
+        appView.options().getThreadingModule(),
         executorService);
     classProcessor.finalizeProcessing(eventConsumer, executorService);
     interfaceProcessor.finalizeProcessing();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/nest/D8NestBasedAccessDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/nest/D8NestBasedAccessDesugaring.java
index 34d6b95..14257d8 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/nest/D8NestBasedAccessDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/nest/D8NestBasedAccessDesugaring.java
@@ -126,6 +126,7 @@
     ThreadUtils.processItems(
         classpathClassesInNests,
         clazz -> synthesizeBridgesForNestBasedAccessesOnClasspath(clazz, eventConsumer),
+        appView.options().getThreadingModule(),
         executorService);
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/NestReducer.java b/src/main/java/com/android/tools/r8/ir/optimize/NestReducer.java
index 80ea1ad..d11f6f3 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/NestReducer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/NestReducer.java
@@ -10,6 +10,7 @@
 import com.android.tools.r8.graph.DexEncodedMember;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.threading.ThreadingModule;
 import com.android.tools.r8.utils.BooleanBox;
 import com.android.tools.r8.utils.IterableUtils;
 import com.android.tools.r8.utils.ThreadUtils;
@@ -71,8 +72,10 @@
         }
       }
     }
-    ThreadUtils.processItems(nestHosts, this::processNestHost, executorService);
-    ThreadUtils.processItems(nestMembers, this::processNestMember, executorService);
+    ThreadingModule threadingModule = appView.options().getThreadingModule();
+    ThreadUtils.processItems(nestHosts, this::processNestHost, threadingModule, executorService);
+    ThreadUtils.processItems(
+        nestMembers, this::processNestMember, threadingModule, executorService);
   }
 
   private void processNestHost(DexProgramClass clazz) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
index 20a8df6..5821b3a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
@@ -793,6 +793,7 @@
 
     feedback.fixupOptimizationInfos(
         appView,
+        appView.options().getThreadingModule(),
         executorService,
         new OptimizationInfoFixer() {
           @Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
index 32e5a1a..59be01e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
@@ -292,6 +292,7 @@
                 methodProcessor,
                 methodProcessingContext,
                 MethodConversionOptions.forLirPhase(appView)),
+        appView.options().getThreadingModule(),
         executorService);
 
     return checkNotNullToCheckNotZeroMapping;
@@ -304,6 +305,7 @@
     ThreadUtils.processItems(
         unboxedEnumHierarchy.keySet(),
         unboxedEnum -> fixupSuperEnumClassInitializer(converter, unboxedEnum, ordinalField),
+        appView.options().getThreadingModule(),
         executorService);
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingUtilityClasses.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingUtilityClasses.java
index fe3de2f..1bcae78 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingUtilityClasses.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingUtilityClasses.java
@@ -114,6 +114,7 @@
                   methodProcessor,
                   methodProcessingContext,
                   MethodConversionOptions.forLirPhase(appView)),
+          appView.options().getThreadingModule(),
           executorService);
       return utilityClasses;
     }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodResolutionOptimizationInfoAnalysis.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodResolutionOptimizationInfoAnalysis.java
index e1b6f85..509c709 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodResolutionOptimizationInfoAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodResolutionOptimizationInfoAnalysis.java
@@ -55,6 +55,7 @@
         stronglyConnectedComponents,
         stronglyConnectedComponent ->
             new Traversal(appView, builder, immediateSubtypingInfo).run(stronglyConnectedComponent),
+        appView.options().getThreadingModule(),
         executorService);
     MethodResolutionOptimizationInfoCollection methodResolutionOptimizationInfoCollection =
         builder.build();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodResolutionOptimizationInfoReprocessingEnqueuer.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodResolutionOptimizationInfoReprocessingEnqueuer.java
index 9ad09b2..2470444 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodResolutionOptimizationInfoReprocessingEnqueuer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodResolutionOptimizationInfoReprocessingEnqueuer.java
@@ -65,6 +65,7 @@
                   });
               return methodsToReprocessInClass;
             },
+            appView.options().getThreadingModule(),
             executorService);
     methodsToReprocess.forEach(
         methodsToReprocessInClass ->
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedback.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedback.java
index d033ca6..be0dd9b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedback.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedback.java
@@ -12,6 +12,7 @@
 import com.android.tools.r8.ir.conversion.FieldOptimizationFeedback;
 import com.android.tools.r8.ir.conversion.MethodOptimizationFeedback;
 import com.android.tools.r8.shaking.AppInfoWithLivenessModifier;
+import com.android.tools.r8.threading.ThreadingModule;
 import com.android.tools.r8.utils.ThreadUtils;
 import java.util.Collection;
 import java.util.concurrent.ExecutionException;
@@ -50,18 +51,22 @@
   }
 
   public void fixupOptimizationInfos(
-      AppView<?> appView, ExecutorService executorService, OptimizationInfoFixer fixer)
+      AppView<?> appView,
+      ThreadingModule threadingModule,
+      ExecutorService executorService,
+      OptimizationInfoFixer fixer)
       throws ExecutionException {
-    fixupOptimizationInfos(appView.appInfo().classes(), executorService, fixer);
+    fixupOptimizationInfos(appView.appInfo().classes(), threadingModule, executorService, fixer);
   }
 
   public void fixupOptimizationInfos(
       Collection<DexProgramClass> classes,
+      ThreadingModule threadingModule,
       ExecutorService executorService,
       OptimizationInfoFixer fixer)
       throws ExecutionException {
     ThreadUtils.processItems(
-        classes, clazz -> clazz.members().forEach(fixer::fixup), executorService);
+        classes, clazz -> clazz.members().forEach(fixer::fixup), threadingModule, executorService);
   }
 
   public void modifyAppInfoWithLiveness(Consumer<AppInfoWithLivenessModifier> consumer) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
index 5999efb..8a071b8 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
@@ -19,6 +19,7 @@
 import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfoCollection;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.AppInfoWithLivenessModifier;
+import com.android.tools.r8.threading.ThreadingModule;
 import com.android.tools.r8.utils.IteratorUtils;
 import com.android.tools.r8.utils.StringUtils;
 import java.util.BitSet;
@@ -68,10 +69,13 @@
 
   @Override
   public void fixupOptimizationInfos(
-      AppView<?> appView, ExecutorService executorService, OptimizationInfoFixer fixer)
+      AppView<?> appView,
+      ThreadingModule threadingModule,
+      ExecutorService executorService,
+      OptimizationInfoFixer fixer)
       throws ExecutionException {
     updateVisibleOptimizationInfo();
-    super.fixupOptimizationInfos(appView, executorService, fixer);
+    super.fixupOptimizationInfos(appView, threadingModule, executorService, fixer);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/inliner/multicallerinliner/MultiCallerInlinerCallGraphBuilder.java b/src/main/java/com/android/tools/r8/ir/optimize/inliner/multicallerinliner/MultiCallerInlinerCallGraphBuilder.java
index d54fc87..07c7931 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/inliner/multicallerinliner/MultiCallerInlinerCallGraphBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/inliner/multicallerinliner/MultiCallerInlinerCallGraphBuilder.java
@@ -28,7 +28,11 @@
 
   public MultiCallerInlinerCallGraph build(ExecutorService executorService)
       throws ExecutionException {
-    ThreadUtils.processItems(appView.appInfo().classes(), this::processClass, executorService);
+    ThreadUtils.processItems(
+        appView.appInfo().classes(),
+        this::processClass,
+        appView.options().getThreadingModule(),
+        executorService);
     return new MultiCallerInlinerCallGraph(nodes);
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/outliner/OutlinerImpl.java b/src/main/java/com/android/tools/r8/ir/optimize/outliner/OutlinerImpl.java
index 6873f86..b4ed86d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/outliner/OutlinerImpl.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/outliner/OutlinerImpl.java
@@ -1454,6 +1454,7 @@
           CodeRewriter.removeAssumeInstructions(appView, code);
           consumer.accept(code);
         },
+        appView.options().getThreadingModule(),
         executorService);
   }
 
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java
index fe4eea6..eb55ca1 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java
@@ -129,6 +129,7 @@
           }
           writeKotlinInfoToAnnotation(clazz, kotlinInfo, oldMeta, writeMetadataFieldInfo);
         },
+        appView.options().getThreadingModule(),
         executorService);
     appView.setKotlinMetadataLens(appView.graphLens());
   }
@@ -158,6 +159,7 @@
           }
           writeKotlinInfoToAnnotation(clazz, kotlinInfo, metadata, writeMetadataFieldInfo);
         },
+        appView.options().getThreadingModule(),
         executorService);
   }
 
diff --git a/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java b/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java
index ba443fe..3a8b545 100644
--- a/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java
@@ -65,8 +65,8 @@
             clazz.forEachMethod(this::adaptClassStringsInMethod);
           }
         },
-        executorService
-    );
+        appView.options().getThreadingModule(),
+        executorService);
   }
 
   private void adaptClassStringsInStaticField(DexEncodedField encodedField) {
@@ -158,6 +158,7 @@
           clazz.forEachProgramMethodMatching(
               DexEncodedMethod::hasCode, this::replaceDexItemBasedConstStringInMethod);
         },
+        appView.options().getThreadingModule(),
         executorService);
   }
 
diff --git a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java
index caf0a51..14a837a 100644
--- a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java
+++ b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java
@@ -69,8 +69,8 @@
             decoupleIdentifierNameStringInStaticField(field);
           }
         },
-        executorService
-    );
+        appView.options().getThreadingModule(),
+        executorService);
   }
 
   private void decoupleIdentifierNameStringInStaticField(DexEncodedField encodedField) {
diff --git a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
index ccfeddc..4dd3d15 100644
--- a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
@@ -442,6 +442,7 @@
     ThreadUtils.processItems(
         methodAccessInfoCollection::forEachMethodReference,
         method -> renameNonReboundMethodReference(method, nonReboundRenamings),
+        appView.options().getThreadingModule(),
         executorService);
     renaming.putAll(nonReboundRenamings);
   }
diff --git a/src/main/java/com/android/tools/r8/naming/RecordInvokeDynamicInvokeCustomRewriter.java b/src/main/java/com/android/tools/r8/naming/RecordInvokeDynamicInvokeCustomRewriter.java
index 78eb905..d3d1212 100644
--- a/src/main/java/com/android/tools/r8/naming/RecordInvokeDynamicInvokeCustomRewriter.java
+++ b/src/main/java/com/android/tools/r8/naming/RecordInvokeDynamicInvokeCustomRewriter.java
@@ -39,6 +39,7 @@
           clazz.forEachProgramMethodMatching(
               DexEncodedMethod::hasCode, this::rewriteRecordInvokeDynamicInMethod);
         },
+        appView.options().getThreadingModule(),
         executorService);
   }
 
diff --git a/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureRewriter.java b/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureRewriter.java
index ad239c5..7e91d18 100644
--- a/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureRewriter.java
+++ b/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureRewriter.java
@@ -114,6 +114,7 @@
             genericSignatureTypeRewriter.rewrite(recordComponent.getSignature());
           }
         },
+        appView.options().getThreadingModule(),
         executorService);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
index 82a9980..d8514a0 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
@@ -645,6 +645,7 @@
                 });
           }
         },
+        appView.options().getThreadingModule(),
         executorService);
     return nonReboundFieldReferences;
   }
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLensFactory.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLensFactory.java
index 7367af3..dfd16e6 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLensFactory.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLensFactory.java
@@ -97,6 +97,7 @@
                     seenFieldReferences,
                     seenMethodReferences)
                 .accept(method),
+        appView.options().getThreadingModule(),
         executorService);
   }
 
diff --git a/src/main/java/com/android/tools/r8/optimize/accessmodification/AccessModifier.java b/src/main/java/com/android/tools/r8/optimize/accessmodification/AccessModifier.java
index c97f10b..64570f5 100644
--- a/src/main/java/com/android/tools/r8/optimize/accessmodification/AccessModifier.java
+++ b/src/main/java/com/android/tools/r8/optimize/accessmodification/AccessModifier.java
@@ -78,7 +78,10 @@
         new ProgramClassesBidirectedGraph(appView, immediateSubtypingInfo)
             .computeStronglyConnectedComponents();
     ThreadUtils.processItems(
-        stronglyConnectedComponents, this::processStronglyConnectedComponent, executorService);
+        stronglyConnectedComponents,
+        this::processStronglyConnectedComponent,
+        appView.options().getThreadingModule(),
+        executorService);
     return this;
   }
 
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java
index f7baed9..2f90ff5 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java
@@ -100,6 +100,7 @@
           // calls.
           effectivelyUnusedArgumentsAnalysis.initializeOptimizableVirtualMethods(classes);
         },
+        appView.options().getThreadingModule(),
         executorService);
 
     timing.end();
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorApplicationFixer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorApplicationFixer.java
index 3d7c4e6..06118f6 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorApplicationFixer.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorApplicationFixer.java
@@ -61,7 +61,8 @@
     assert !affectedClasses.isEmpty();
 
     timing.begin("Fixup application");
-    ThreadUtils.processItems(affectedClasses, this::fixupClass, executorService);
+    ThreadUtils.processItems(
+        affectedClasses, this::fixupClass, appView.options().getThreadingModule(), executorService);
     timing.end();
 
     // Fixup optimization info.
@@ -131,6 +132,7 @@
     getSimpleFeedback()
         .fixupOptimizationInfos(
             appView,
+            appView.options().getThreadingModule(),
             executorService,
             new OptimizationInfoFixer() {
               @Override
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java
index 848e450..9b0f212 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java
@@ -138,6 +138,7 @@
                   });
               return methodsToReprocessInClass;
             },
+            appView.options().getThreadingModule(),
             executorService);
     methodsToReprocess.forEach(
         methodsToReprocessInClass ->
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java
index 1a94bc3..c626347 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java
@@ -97,6 +97,7 @@
     ThreadUtils.processItems(
         stronglyConnectedProgramComponents,
         this::processStronglyConnectedComponent,
+        appView.options().getThreadingModule(),
         executorService);
     timing.end();
 
@@ -153,6 +154,7 @@
     ThreadUtils.processItems(
         appView.appInfo().classes(),
         clazz -> prunedMethods.addAll(setOptimizationInfo(clazz)),
+        appView.options().getThreadingModule(),
         executorService);
     for (ProgramMethod prunedMethod : prunedMethods) {
       converter.onMethodPruned(prunedMethod);
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
index fbce71a..181f551 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
@@ -209,6 +209,7 @@
                         interfaceDispatchOutsideProgram.getOrDefault(
                             classes, DexMethodSignatureSet.empty()),
                         affectedClassConsumer),
+            appView.options().getThreadingModule(),
             executorService);
     eventConsumer.finished(appView);
     timing.end();
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InParameterFlowPropagator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InParameterFlowPropagator.java
index fae616e..c9794f5 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InParameterFlowPropagator.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InParameterFlowPropagator.java
@@ -62,7 +62,11 @@
 
     List<Set<ParameterNode>> stronglyConnectedComponents =
         flowGraph.computeStronglyConnectedComponents();
-    ThreadUtils.processItems(stronglyConnectedComponents, this::process, executorService);
+    ThreadUtils.processItems(
+        stronglyConnectedComponents,
+        this::process,
+        appView.options().getThreadingModule(),
+        executorService);
 
     // The algorithm only changes the parameter states of each monomorphic method state. In case any
     // of these method states have effectively become unknown, we replace them by the canonicalized
@@ -120,7 +124,10 @@
 
   private void postProcessMethodStates(ExecutorService executorService) throws ExecutionException {
     ThreadUtils.processItems(
-        appView.appInfo().classes(), this::postProcessMethodStates, executorService);
+        appView.appInfo().classes(),
+        this::postProcessMethodStates,
+        appView.options().getThreadingModule(),
+        executorService);
   }
 
   private void postProcessMethodStates(DexProgramClass clazz) {
diff --git a/src/main/java/com/android/tools/r8/optimize/fields/FieldFinalizer.java b/src/main/java/com/android/tools/r8/optimize/fields/FieldFinalizer.java
index 154c354..41605be 100644
--- a/src/main/java/com/android/tools/r8/optimize/fields/FieldFinalizer.java
+++ b/src/main/java/com/android/tools/r8/optimize/fields/FieldFinalizer.java
@@ -43,7 +43,11 @@
   }
 
   private void processClasses(ExecutorService executorService) throws ExecutionException {
-    ThreadUtils.processItems(appView.appInfo().classes(), this::processClass, executorService);
+    ThreadUtils.processItems(
+        appView.appInfo().classes(),
+        this::processClass,
+        appView.options().getThreadingModule(),
+        executorService);
   }
 
   private void processClass(DexProgramClass clazz) {
diff --git a/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/CfOpenClosedInterfacesAnalysis.java b/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/CfOpenClosedInterfacesAnalysis.java
index c33be3c..7ab0fa9 100644
--- a/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/CfOpenClosedInterfacesAnalysis.java
+++ b/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/CfOpenClosedInterfacesAnalysis.java
@@ -73,7 +73,11 @@
   }
 
   private void processClasses(ExecutorService executorService) throws ExecutionException {
-    ThreadUtils.processItems(appView.appInfo().classes(), this::processClass, executorService);
+    ThreadUtils.processItems(
+        appView.appInfo().classes(),
+        this::processClass,
+        appView.options().getThreadingModule(),
+        executorService);
   }
 
   private void processClass(DexProgramClass clazz) {
diff --git a/src/main/java/com/android/tools/r8/optimize/proto/ProtoNormalizer.java b/src/main/java/com/android/tools/r8/optimize/proto/ProtoNormalizer.java
index 902fb45..a02ddf5 100644
--- a/src/main/java/com/android/tools/r8/optimize/proto/ProtoNormalizer.java
+++ b/src/main/java/com/android/tools/r8/optimize/proto/ProtoNormalizer.java
@@ -210,6 +210,7 @@
                 }
               });
         },
+        appView.options().getThreadingModule(),
         executorService);
 
     // Reserve parameter lists that won't lead to any sharing after normalization. Any method with
@@ -240,6 +241,7 @@
         method ->
             computeExtraReservationsFromMethod(
                 method, unoptimizableParameterLists, unoptimizableSignatures),
+        appView.options().getThreadingModule(),
         executorService);
 
     return new GlobalReservationState(reservedParameterLists, unoptimizableSignatures);
diff --git a/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemover.java b/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemover.java
index 53b3147..b1e1922 100644
--- a/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemover.java
+++ b/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemover.java
@@ -172,6 +172,7 @@
             stronglyConnectedProgramComponents,
             this::removeRedundantBridgesInComponent,
             removedBridges -> !removedBridges.isEmpty(),
+            appView.options().getThreadingModule(),
             executorService);
     ProgramMethodSet removedBridges = ProgramMethodSet.create();
     results.forEach(
diff --git a/src/main/java/com/android/tools/r8/profile/startup/instrumentation/StartupInstrumentation.java b/src/main/java/com/android/tools/r8/profile/startup/instrumentation/StartupInstrumentation.java
index 550458f..ba3abda 100644
--- a/src/main/java/com/android/tools/r8/profile/startup/instrumentation/StartupInstrumentation.java
+++ b/src/main/java/com/android/tools/r8/profile/startup/instrumentation/StartupInstrumentation.java
@@ -75,7 +75,11 @@
   }
 
   private void instrumentAllClasses(ExecutorService executorService) throws ExecutionException {
-    ThreadUtils.processItems(appView.appInfo().classes(), this::instrumentClass, executorService);
+    ThreadUtils.processItems(
+        appView.appInfo().classes(),
+        this::instrumentClass,
+        appView.options().getThreadingModule(),
+        executorService);
   }
 
   private void injectStartupRuntimeLibrary(ExecutorService executorService)
diff --git a/src/main/java/com/android/tools/r8/repackaging/RepackagingConstraintGraph.java b/src/main/java/com/android/tools/r8/repackaging/RepackagingConstraintGraph.java
index 50e39b4..9313b7c 100644
--- a/src/main/java/com/android/tools/r8/repackaging/RepackagingConstraintGraph.java
+++ b/src/main/java/com/android/tools/r8/repackaging/RepackagingConstraintGraph.java
@@ -93,7 +93,10 @@
   public void populateConstraints(ExecutorService executorService) throws ExecutionException {
     // Concurrently add references from methods to the graph.
     ThreadUtils.processItems(
-        pkg::forEachMethod, this::registerReferencesFromMethod, executorService);
+        pkg::forEachMethod,
+        this::registerReferencesFromMethod,
+        appView.options().getThreadingModule(),
+        executorService);
 
     // TODO(b/165783399): Evaluate if it is worth to parallelize this. The work per field and class
     //  should be little, so it may not be.
diff --git a/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
index a02420c..40e2beb 100644
--- a/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
+++ b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
@@ -182,7 +182,11 @@
   }
 
   public void run(ExecutorService executorService) throws ExecutionException {
-    ThreadUtils.processItems(appView.appInfo().classes(), this::run, executorService);
+    ThreadUtils.processItems(
+        appView.appInfo().classes(),
+        this::run,
+        appView.options().getThreadingModule(),
+        executorService);
     assert verifyNoKeptKotlinMembersForClassesWithNoKotlinInfo();
   }
 
diff --git a/src/main/java/com/android/tools/r8/shaking/ClassInitFieldSynthesizer.java b/src/main/java/com/android/tools/r8/shaking/ClassInitFieldSynthesizer.java
index 1888d15..a06ecbc 100644
--- a/src/main/java/com/android/tools/r8/shaking/ClassInitFieldSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/shaking/ClassInitFieldSynthesizer.java
@@ -33,7 +33,10 @@
 
   public void run(ExecutorService executorService) throws ExecutionException {
     ThreadUtils.processMap(
-        appView.appInfo().initClassReferences, this::synthesizeClassInitField, executorService);
+        appView.appInfo().initClassReferences,
+        this::synthesizeClassInitField,
+        appView.options().getThreadingModule(),
+        executorService);
     appView.setInitClassLens(lensBuilder.build());
     appView.notifyOptimizationFinishedForTesting();
   }
diff --git a/src/main/java/com/android/tools/r8/shaking/DiscardedChecker.java b/src/main/java/com/android/tools/r8/shaking/DiscardedChecker.java
index cbc4940..3d01f22 100644
--- a/src/main/java/com/android/tools/r8/shaking/DiscardedChecker.java
+++ b/src/main/java/com/android/tools/r8/shaking/DiscardedChecker.java
@@ -55,7 +55,11 @@
     assert failed.isEmpty();
 
     // TODO(b/131668850): Consider only iterating the items matched by a -checkdiscard rule.
-    ThreadUtils.processItems(classes, this::checkClassAndMembers, executorService);
+    ThreadUtils.processItems(
+        classes,
+        this::checkClassAndMembers,
+        appView.options().getThreadingModule(),
+        executorService);
 
     // Sort the failures for determinism.
     failed.sort((item, other) -> item.getReference().compareTo(other.getReference()));
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index fda3ddf..6bcbfc7 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -4107,13 +4107,15 @@
     ThreadUtils.processItems(
         pendingCodeDesugaring,
         method -> desugaring.prepare(method, eventConsumer, programAdditions),
+        appView.options().getThreadingModule(),
         executorService);
-    programAdditions.apply(executorService);
+    programAdditions.apply(appView.options().getThreadingModule(), executorService);
 
     // Then do the actual desugaring.
     ThreadUtils.processItems(
         pendingCodeDesugaring,
         method -> desugaring.desugar(method, additions.getMethodContext(method), eventConsumer),
+        appView.options().getThreadingModule(),
         executorService);
 
     // Move the pending methods and mark them live and ready for tracing.
diff --git a/src/main/java/com/android/tools/r8/shaking/EnqueuerDeferredTracingImpl.java b/src/main/java/com/android/tools/r8/shaking/EnqueuerDeferredTracingImpl.java
index 22d9468..759ebd7 100644
--- a/src/main/java/com/android/tools/r8/shaking/EnqueuerDeferredTracingImpl.java
+++ b/src/main/java/com/android/tools/r8/shaking/EnqueuerDeferredTracingImpl.java
@@ -243,6 +243,7 @@
     ThreadUtils.processItems(
         methodsToProcess,
         (method, ignored) -> rewriteMethod(method, initializedClassesWithContexts, prunedFields),
+        appView.options().getThreadingModule(),
         executorService,
         WorkLoad.HEAVY);
 
diff --git a/src/main/java/com/android/tools/r8/shaking/TreePruner.java b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
index 62a06ae..7dc92f2 100644
--- a/src/main/java/com/android/tools/r8/shaking/TreePruner.java
+++ b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
@@ -437,6 +437,7 @@
     OptimizationFeedback feedback = OptimizationFeedbackSimple.getInstance();
     feedback.fixupOptimizationInfos(
         application.classes(),
+        appView.options().getThreadingModule(),
         executorService,
         new OptimizationInfoFixer() {
           @Override
diff --git a/src/main/java/com/android/tools/r8/threading/SynchronizedTaskCollection.java b/src/main/java/com/android/tools/r8/threading/SynchronizedTaskCollection.java
index d82886e..43863f1 100644
--- a/src/main/java/com/android/tools/r8/threading/SynchronizedTaskCollection.java
+++ b/src/main/java/com/android/tools/r8/threading/SynchronizedTaskCollection.java
@@ -5,11 +5,13 @@
 package com.android.tools.r8.threading;
 
 import com.android.tools.r8.utils.InternalOptions;
+import com.google.common.util.concurrent.Futures;
 import java.util.List;
 import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Future;
+import java.util.function.Consumer;
 
 public class SynchronizedTaskCollection<T> extends TaskCollection<T> {
 
@@ -22,20 +24,24 @@
     super.submit(task);
   }
 
+  private synchronized List<Future<T>> synchronizedGetAndClearFutures() {
+    return internalGetAndClearFutures();
+  }
+
   @Override
-  public void await() throws ExecutionException {
+  public void await(Consumer<T> consumer) throws ExecutionException {
     // Assuming tasks may add new tasks, awaiting all pending tasks must be run in a loop.
     // The identification of futures is synchronized with submit so that we don't have concurrent
     // modification of the task list.
-    List<Future<T>> futures;
-    synchronized (this) {
-      futures = getAndClearFutures();
-    }
-    do {
-      getThreadingModule().awaitFutures(futures);
-      synchronized (this) {
-        futures = getAndClearFutures();
+    List<Future<T>> futures = synchronizedGetAndClearFutures();
+    while (!futures.isEmpty()) {
+      internalGetThreadingModule().awaitFutures(futures);
+      if (consumer != null) {
+        for (Future<T> f : futures) {
+          consumer.accept(Futures.getDone(f));
+        }
       }
-    } while (!futures.isEmpty());
+      futures = synchronizedGetAndClearFutures();
+    }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/threading/TaskCollection.java b/src/main/java/com/android/tools/r8/threading/TaskCollection.java
index 3f7752c..11a64bb 100644
--- a/src/main/java/com/android/tools/r8/threading/TaskCollection.java
+++ b/src/main/java/com/android/tools/r8/threading/TaskCollection.java
@@ -6,12 +6,15 @@
 
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.ThrowingAction;
+import com.google.common.util.concurrent.Futures;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Future;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
 
 public class TaskCollection<T> {
 
@@ -20,29 +23,74 @@
 
   private final List<Future<T>> futures = new ArrayList<>();
 
-  public TaskCollection(InternalOptions options, ExecutorService executorService) {
-    this.threadingModule = options.getThreadingModule();
+  public TaskCollection(ThreadingModule threadingModule, ExecutorService executorService) {
+    this.threadingModule = threadingModule;
     this.executorService = executorService;
   }
 
-  public int size() {
+  public TaskCollection(InternalOptions options, ExecutorService executorService) {
+    this(options.getThreadingModule(), executorService);
+  }
+
+  /**
+   * Submit a task for execution.
+   *
+   * <p>The task may start running immediately and on the same thread as the caller, or may run
+   * later with completion ensured by a call to {@link #await(Consumer)}.
+   *
+   * <p>This is the main implementation of adding a task for execution.
+   *
+   * @param task Task to submit for execution.
+   */
+  public void submit(Callable<T> task) throws ExecutionException {
+    futures.add(threadingModule.submit(task, executorService));
+  }
+
+  /**
+   * Await the completion of all tasks.
+   *
+   * <p>This is the main implementation of awaiting task executions.
+   *
+   * @param consumer Consumer to get each task result. Use null if no results are needed.
+   */
+  public void await(Consumer<T> consumer) throws ExecutionException {
+    threadingModule.awaitFutures(futures);
+    if (consumer != null) {
+      for (Future<T> future : futures) {
+        consumer.accept(Futures.getDone(future));
+      }
+    }
+    futures.clear();
+  }
+
+  // Final helper methods for the collection.
+
+  /** Number of current tasks in the collection. */
+  public final int size() {
     return futures.size();
   }
 
-  public boolean isEmpty() {
+  /** True if no tasks are in the collection. */
+  public final boolean isEmpty() {
     return size() == 0;
   }
 
-  List<Future<T>> getAndClearFutures() {
+  // Internal getter for subclasses.
+  final List<Future<T>> internalGetAndClearFutures() {
     List<Future<T>> copy = new ArrayList<>(futures);
     futures.clear();
     return copy;
   }
 
-  ThreadingModule getThreadingModule() {
+  // Internal getter for subclasses.
+  final ThreadingModule internalGetThreadingModule() {
     return threadingModule;
   }
 
+  // All methods below are derived from the two primitives above.
+  // This ensures that the synchronized impl remains sound.
+
+  /** Derived submit to allow throwing tasks. */
   public final <E extends Exception> void submit(ThrowingAction<E> task) throws ExecutionException {
     submit(
         () -> {
@@ -51,12 +99,30 @@
         });
   }
 
-  public void submit(Callable<T> task) throws ExecutionException {
-    futures.add(threadingModule.submit(task, executorService));
+  /** Derived await when no results are needed. */
+  public final void await() throws ExecutionException {
+    await(null);
   }
 
-  public void await() throws ExecutionException {
-    threadingModule.awaitFutures(futures);
-    futures.clear();
+  /** Derived await to get all the results in a list. */
+  public final List<T> awaitWithResults() throws ExecutionException {
+    List<T> results = new ArrayList<>(size());
+    await(results::add);
+    return results;
+  }
+
+  /** Derived await to get a subset of the results in a list. */
+  public final List<T> awaitWithResults(Predicate<T> predicate) throws ExecutionException {
+    if (predicate == null) {
+      return awaitWithResults();
+    }
+    List<T> filtered = new ArrayList<>();
+    await(
+        result -> {
+          if (predicate.test(result)) {
+            filtered.add(result);
+          }
+        });
+    return filtered;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 1fe0c94..174afcc 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -2190,6 +2190,7 @@
                   method.setCode(lirCode, appView);
                 });
           },
+          appView.options().getThreadingModule(),
           executorService);
       // Conversion to LIR via IR will allocate type elements.
       // They are not needed after construction so remove them again.
diff --git a/src/main/java/com/android/tools/r8/utils/ThreadUtils.java b/src/main/java/com/android/tools/r8/utils/ThreadUtils.java
index 0e5d90c..bfab6da 100644
--- a/src/main/java/com/android/tools/r8/utils/ThreadUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ThreadUtils.java
@@ -3,15 +3,13 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.utils;
 
-import static com.google.common.base.Predicates.alwaysTrue;
-
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.threading.TaskCollection;
+import com.android.tools.r8.threading.ThreadingModule;
 import com.android.tools.r8.utils.ListUtils.ReferenceAndIntConsumer;
-import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Iterator;
-import java.util.List;
 import java.util.Map;
 import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutionException;
@@ -74,89 +72,102 @@
   }
 
   public static <T, R, E extends Exception> Collection<R> processItemsWithResults(
-      Iterable<T> items, ThrowingFunction<T, R, E> consumer, ExecutorService executorService)
+      Iterable<T> items,
+      ThrowingFunction<T, R, E> consumer,
+      ThreadingModule threadingModule,
+      ExecutorService executorService)
       throws ExecutionException {
-    return processItemsWithResults(items, (item, i) -> consumer.apply(item), executorService);
+    return processItemsWithResults(
+        items, (item, i) -> consumer.apply(item), threadingModule, executorService);
   }
 
   public static <T, R, E extends Exception> Collection<R> processItemsWithResultsThatMatches(
       Iterable<T> items,
       ThrowingFunction<T, R, E> consumer,
       Predicate<R> predicate,
+      ThreadingModule threadingModule,
       ExecutorService executorService)
       throws ExecutionException {
     return processItemsWithResultsThatMatches(
-        items, (item, i) -> consumer.apply(item), predicate, executorService);
+        items, (item, i) -> consumer.apply(item), predicate, threadingModule, executorService);
   }
 
   public static <T, R, E extends Exception> Collection<R> processItemsWithResults(
       Iterable<T> items,
       ThrowingReferenceIntFunction<T, R, E> consumer,
+      ThreadingModule threadingModule,
       ExecutorService executorService)
       throws ExecutionException {
-    return processItemsWithResults(items::forEach, consumer, executorService);
+    return processItemsWithResults(items::forEach, consumer, threadingModule, executorService);
   }
 
   public static <T, R, E extends Exception> Collection<R> processItemsWithResultsThatMatches(
       Iterable<T> items,
       ThrowingReferenceIntFunction<T, R, E> consumer,
       Predicate<R> predicate,
+      ThreadingModule threadingModule,
       ExecutorService executorService)
       throws ExecutionException {
-    return processItemsWithResultsThatMatches(items::forEach, consumer, predicate, executorService);
-  }
-
-  public static <T, R, E extends Exception> Collection<R> processItemsWithResults(
-      ForEachable<T> items, ThrowingFunction<T, R, E> consumer, ExecutorService executorService)
-      throws ExecutionException {
-    return processItemsWithResults(items, (item, i) -> consumer.apply(item), executorService);
+    return processItemsWithResultsThatMatches(
+        items::forEach, consumer, predicate, threadingModule, executorService);
   }
 
   public static <T, R, E extends Exception> Collection<R> processItemsWithResults(
       ForEachable<T> items,
       ThrowingReferenceIntFunction<T, R, E> consumer,
+      ThreadingModule threadingModule,
       ExecutorService executorService)
       throws ExecutionException {
-    IntBox indexSupplier = new IntBox();
-    List<Future<R>> futures = new ArrayList<>();
-    items.forEach(
-        item -> {
-          int index = indexSupplier.getAndIncrement();
-          futures.add(executorService.submit(() -> consumer.apply(item, index)));
-        });
-    return awaitFuturesWithResults(futures, alwaysTrue());
+    return processItemsWithResultsThatMatches(
+        items, consumer, null, threadingModule, executorService);
   }
 
   public static <T, R, E extends Exception> Collection<R> processItemsWithResultsThatMatches(
       ForEachable<T> items,
       ThrowingReferenceIntFunction<T, R, E> consumer,
       Predicate<R> predicate,
+      ThreadingModule threadingModule,
       ExecutorService executorService)
       throws ExecutionException {
-    IntBox indexSupplier = new IntBox();
-    List<Future<R>> futures = new ArrayList<>();
-    items.forEach(
-        item -> {
-          int index = indexSupplier.getAndIncrement();
-          futures.add(executorService.submit(() -> consumer.apply(item, index)));
-        });
-    return awaitFuturesWithResults(futures, predicate);
+    TaskCollection<R> tasks = new TaskCollection<>(threadingModule, executorService);
+    try {
+      items.forEachWithIndex(
+          (index, item) -> {
+            try {
+              tasks.submit(() -> consumer.apply(item, index));
+            } catch (ExecutionException e) {
+              throw new UncheckedExecutionException(e);
+            }
+          });
+    } catch (UncheckedExecutionException e) {
+      throw e.rethrow();
+    }
+    return tasks.awaitWithResults(predicate);
   }
 
   public static <T> void processItems(
-      Collection<T> items, Consumer<T> consumer, ExecutorService executorService)
+      Collection<T> items,
+      Consumer<T> consumer,
+      ThreadingModule threadingModule,
+      ExecutorService executorService)
       throws ExecutionException {
-    processItems(items, (item, i) -> consumer.accept(item), executorService, WorkLoad.LIGHT);
+    processItems(
+        items,
+        (item, i) -> consumer.accept(item),
+        threadingModule,
+        executorService,
+        WorkLoad.LIGHT);
   }
 
   public static <T> void processItems(
       Collection<T> items,
       ReferenceAndIntConsumer<T> consumer,
+      ThreadingModule threadingModule,
       ExecutorService executorService,
       WorkLoad workLoad)
       throws ExecutionException {
     if (items.size() >= workLoad.getThreshold()) {
-      processItems(items::forEach, consumer::accept, executorService);
+      processItems(items::forEach, consumer::accept, threadingModule, executorService);
     } else {
       int counter = 0;
       for (T item : items) {
@@ -166,14 +177,18 @@
   }
 
   public static <T, E extends Exception> void processItems(
-      ForEachable<T> items, ThrowingConsumer<T, E> consumer, ExecutorService executorService)
+      ForEachable<T> items,
+      ThrowingConsumer<T, E> consumer,
+      ThreadingModule threadingModule,
+      ExecutorService executorService)
       throws ExecutionException {
-    processItems(items, (item, i) -> consumer.accept(item), executorService);
+    processItems(items, (item, i) -> consumer.accept(item), threadingModule, executorService);
   }
 
   public static <T, E extends Exception> void processItems(
       ForEachable<T> items,
       ThrowingReferenceIntConsumer<T, E> consumer,
+      ThreadingModule threadingModule,
       ExecutorService executorService)
       throws ExecutionException {
     processItemsWithResults(
@@ -182,11 +197,15 @@
           consumer.accept(item, i);
           return null;
         },
+        threadingModule,
         executorService);
   }
 
   public static <T, U, E extends Exception> void processMap(
-      Map<T, U> items, ThrowingBiConsumer<T, U, E> consumer, ExecutorService executorService)
+      Map<T, U> items,
+      ThrowingBiConsumer<T, U, E> consumer,
+      ThreadingModule threadingModule,
+      ExecutorService executorService)
       throws ExecutionException {
     processMapWithResults(
         items,
@@ -194,24 +213,33 @@
           consumer.accept(key, value);
           return null;
         },
+        threadingModule,
         executorService);
   }
 
   public static <T, U, R, E extends Exception> Collection<R> processMapWithResults(
-      Map<T, U> items, ThrowingBiFunction<T, U, R, E> consumer, ExecutorService executorService)
+      Map<T, U> items,
+      ThrowingBiFunction<T, U, R, E> consumer,
+      ThreadingModule threadingModule,
+      ExecutorService executorService)
       throws ExecutionException {
     return processItemsWithResults(
-        items.entrySet(), arg -> consumer.apply(arg.getKey(), arg.getValue()), executorService);
+        items.entrySet(),
+        arg -> consumer.apply(arg.getKey(), arg.getValue()),
+        threadingModule,
+        executorService);
   }
 
   public static <E extends Exception> void processMethods(
       AppView<?> appView,
       ThrowingConsumer<ProgramMethod, E> consumer,
+      ThreadingModule threadingModule,
       ExecutorService executorService)
       throws ExecutionException {
     processItems(
         appView.appInfo().classes(),
         clazz -> clazz.forEachProgramMethod(consumer::acceptWithRuntimeException),
+        threadingModule,
         executorService);
   }
 
@@ -238,35 +266,6 @@
     }
   }
 
-  public static <R> Collection<R> awaitFuturesWithResults(
-      Collection<? extends Future<R>> futures, Predicate<R> predicate) throws ExecutionException {
-    List<R> results =
-        predicate == alwaysTrue() ? new ArrayList<>(futures.size()) : new ArrayList<>();
-    Iterator<? extends Future<R>> futureIterator = futures.iterator();
-    try {
-      while (futureIterator.hasNext()) {
-        R result = futureIterator.next().get();
-        if (predicate.test(result)) {
-          results.add(result);
-        }
-      }
-    } catch (InterruptedException e) {
-      throw new RuntimeException("Interrupted while waiting for future.", e);
-    } finally {
-      // In case we get interrupted or one of the threads throws an exception, still wait for all
-      // further work to make sure synchronization guarantees are met. Calling cancel unfortunately
-      // does not guarantee that the task at hand actually terminates before cancel returns.
-      while (futureIterator.hasNext()) {
-        try {
-          futureIterator.next().get();
-        } catch (Throwable t) {
-          // Ignore any new Exception.
-        }
-      }
-    }
-    return results;
-  }
-
   static ExecutorService getExecutorServiceForProcessors(int processors) {
     // This heuristic is based on measurements on a 32 core (hyper-threaded) machine.
     int threads = processors <= 2 ? processors : (int) Math.ceil(Integer.min(processors, 16) / 2.0);
diff --git a/src/main/java/com/android/tools/r8/utils/UncheckedExecutionException.java b/src/main/java/com/android/tools/r8/utils/UncheckedExecutionException.java
new file mode 100644
index 0000000..e2ac7de
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/UncheckedExecutionException.java
@@ -0,0 +1,21 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils;
+
+import java.util.concurrent.ExecutionException;
+
+public class UncheckedExecutionException extends RuntimeException {
+
+  private final ExecutionException exception;
+
+  public UncheckedExecutionException(ExecutionException exception) {
+    super(exception);
+    this.exception = exception;
+  }
+
+  public ExecutionException rethrow() throws ExecutionException {
+    throw exception;
+  }
+}