diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4b96ec9..f963f70 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,14 +10,14 @@ permissions: contents: read env: - COMMON_BRANCH: main + COMMON_BRANCH: common COMMON_DIR: Dissolver Enhanced (Common) TARGET_DIR: Dissolver Enhanced (Target) jobs: build-common: name: Build Common - if: ${{ github.ref_name == 'main' }} + if: ${{ github.ref_name == 'common' }} runs-on: ubuntu-22.04 steps: - name: Checkout Common @@ -59,7 +59,7 @@ jobs: discover-version-branches: name: Discover Version Branches - if: ${{ github.ref_name == 'main' }} + if: ${{ github.ref_name == 'common' }} runs-on: ubuntu-22.04 outputs: branches: ${{ steps.branches.outputs.branches }} @@ -81,7 +81,7 @@ jobs: build-all-version-branches: name: Build ${{ matrix.branch }} - if: ${{ github.ref_name == 'main' && needs.discover-version-branches.outputs.branches != '[]' }} + if: ${{ github.ref_name == 'common' && needs.discover-version-branches.outputs.branches != '[]' }} needs: discover-version-branches runs-on: ubuntu-22.04 strategy: @@ -186,7 +186,7 @@ jobs: package-all-builds: name: Package All Builds - if: ${{ github.ref_name == 'main' && needs.build-common.result == 'success' && needs.build-all-version-branches.result == 'success' }} + if: ${{ github.ref_name == 'common' && needs.build-common.result == 'success' && needs.build-all-version-branches.result == 'success' }} needs: - build-common - build-all-version-branches diff --git a/src/main/java/net/exohayvan/dissolver_enhanced/data/RecipeLoadCoordinator.java b/src/main/java/net/exohayvan/dissolver_enhanced/data/RecipeLoadCoordinator.java new file mode 100644 index 0000000..03194fa --- /dev/null +++ b/src/main/java/net/exohayvan/dissolver_enhanced/data/RecipeLoadCoordinator.java @@ -0,0 +1,13 @@ +package net.exohayvan.dissolver_enhanced.data; + +public final class RecipeLoadCoordinator { + public static final RecipeLoadCoordinator GLOBAL = new RecipeLoadCoordinator(); + + private final Object monitor = new Object(); + + public void runExclusive(Runnable load) { + synchronized (monitor) { + load.run(); + } + } +} diff --git a/src/main/java/net/exohayvan/dissolver_enhanced/mixin/RecipeManagerMixin.java b/src/main/java/net/exohayvan/dissolver_enhanced/mixin/RecipeManagerMixin.java index 567f6cf..fb5a25d 100644 --- a/src/main/java/net/exohayvan/dissolver_enhanced/mixin/RecipeManagerMixin.java +++ b/src/main/java/net/exohayvan/dissolver_enhanced/mixin/RecipeManagerMixin.java @@ -31,6 +31,7 @@ import net.minecraft.util.profiler.Profiler; import net.exohayvan.dissolver_enhanced.DissolverEnhanced; import net.exohayvan.dissolver_enhanced.data.EMCValues; +import net.exohayvan.dissolver_enhanced.data.RecipeLoadCoordinator; import net.exohayvan.dissolver_enhanced.helpers.RecipeGenerator; @Mixin(RecipeManager.class) @@ -47,23 +48,21 @@ public void interceptApply(Map map, ResourceManager res @Inject(method = "apply", at = @At("HEAD")) private void applyMixin(Map map, ResourceManager resourceManager, Profiler profiler, CallbackInfo info) { - EMCValues.beginStartup(map.size()); - RECIPES.clear(); - RECIPE_SOURCES.clear(); - RECIPE_JSON.clear(); - STONE_CUTTER_LIST.clear(); + Map recipeSnapshot = new HashMap<>(map); RegistryOps registryOps = this.registryLookup.getOps(JsonOps.INSTANCE); - // let tag items load before looking through recipes - new Thread(() -> { + new Thread(() -> RecipeLoadCoordinator.GLOBAL.runExclusive(() -> { + EMCValues.beginStartup(recipeSnapshot.size()); + RECIPES.clear(); + RECIPE_SOURCES.clear(); + RECIPE_JSON.clear(); + STONE_CUTTER_LIST.clear(); wait(800); - Iterator> recipeIterator = map.entrySet().iterator(); - while (recipeIterator.hasNext()) { - Map.Entry entry = recipeIterator.next(); + for (Map.Entry entry : recipeSnapshot.entrySet()) { try { getRecipe(entry, registryOps); - }catch (Exception e) { + } catch (Exception e) { if (!getJsonRecipe(entry)) { EMCValues.incrementRecipesNotUnderstood(); } @@ -71,7 +70,7 @@ private void applyMixin(Map map, ResourceManager resour } EMCValues.recipesLoaded(RECIPES, RECIPE_SOURCES, RECIPE_JSON, STONE_CUTTER_LIST); - }).start(); + })).start(); } private static final HashMap> RECIPES = new HashMap>(); diff --git a/src/test/java/net/exohayvan/dissolver_enhanced/data/RecipeLoadCoordinatorTest.java b/src/test/java/net/exohayvan/dissolver_enhanced/data/RecipeLoadCoordinatorTest.java new file mode 100644 index 0000000..fc37590 --- /dev/null +++ b/src/test/java/net/exohayvan/dissolver_enhanced/data/RecipeLoadCoordinatorTest.java @@ -0,0 +1,42 @@ +package net.exohayvan.dissolver_enhanced.data; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import org.junit.jupiter.api.Test; + +class RecipeLoadCoordinatorTest { + @Test + void runsOverlappingLoadsExclusively() throws Exception { + RecipeLoadCoordinator coordinator = new RecipeLoadCoordinator(); + CountDownLatch firstStarted = new CountDownLatch(1); + CountDownLatch releaseFirst = new CountDownLatch(1); + CountDownLatch completed = new CountDownLatch(2); + AtomicInteger active = new AtomicInteger(); + AtomicInteger peak = new AtomicInteger(); + + Thread first = new Thread(() -> coordinator.runExclusive(() -> { + peak.accumulateAndGet(active.incrementAndGet(), Math::max); + firstStarted.countDown(); + try { releaseFirst.await(5, TimeUnit.SECONDS); } catch (InterruptedException exception) { Thread.currentThread().interrupt(); } + active.decrementAndGet(); + completed.countDown(); + })); + Thread second = new Thread(() -> coordinator.runExclusive(() -> { + peak.accumulateAndGet(active.incrementAndGet(), Math::max); + active.decrementAndGet(); + completed.countDown(); + })); + + first.start(); + assertThat(firstStarted.await(5, TimeUnit.SECONDS)).isTrue(); + second.start(); + Thread.sleep(100); + assertThat(peak.get()).isEqualTo(1); + releaseFirst.countDown(); + assertThat(completed.await(5, TimeUnit.SECONDS)).isTrue(); + assertThat(peak.get()).isEqualTo(1); + } +}