Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions gradle/test.libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ java-debug = "0.53.1"
mixin = "0.15.3+mixin.0.8.7"
bouncycastle = "1.81"

gradle-latest = "9.0.0"
gradle-nightly = "9.2.0-20250902075334+0000"
gradle-latest = "9.1.0"
gradle-nightly = "9.3.0-20250923005153+0000"
fabric-loader = "0.16.14"

[libraries]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Duration;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -80,6 +81,8 @@
import net.fabricmc.loom.util.service.ServiceFactory;

public abstract class CompileConfiguration implements Runnable {
private static final String LOCK_PROPERTY_KEY = "fabric.loom.internal.global.lock";

@Inject
protected abstract Project getProject();

Expand Down Expand Up @@ -110,7 +113,11 @@ public void run() {
}

try {
setupMinecraft(configContext);
// Setting up loom across Gradle projects is not thread safe, synchronize it here to ensure that multiple projects cannot use it.
// There is no easy way around this, as we want to use the same global cache for downloaded or generated files.
synchronized (getGlobalLockObject()) {
setupMinecraft(configContext);
}

LoomDependencyManager dependencyManager = new LoomDependencyManager();
extension.setDependencyManager(dependencyManager);
Expand Down Expand Up @@ -151,8 +158,7 @@ public void run() {
}
}

// This is not thread safe across getProject()s synchronize it here just to be sure, might be possible to move this further down, but for now this will do.
private synchronized void setupMinecraft(ConfigContext configContext) throws Exception {
private void setupMinecraft(ConfigContext configContext) throws Exception {
final Project project = configContext.project();
final LoomGradleExtension extension = configContext.extension();

Expand Down Expand Up @@ -457,4 +463,18 @@ private void afterEvaluationWithService(Consumer<ServiceFactory> consumer) {
}
});
}

// This is a nasty piece of work, but seems to work quite nicely.
// We need a lock that works across classloaders, a regular synchronized method will not work here.
// We can abuse system properties as a shared object store that we know for sure will be on the same classloader regardless of what Gradle does to loom.
// This allows us to ensure that all instances of loom regardless of classloader get the same object to lock on.
private static Object getGlobalLockObject() {
if (!System.getProperties().contains(LOCK_PROPERTY_KEY)) {
// The .intern resolves a possible race where two difference value objects (remember not the same classloader) are set.
//noinspection StringOperationCanBeSimplified
System.getProperties().setProperty(LOCK_PROPERTY_KEY, LOCK_PROPERTY_KEY.intern());
}

return Objects.requireNonNull(System.getProperty(LOCK_PROPERTY_KEY));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import spock.lang.Unroll

import net.fabricmc.loom.test.util.GradleProjectTestTrait

import static net.fabricmc.loom.test.LoomTestConstants.PRE_RELEASE_GRADLE
import static net.fabricmc.loom.test.LoomTestConstants.STANDARD_TEST_VERSIONS
import static org.gradle.testkit.runner.TaskOutcome.SUCCESS

Expand Down Expand Up @@ -89,7 +90,7 @@ class MultiMcVersionTest extends Specification implements GradleProjectTestTrait
// See: https://github.com/gradle/gradle/issues/30401
// By default parallel configuration of all projects is preferred.
args: [
"-Dorg.gradle.internal.isolated-projects.configure-on-demand.tasks=true"
"-Dorg.gradle.internal.isolated-projects.configure-on-demand=true"
])

then:
Expand All @@ -98,6 +99,6 @@ class MultiMcVersionTest extends Specification implements GradleProjectTestTrait
result.output.count("Fabric Loom:") == 1

where:
version << STANDARD_TEST_VERSIONS
version << [PRE_RELEASE_GRADLE]
}
}