-
-
Notifications
You must be signed in to change notification settings - Fork 84
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Use Worker API to download files in parallel
Refers to #138
- Loading branch information
1 parent
add4f2a
commit 0f07235
Showing
16 changed files
with
317 additions
and
52 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
79 changes: 79 additions & 0 deletions
79
src/main/java/de/undercouch/gradle/tasks/download/internal/DefaultWorkerExecutorHelper.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
package de.undercouch.gradle.tasks.download.internal; | ||
|
||
import org.gradle.api.UncheckedIOException; | ||
import org.gradle.api.provider.Property; | ||
import org.gradle.workers.WorkAction; | ||
import org.gradle.workers.WorkParameters; | ||
import org.gradle.workers.WorkQueue; | ||
import org.gradle.workers.WorkerExecutor; | ||
|
||
import javax.inject.Inject; | ||
import java.io.IOException; | ||
import java.util.Map; | ||
import java.util.concurrent.ConcurrentHashMap; | ||
import java.util.concurrent.atomic.AtomicInteger; | ||
|
||
/** | ||
* Default implementation of {@link WorkerExecutorHelper} that executes | ||
* {@link Job}s asynchronously using the Gradle Worker API | ||
* @author Michel Kraemer | ||
*/ | ||
@SuppressWarnings("UnstableApiUsage") | ||
public class DefaultWorkerExecutorHelper extends WorkerExecutorHelper { | ||
/** | ||
* A unique ID for jobs. Used to access jobs in {@link #jobs} | ||
*/ | ||
private static final AtomicInteger UNIQUE_ID = new AtomicInteger(); | ||
|
||
/** | ||
* A maps of jobs submitted to this executor | ||
*/ | ||
private static final Map<Integer, Job> jobs = new ConcurrentHashMap<>(); | ||
|
||
private final WorkerExecutor workerExecutor; | ||
private final WorkQueue workQueue; | ||
|
||
/** | ||
* Constructs a new executor | ||
* @param workerExecutor the Gradle Worker API executor | ||
*/ | ||
@Inject | ||
public DefaultWorkerExecutorHelper(WorkerExecutor workerExecutor) { | ||
this.workerExecutor = workerExecutor; | ||
this.workQueue = workerExecutor.noIsolation(); | ||
} | ||
|
||
@Override | ||
public void submit(Job job) { | ||
int id = UNIQUE_ID.getAndIncrement(); | ||
jobs.put(id, job); | ||
workQueue.submit(DefaultWorkAction.class, parameters -> | ||
parameters.getID().set(id)); | ||
} | ||
|
||
@Override | ||
public void await() { | ||
workerExecutor.await(); | ||
} | ||
|
||
@Override | ||
public boolean needsAwait() { | ||
return false; | ||
} | ||
|
||
public interface DefaultWorkParameters extends WorkParameters { | ||
Property<Integer> getID(); | ||
} | ||
|
||
public static abstract class DefaultWorkAction implements WorkAction<DefaultWorkParameters> { | ||
@Override | ||
public void execute() { | ||
Job job = jobs.remove(getParameters().getID().get()); | ||
try { | ||
job.run(); | ||
} catch (IOException e) { | ||
throw new UncheckedIOException(e); | ||
} | ||
} | ||
} | ||
} |
15 changes: 15 additions & 0 deletions
15
src/main/java/de/undercouch/gradle/tasks/download/internal/Job.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package de.undercouch.gradle.tasks.download.internal; | ||
|
||
import java.io.IOException; | ||
|
||
/** | ||
* An asynchronous job executed by a {@link WorkerExecutorHelper} | ||
* @author Michel Kraemer | ||
*/ | ||
public interface Job { | ||
/** | ||
* Execute the job | ||
* @throws IOException if the job failed | ||
*/ | ||
void run() throws IOException; | ||
} |
51 changes: 51 additions & 0 deletions
51
src/main/java/de/undercouch/gradle/tasks/download/internal/LegacyWorkerExecutorHelper.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
package de.undercouch.gradle.tasks.download.internal; | ||
|
||
import java.io.IOException; | ||
import java.util.Queue; | ||
import java.util.concurrent.CompletableFuture; | ||
import java.util.concurrent.ConcurrentLinkedQueue; | ||
import java.util.concurrent.ExecutorService; | ||
import java.util.concurrent.Executors; | ||
import java.util.concurrent.Future; | ||
|
||
/** | ||
* Executes jobs asynchronously with an {@link ExecutorService} on Gradle | ||
* versions where the Worker API is not available | ||
* @author Michel Kraemer | ||
*/ | ||
public class LegacyWorkerExecutorHelper extends WorkerExecutorHelper { | ||
private final ExecutorService executorService = Executors.newWorkStealingPool(); | ||
private final Queue<Future<Void>> futures = new ConcurrentLinkedQueue<>(); | ||
|
||
@Override | ||
public void submit(Job job) { | ||
CompletableFuture<Void> f = new CompletableFuture<>(); | ||
futures.add(f); | ||
executorService.submit(() -> { | ||
try { | ||
job.run(); | ||
f.complete(null); | ||
futures.remove(f); | ||
} catch (IOException e) { | ||
f.completeExceptionally(e); | ||
} | ||
}); | ||
} | ||
|
||
@Override | ||
public void await() { | ||
Future<Void> f; | ||
while ((f = futures.poll()) != null) { | ||
try { | ||
f.get(); | ||
} catch (Exception e) { | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
} | ||
|
||
@Override | ||
public boolean needsAwait() { | ||
return true; | ||
} | ||
} |
45 changes: 45 additions & 0 deletions
45
src/main/java/de/undercouch/gradle/tasks/download/internal/WorkerExecutorHelper.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
package de.undercouch.gradle.tasks.download.internal; | ||
|
||
import org.gradle.api.model.ObjectFactory; | ||
import org.gradle.util.GradleVersion; | ||
|
||
/** | ||
* Executes jobs asynchronously. Either uses the Gradle Worker API (if | ||
* available) or falls back to a legacy implementation using an | ||
* {@link java.util.concurrent.ExecutorService}. | ||
* @author Michel Kraemer | ||
*/ | ||
public abstract class WorkerExecutorHelper { | ||
/** | ||
* Creates a new instance of the {@link WorkerExecutorHelper} depending | ||
* on the Gradle version | ||
* @param objectFactory creates Gradle model objects | ||
* @return the helper | ||
*/ | ||
public static WorkerExecutorHelper newInstance(ObjectFactory objectFactory) { | ||
if (GradleVersion.current().getBaseVersion().compareTo(GradleVersion.version("5.6")) >= 0) { | ||
return objectFactory.newInstance(DefaultWorkerExecutorHelper.class); | ||
} | ||
return new LegacyWorkerExecutorHelper(); | ||
} | ||
|
||
/** | ||
* Execute a job asynchronously | ||
* @param job the job to execute | ||
*/ | ||
public abstract void submit(Job job); | ||
|
||
/** | ||
* Wait for all jobs of the current build operation to complete | ||
*/ | ||
public abstract void await(); | ||
|
||
/** | ||
* Returns {@code true} if {@link #await()} MUST be called at the end of | ||
* the task. This mostly applies to Gradle versions that don't have a | ||
* Worker API and therefore cannot let the task continue to run in parallel | ||
* to others. | ||
* @return {@code true} if {@link #await()} must be called | ||
*/ | ||
public abstract boolean needsAwait(); | ||
} |
Oops, something went wrong.