Skip to content

Commit 7c1358d

Browse files
committed
wip: add in a sync executor to pass tasks back to the gradle thread in a less brittle way
starts on fixing #39
1 parent 2ac043d commit 7c1358d

File tree

10 files changed

+104
-29
lines changed

10 files changed

+104
-29
lines changed

subprojects/gradle-plugin/src/main/java/org/spongepowered/gradle/vanilla/internal/bundler/BundleElement.java

-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
package org.spongepowered.gradle.vanilla.internal.bundler;
2626

2727
import org.immutables.value.Value;
28-
import org.spongepowered.gradle.vanilla.internal.model.GroupArtifactVersion;
2928

3029
/**
3130
* A single entry in a bundle.

subprojects/gradle-plugin/src/main/java/org/spongepowered/gradle/vanilla/internal/bundler/BundlerMetadata.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,12 @@ public abstract class BundlerMetadata {
5959
/**
6060
* Attempt to read bundler metadata from a jar.
6161
*
62-
* <p>If the jar is not a Minecraft bundler jar, an empty {@link Optional} will be returned.</p>
62+
* <p>If the jar is not a Minecraft bundler jar, an empty {@link Optional} will
63+
* be returned.</p>
6364
*
6465
* @param jar the jar to read
6566
* @return parsed metadata
67+
* @throws IOException if an error occurs while trying to read from the jar
6668
*/
6769
public static Optional<BundlerMetadata> read(final Path jar) throws IOException {
6870
try (final JarFile file = new JarFile(jar)) {

subprojects/gradle-plugin/src/main/java/org/spongepowered/gradle/vanilla/internal/repository/LauncherMetaMetadataSupplierAndArtifactProducer.java

+5-2
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import org.spongepowered.gradle.vanilla.resolver.ResolutionResult;
3939

4040
import java.util.Optional;
41+
import java.util.concurrent.CompletableFuture;
4142
import java.util.concurrent.ExecutionException;
4243

4344
import javax.inject.Inject;
@@ -74,8 +75,10 @@ public void execute(final ComponentMetadataSupplierDetails details) {
7475
final MinecraftResolver resolver = providerService.resolver();
7576
// Request the appropriate jar, block until it's provided
7677
// TODO: maybe validate that the state keys of the provided modifiers actually match the artifact ID?
77-
final ResolutionResult<MinecraftResolver.MinecraftEnvironment>
78-
resolution = resolver.provide(platform.get(), version, providerService.peekModifiers()).get();
78+
final CompletableFuture<ResolutionResult<MinecraftResolver.MinecraftEnvironment>> resolutionFuture = resolver
79+
.provide(platform.get(), version, providerService.peekModifiers());
80+
81+
final ResolutionResult<MinecraftResolver.MinecraftEnvironment> resolution = resolver.processSyncTasksUntilComplete(resolutionFuture);
7982
if (!resolution.isPresent()) {
8083
return;
8184
}

subprojects/gradle-plugin/src/main/java/org/spongepowered/gradle/vanilla/internal/repository/MinecraftRepositoryPlugin.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@
4242
import org.gradle.api.plugins.ExtensionAware;
4343
import org.gradle.api.provider.Provider;
4444
import org.gradle.build.event.BuildEventsListenerRegistry;
45-
import org.spongepowered.gradle.vanilla.internal.Constants;
4645
import org.spongepowered.gradle.vanilla.MinecraftExtension;
46+
import org.spongepowered.gradle.vanilla.internal.Constants;
4747
import org.spongepowered.gradle.vanilla.internal.MinecraftExtensionImpl;
4848
import org.spongepowered.gradle.vanilla.internal.model.VersionClassifier;
4949
import org.spongepowered.gradle.vanilla.internal.repository.modifier.ArtifactModifier;
@@ -184,7 +184,7 @@ private void configureResolutionStrategy(
184184
// If we do have a version, try to resolve that fixed version
185185
if (version != null) {
186186
try {
187-
resolver.provide(platform.get(), version, providerService.peekModifiers()).get();
187+
resolver.processSyncTasksUntilComplete(resolver.provide(platform.get(), version, providerService.peekModifiers()));
188188
} catch (final InterruptedException ex) {
189189
Thread.currentThread().interrupt();
190190
} catch (final ExecutionException ex) {

subprojects/gradle-plugin/src/main/java/org/spongepowered/gradle/vanilla/internal/transformer/AtlasTransformers.java

-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
import org.cadixdev.bombe.asm.jar.JarEntryRemappingTransformer;
2929
import org.cadixdev.bombe.jar.JarEntryTransformer;
3030
import org.cadixdev.lorenz.MappingSet;
31-
import org.cadixdev.lorenz.asm.LorenzRemapper;
3231
import org.spongepowered.gradle.vanilla.internal.asm.EnhancedClassRemapper;
3332
import org.spongepowered.gradle.vanilla.internal.asm.EnhancedRemapper;
3433
import org.spongepowered.gradle.vanilla.internal.asm.LocalVariableNamingClassVisitor;

subprojects/gradle-plugin/src/main/java/org/spongepowered/gradle/vanilla/repository/MinecraftResolver.java

+32-8
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,17 @@
2727
import org.spongepowered.gradle.vanilla.internal.model.GroupArtifactVersion;
2828
import org.spongepowered.gradle.vanilla.internal.model.VersionDescriptor;
2929
import org.spongepowered.gradle.vanilla.internal.model.VersionManifestRepository;
30-
import org.spongepowered.gradle.vanilla.resolver.Downloader;
3130
import org.spongepowered.gradle.vanilla.internal.repository.ResolvableTool;
3231
import org.spongepowered.gradle.vanilla.internal.repository.modifier.ArtifactModifier;
3332
import org.spongepowered.gradle.vanilla.internal.repository.modifier.AssociatedResolutionFlags;
33+
import org.spongepowered.gradle.vanilla.resolver.Downloader;
3434
import org.spongepowered.gradle.vanilla.resolver.ResolutionResult;
3535

3636
import java.net.URLClassLoader;
3737
import java.nio.file.Path;
3838
import java.util.Set;
3939
import java.util.concurrent.CompletableFuture;
40+
import java.util.concurrent.ExecutionException;
4041
import java.util.concurrent.Executor;
4142
import java.util.function.BiConsumer;
4243
import java.util.function.Supplier;
@@ -96,7 +97,21 @@ public interface MinecraftResolver {
9697
* environment and a target path
9798
* @return a future returning the result of resolving a jar path
9899
*/
99-
CompletableFuture<ResolutionResult<Path>> produceAssociatedArtifactSync(final MinecraftPlatform side, final String version, final Set<ArtifactModifier> modifiers, final String id, final Set<AssociatedResolutionFlags> flags, final BiConsumer<MinecraftEnvironment, Path> action);
100+
CompletableFuture<ResolutionResult<Path>> produceAssociatedArtifactSync(
101+
final MinecraftPlatform side, final String version, final Set<ArtifactModifier> modifiers, final String id,
102+
final Set<AssociatedResolutionFlags> flags, final BiConsumer<MinecraftEnvironment, Path> action
103+
);
104+
105+
/**
106+
* Block on the completion of a provided future, processing "sync" tasks while
107+
* that occurs.
108+
*
109+
* @param <T> the return value type
110+
* @param future the future to await
111+
* @return the result of the future
112+
* @throws ExecutionException if the task execution fails
113+
*/
114+
<T> T processSyncTasksUntilComplete(CompletableFuture<T> future) throws ExecutionException, InterruptedException;
100115

101116
interface MinecraftEnvironment {
102117

@@ -162,13 +177,22 @@ interface Context {
162177
Executor executor();
163178

164179
/**
165-
* Return a child classloader with a tool and its dependencies on the
166-
* classpath, as well as the VanillaGradle jar.
180+
* An executor for performing main-thread synchronous operations, like some
181+
* dependency resolution.
182+
*
183+
* @return the synchronous executor
184+
*/
185+
Executor syncExecutor();
186+
187+
/**
188+
* Return a child classloader with a tool and its dependencies on the classpath,
189+
* as well as the VanillaGradle jar.
167190
*
168-
* <p>This is a very fragile arrangement but it allows some dependencies
169-
* to be overridden at runtime. Classes from Gradle, VanillaGradle's
170-
* dependencies, and the JDK can be safely shared, but VanillaGradle
171-
* classes CAN NOT.</p>
191+
* <p>This is a very fragile arrangement but it allows some dependencies to be
192+
* overridden at runtime. Classes from Gradle, VanillaGradle's dependencies, and
193+
* the JDK can be safely shared, but VanillaGradle classes CAN NOT.</p>
194+
*
195+
* <p>This must be run on the {@link #syncExecutor()}.</p>
172196
*
173197
* @param tool the tool to resolve
174198
* @return a class loader with the tool on the classpath

subprojects/gradle-plugin/src/main/java/org/spongepowered/gradle/vanilla/repository/MinecraftResolverImpl.java

+54-4
Original file line numberDiff line numberDiff line change
@@ -42,17 +42,17 @@
4242
import org.spongepowered.gradle.vanilla.internal.model.GroupArtifactVersion;
4343
import org.spongepowered.gradle.vanilla.internal.model.VersionDescriptor;
4444
import org.spongepowered.gradle.vanilla.internal.model.VersionManifestRepository;
45-
import org.spongepowered.gradle.vanilla.internal.util.FunctionalUtils;
46-
import org.spongepowered.gradle.vanilla.resolver.Downloader;
47-
import org.spongepowered.gradle.vanilla.resolver.HashAlgorithm;
4845
import org.spongepowered.gradle.vanilla.internal.repository.IvyModuleWriter;
4946
import org.spongepowered.gradle.vanilla.internal.repository.ResolvableTool;
5047
import org.spongepowered.gradle.vanilla.internal.repository.modifier.ArtifactModifier;
5148
import org.spongepowered.gradle.vanilla.internal.repository.modifier.AssociatedResolutionFlags;
52-
import org.spongepowered.gradle.vanilla.internal.transformer.AtlasTransformers;
5349
import org.spongepowered.gradle.vanilla.internal.resolver.AsyncUtils;
5450
import org.spongepowered.gradle.vanilla.internal.resolver.FileUtils;
51+
import org.spongepowered.gradle.vanilla.internal.transformer.AtlasTransformers;
52+
import org.spongepowered.gradle.vanilla.internal.util.FunctionalUtils;
5553
import org.spongepowered.gradle.vanilla.internal.util.SelfPreferringClassLoader;
54+
import org.spongepowered.gradle.vanilla.resolver.Downloader;
55+
import org.spongepowered.gradle.vanilla.resolver.HashAlgorithm;
5656
import org.spongepowered.gradle.vanilla.resolver.ResolutionResult;
5757

5858
import java.io.IOException;
@@ -71,13 +71,15 @@
7171
import java.util.HashSet;
7272
import java.util.List;
7373
import java.util.Set;
74+
import java.util.concurrent.BlockingQueue;
7475
import java.util.concurrent.CompletableFuture;
7576
import java.util.concurrent.CompletionException;
7677
import java.util.concurrent.ConcurrentHashMap;
7778
import java.util.concurrent.ConcurrentMap;
7879
import java.util.concurrent.ExecutionException;
7980
import java.util.concurrent.Executor;
8081
import java.util.concurrent.ExecutorService;
82+
import java.util.concurrent.SynchronousQueue;
8183
import java.util.function.BiConsumer;
8284
import java.util.function.Function;
8385
import java.util.function.Supplier;
@@ -95,6 +97,8 @@ public class MinecraftResolverImpl implements MinecraftResolver, MinecraftResolv
9597
private final ConcurrentMap<EnvironmentKey, CompletableFuture<ResolutionResult<MinecraftEnvironment>>> artifacts = new ConcurrentHashMap<>();
9698
private final ConcurrentMap<EnvironmentKey, CompletableFuture<ResolutionResult<Path>>> associatedArtifacts = new ConcurrentHashMap<>();
9799
private final boolean forceRefresh;
100+
private final BlockingQueue<Runnable> syncTasks = new SynchronousQueue<>();
101+
private final Executor syncExecutor = run -> this.syncTasks.add(run);
98102

99103
public MinecraftResolverImpl(
100104
final VersionManifestRepository manifests,
@@ -127,6 +131,11 @@ public Executor executor() {
127131
return this.executor;
128132
}
129133

134+
@Override
135+
public Executor syncExecutor() {
136+
return this.syncExecutor;
137+
}
138+
130139
@Override
131140
public Supplier<URLClassLoader> classLoaderWithTool(final ResolvableTool tool) {
132141
final @Nullable Function<ResolvableTool, URL[]> toolResolver = this.toolResolver;
@@ -520,6 +529,32 @@ public CompletableFuture<ResolutionResult<Path>> produceAssociatedArtifactSync(
520529
return ourResult;
521530
}
522531

532+
@Override
533+
public <T> T processSyncTasksUntilComplete(CompletableFuture<T> future) throws InterruptedException, ExecutionException {
534+
future.handle((res, err) -> {
535+
this.syncTasks.add(new CompleteEvaluation(future));
536+
return res;
537+
});
538+
539+
Runnable action;
540+
for (;;) {
541+
action = this.syncTasks.take();
542+
543+
// todo: rethrow exceptions with an ExecutionException
544+
if (action instanceof CompleteEvaluation && ((CompleteEvaluation) action).completed == future) {
545+
break;
546+
}
547+
548+
try {
549+
action.run();
550+
} catch (final Exception ex) {
551+
MinecraftResolverImpl.LOGGER.error("Failed to execute synchronous task {} while resolving {}", action, future, ex);
552+
}
553+
}
554+
555+
return future.get();
556+
}
557+
523558
private String sharedArtifactFileName(
524559
final String artifactId,
525560
final String version,
@@ -668,4 +703,19 @@ public VersionDescriptor.Full metadata() {
668703

669704
}
670705

706+
static final class CompleteEvaluation implements Runnable {
707+
708+
final CompletableFuture<?> completed;
709+
710+
CompleteEvaluation(final CompletableFuture<?> completed) {
711+
this.completed = completed;
712+
}
713+
714+
@Override
715+
public void run() {
716+
// no-op
717+
}
718+
719+
}
720+
671721
}

subprojects/gradle-plugin/src/main/java/org/spongepowered/gradle/vanilla/repository/MinecraftSide.java

-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626

2727
import org.checkerframework.checker.nullness.qual.Nullable;
2828
import org.spongepowered.gradle.vanilla.internal.Constants;
29-
import org.spongepowered.gradle.vanilla.internal.bundler.BundleElement;
3029
import org.spongepowered.gradle.vanilla.internal.bundler.BundlerMetadata;
3130
import org.spongepowered.gradle.vanilla.internal.model.DownloadClassifier;
3231
import org.spongepowered.gradle.vanilla.internal.model.GroupArtifactVersion;

subprojects/gradle-plugin/src/main/java/org/spongepowered/gradle/vanilla/task/DecompileJarTask.java

+8-7
Original file line numberDiff line numberDiff line change
@@ -43,15 +43,15 @@
4343
import org.gradle.internal.component.external.model.ModuleComponentArtifactIdentifier;
4444
import org.gradle.jvm.toolchain.JavaLauncher;
4545
import org.gradle.workers.WorkerExecutor;
46-
import org.spongepowered.gradle.vanilla.internal.Constants;
4746
import org.spongepowered.gradle.vanilla.MinecraftExtension;
47+
import org.spongepowered.gradle.vanilla.internal.Constants;
4848
import org.spongepowered.gradle.vanilla.internal.MinecraftExtensionImpl;
49-
import org.spongepowered.gradle.vanilla.repository.MinecraftPlatform;
5049
import org.spongepowered.gradle.vanilla.internal.repository.MinecraftProviderService;
51-
import org.spongepowered.gradle.vanilla.resolver.ResolutionResult;
5250
import org.spongepowered.gradle.vanilla.internal.repository.modifier.ArtifactModifier;
5351
import org.spongepowered.gradle.vanilla.internal.repository.modifier.AssociatedResolutionFlags;
5452
import org.spongepowered.gradle.vanilla.internal.worker.JarDecompileWorker;
53+
import org.spongepowered.gradle.vanilla.repository.MinecraftPlatform;
54+
import org.spongepowered.gradle.vanilla.resolver.ResolutionResult;
5555

5656
import java.io.File;
5757
import java.lang.management.ManagementFactory;
@@ -185,19 +185,20 @@ public void execute() {
185185
this.getWorkerExecutor().await();
186186
}
187187
);
188-
} finally {
189-
DecompileJarTask.DECOMPILE_LOCK.unlock();
190-
}
191188

192189
try {
193-
final ResolutionResult<Path> result = resultFuture.get();
190+
final ResolutionResult<Path> result = minecraftProvider.resolver().processSyncTasksUntilComplete(resultFuture);
194191
this.setDidWork(!result.upToDate());
195192
} catch (final ExecutionException ex) {
196193
throw new GradleException("Failed to decompile " + this.getMinecraftVersion().get(), ex.getCause());
197194
} catch (final InterruptedException ex) {
198195
Thread.currentThread().interrupt();
199196
throw new GradleException("Interrupted");
200197
}
198+
199+
} finally {
200+
DecompileJarTask.DECOMPILE_LOCK.unlock();
201+
}
201202
}
202203

203204
}

subprojects/gradle-plugin/src/main/java/org/spongepowered/gradle/vanilla/task/GenEclipseRuns.java

-2
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
package org.spongepowered.gradle.vanilla.task;
2626

2727
import org.gradle.api.DefaultTask;
28-
import org.gradle.api.InvalidUserDataException;
2928
import org.gradle.api.file.DirectoryProperty;
3029
import org.gradle.api.provider.Property;
3130
import org.gradle.api.provider.SetProperty;
@@ -37,7 +36,6 @@
3736
import org.spongepowered.gradle.vanilla.internal.runs.EclipseRunConfigurationWriter;
3837
import org.spongepowered.gradle.vanilla.runs.RunConfiguration;
3938

40-
import java.io.File;
4139
import java.io.IOException;
4240
import java.nio.file.Files;
4341
import java.nio.file.Path;

0 commit comments

Comments
 (0)