| // Copyright (c) 2025, 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.shaking; | |
| import com.android.tools.r8.graph.analysis.EnqueuerAnalysisCollection; | |
| import com.android.tools.r8.graph.analysis.FinishedEnqueuerAnalysis; | |
| import com.android.tools.r8.graph.analysis.FixpointEnqueuerAnalysis; | |
| import com.android.tools.r8.threading.TaskCollection; | |
| import com.android.tools.r8.threading.ThreadingModule; | |
| import com.android.tools.r8.utils.timing.Timing; | |
| import java.util.concurrent.Callable; | |
| import java.util.concurrent.ExecutionException; | |
| import java.util.concurrent.ExecutorService; | |
| public class EnqueuerTaskCollection implements FixpointEnqueuerAnalysis, FinishedEnqueuerAnalysis { | |
| private final Enqueuer enqueuer; | |
| // A task collection that stores tasks that may enqueue worklist items. The enqueuer therefore | |
| // needs to await this task collection upon each intermediate fixpoint. | |
| private final TaskCollection<Void> enqueuerDependentTaskCollection; | |
| // A task collection that stores tasks that the Enqueuer does not need to await until it is | |
| // entirely done. | |
| private final TaskCollection<Void> enqueuerIndependentTaskCollection; | |
| EnqueuerTaskCollection( | |
| Enqueuer enqueuer, ThreadingModule threadingModule, ExecutorService executorService) { | |
| this.enqueuer = enqueuer; | |
| this.enqueuerDependentTaskCollection = new TaskCollection<>(threadingModule, executorService); | |
| this.enqueuerIndependentTaskCollection = new TaskCollection<>(threadingModule, executorService); | |
| } | |
| public void register(EnqueuerAnalysisCollection.Builder analysesBuilder) { | |
| analysesBuilder.addFixpointAnalysis(this).addFinishedAnalysis(this); | |
| } | |
| public void submitEnqueuerDependentTask(Callable<Void> callable) { | |
| enqueuerDependentTaskCollection.submitUnchecked(callable); | |
| } | |
| public void submitEnqueuerIndependentTask(Runnable runnable) { | |
| enqueuerIndependentTaskCollection.submitUnchecked( | |
| () -> { | |
| // Conservatively acquire the app read lock. This ensures that the execution of Enqueuer | |
| // independent tasks is mutually exclusive with desugaring. | |
| enqueuer.withAppReadLock(runnable); | |
| return null; | |
| }); | |
| } | |
| public void awaitEnqueuerIndependentTasks() throws ExecutionException { | |
| enqueuerIndependentTaskCollection.await(); | |
| } | |
| /** Returns true if all Enqueuer dependent tasks were removed due to their completion. */ | |
| public boolean removeCompletedEnqueuerDependentTasks() throws ExecutionException { | |
| enqueuerDependentTaskCollection.removeCompletedFutures(); | |
| return enqueuerDependentTaskCollection.isEmpty(); | |
| } | |
| @Override | |
| public void notifyFixpoint( | |
| Enqueuer enqueuer, EnqueuerWorklist worklist, ExecutorService executorService, Timing timing) | |
| throws ExecutionException { | |
| enqueuerDependentTaskCollection.await(); | |
| enqueuerIndependentTaskCollection.removeCompletedFutures(); | |
| } | |
| @Override | |
| public void done(Enqueuer enqueuer, ExecutorService executorService) { | |
| assert enqueuerDependentTaskCollection.isEmpty(); | |
| assert enqueuerIndependentTaskCollection.isEmpty(); | |
| } | |
| public boolean verifyNoEnqueuerDependentTasks() { | |
| assert enqueuerDependentTaskCollection.isEmpty(); | |
| return true; | |
| } | |
| } |