From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: wangxyper Date: Thu, 19 Jan 2023 10:03:10 +0800 Subject: [PATCH] Hearse: Add locks when calling getChunk in worker thread Original license: MIT Original project: https://github.com/Era4FunMC/Hearse diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java index e311724d2e723115bc9549a61e6206a8aed835d8..b151339cc56418f82f9b75b39b8fc6ab6686993e 100644 --- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java +++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java @@ -1,5 +1,6 @@ package net.minecraft.server.level; +import co.earthme.hearse.concurrent.thread.Worker; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.Lists; import com.mojang.datafixers.DataFixer; @@ -377,57 +378,68 @@ public class ServerChunkCache extends ChunkSource { } // Paper end - async chunk io + private final Object workerGetChunkLock = new Object(); + @Nullable @Override public ChunkAccess getChunk(int x, int z, ChunkStatus leastStatus, boolean create) { - final int x1 = x; final int z1 = z; // Paper - conflict on variable change if (!io.papermc.paper.util.TickThread.isTickThread()) { // Paper - rewrite chunk system return (ChunkAccess) CompletableFuture.supplyAsync(() -> { return this.getChunk(x, z, leastStatus, create); }, this.mainThreadProcessor).join(); } else { - // Paper start - optimise for loaded chunks - LevelChunk ifLoaded = this.getChunkAtIfLoadedMainThread(x, z); - if (ifLoaded != null) { - return ifLoaded; + if (Thread.currentThread() instanceof Worker){ + synchronized (this.workerGetChunkLock){ + return this.getChunkUnsafe(x,z,leastStatus,create); + } } - // Paper end - //ProfilerFiller gameprofilerfiller = this.level.getProfiler(); // Purpur + return this.getChunkUnsafe(x,z,leastStatus,create); + } + } - //gameprofilerfiller.incrementCounter("getChunk"); // Purpur - long k = ChunkPos.asLong(x, z); + private ChunkAccess getChunkUnsafe(int x,int z,ChunkStatus leastStatus,boolean create){ + final int x1 = x; final int z1 = z; // Paper - conflict on variable change + // Paper start - optimise for loaded chunks + LevelChunk ifLoaded = this.getChunkAtIfLoadedMainThread(x, z); + if (ifLoaded != null) { + return ifLoaded; + } + // Paper end + //ProfilerFiller gameprofilerfiller = this.level.getProfiler(); // Purpur - ChunkAccess ichunkaccess; + //gameprofilerfiller.incrementCounter("getChunk"); // Purpur + long k = ChunkPos.asLong(x, z); - // Paper - rewrite chunk system - there are no correct callbacks to remove items from cache in the new chunk system + ChunkAccess ichunkaccess; - //gameprofilerfiller.incrementCounter("getChunkCacheMiss"); // Purpur - CompletableFuture> completablefuture = this.getChunkFutureMainThread(x, z, leastStatus, create, true); // Paper - ServerChunkCache.MainThreadExecutor chunkproviderserver_b = this.mainThreadProcessor; + // Paper - rewrite chunk system - there are no correct callbacks to remove items from cache in the new chunk system - Objects.requireNonNull(completablefuture); - if (!completablefuture.isDone()) { // Paper - // Paper start - async chunk io/loading - io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler.pushChunkWait(this.level, x1, z1); // Paper - rewrite chunk system - // Paper end - com.destroystokyo.paper.io.SyncLoadFinder.logSyncLoad(this.level, x1, z1); // Paper - sync load info - //this.level.timings.syncChunkLoad.startTiming(); // Paper // Purpur - chunkproviderserver_b.managedBlock(completablefuture::isDone); - io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler.popChunkWait(); // Paper - async chunk debug // Paper - rewrite chunk system - //this.level.timings.syncChunkLoad.stopTiming(); // Paper // Purpur - } // Paper - ichunkaccess = (ChunkAccess) ((Either) completablefuture.join()).map((ichunkaccess1) -> { - return ichunkaccess1; - }, (playerchunk_failure) -> { - if (create) { - throw (IllegalStateException) Util.pauseInIde(new IllegalStateException("Chunk not there when requested: " + playerchunk_failure)); - } else { - return null; - } - }); - this.storeInCache(k, ichunkaccess, leastStatus); - return ichunkaccess; - } + //gameprofilerfiller.incrementCounter("getChunkCacheMiss"); // Purpur + CompletableFuture> completablefuture = this.getChunkFutureMainThread(x, z, leastStatus, create, true); // Paper + ServerChunkCache.MainThreadExecutor chunkproviderserver_b = this.mainThreadProcessor; + + Objects.requireNonNull(completablefuture); + if (!completablefuture.isDone()) { // Paper + // Paper start - async chunk io/loading + io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler.pushChunkWait(this.level, x1, z1); // Paper - rewrite chunk system + // Paper end + com.destroystokyo.paper.io.SyncLoadFinder.logSyncLoad(this.level, x1, z1); // Paper - sync load info + //this.level.timings.syncChunkLoad.startTiming(); // Paper // Purpur + chunkproviderserver_b.managedBlock(completablefuture::isDone); + io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler.popChunkWait(); // Paper - async chunk debug // Paper - rewrite chunk system + //this.level.timings.syncChunkLoad.stopTiming(); // Paper // Purpur + } // Paper + ichunkaccess = (ChunkAccess) ((Either) completablefuture.join()).map((ichunkaccess1) -> { + return ichunkaccess1; + }, (playerchunk_failure) -> { + if (create) { + throw (IllegalStateException) Util.pauseInIde(new IllegalStateException("Chunk not there when requested: " + playerchunk_failure)); + } else { + return null; + } + }); + this.storeInCache(k, ichunkaccess, leastStatus); + return ichunkaccess; } @Nullable @@ -440,6 +452,7 @@ public class ServerChunkCache extends ChunkSource { } } + private void clearCache() { Arrays.fill(this.lastChunkPos, ChunkPos.INVALID_CHUNK_POS); Arrays.fill(this.lastChunkStatus, (Object) null);