From f3712d3b8dd556087b786a8918904792be231e16 Mon Sep 17 00:00:00 2001 From: Jason Mitchell Date: Mon, 4 Mar 2024 02:20:48 -0800 Subject: Maybe thread safe (#2523) * Maybe thread safe * While we're at it, let's reduce allocations by a few orders of magnitude. ~1gb -> 1mb in testing for similar time periods. * use return value of add() again * That's apparently not available on the server side * Spotless apply for branch maybe-thread-safe for #2523 (#2524) spotlessApply --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .../api/threads/GT_Runnable_Cable_Update.java | 40 ++++---- .../threads/GT_Runnable_MachineBlockUpdate.java | 101 ++++++++++++++------- 2 files changed, 90 insertions(+), 51 deletions(-) (limited to 'src/main/java/gregtech/api/threads') diff --git a/src/main/java/gregtech/api/threads/GT_Runnable_Cable_Update.java b/src/main/java/gregtech/api/threads/GT_Runnable_Cable_Update.java index 31c3c56aa8..4375d21e40 100644 --- a/src/main/java/gregtech/api/threads/GT_Runnable_Cable_Update.java +++ b/src/main/java/gregtech/api/threads/GT_Runnable_Cable_Update.java @@ -1,7 +1,6 @@ package gregtech.api.threads; import net.minecraft.tileentity.TileEntity; -import net.minecraft.util.ChunkCoordinates; import net.minecraft.world.World; import net.minecraftforge.common.util.ForgeDirection; @@ -13,29 +12,34 @@ import gregtech.common.GT_Proxy; public class GT_Runnable_Cable_Update extends GT_Runnable_MachineBlockUpdate { - protected GT_Runnable_Cable_Update(World aWorld, ChunkCoordinates aCoords) { - super(aWorld, aCoords); + protected GT_Runnable_Cable_Update(World aWorld, int posX, int posY, int posZ) { + super(aWorld, posX, posY, posZ); } - public static void setCableUpdateValues(World aWorld, ChunkCoordinates aCoords) { + public static void setCableUpdateValues(World aWorld, int posX, int posY, int posZ) { if (isEnabled) { - EXECUTOR_SERVICE.submit(new GT_Runnable_Cable_Update(aWorld, aCoords)); + EXECUTOR_SERVICE.submit(new GT_Runnable_Cable_Update(aWorld, posX, posY, posZ)); } } @Override public void run() { + int posX, posY, posZ; try { while (!tQueue.isEmpty()) { - final ChunkCoordinates aCoords = tQueue.poll(); + final long packedCoords = tQueue.dequeueLong(); + posX = unpackLongX(packedCoords); + posY = unpackLongY(packedCoords); + posZ = unpackLongZ(packedCoords); + final TileEntity tTileEntity; GT_Proxy.TICK_LOCK.lock(); try { // we dont want to go over cables that are in unloaded chunks // keeping the lock just to make sure no CME happens - if (world.blockExists(aCoords.posX, aCoords.posY, aCoords.posZ)) { - tTileEntity = world.getTileEntity(aCoords.posX, aCoords.posY, aCoords.posZ); + if (world.blockExists(posX, posY, posZ)) { + tTileEntity = world.getTileEntity(posX, posY, posZ); } else { tTileEntity = null; } @@ -51,22 +55,24 @@ public class GT_Runnable_Cable_Update extends GT_Runnable_MachineBlockUpdate { // only add blocks the cable is connected to if (tTileEntity instanceof BaseMetaPipeEntity metaPipe && metaPipe.getMetaTileEntity() instanceof GT_MetaPipeEntity_Cable cable) { - ChunkCoordinates tCoords; - for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) { + for (int i = 0; i < ForgeDirection.VALID_DIRECTIONS.length; i++) { + final ForgeDirection side = ForgeDirection.VALID_DIRECTIONS[i]; if (cable.isConnectedAtSide(side)) { - if (visited.add( - tCoords = new ChunkCoordinates( - aCoords.posX + side.offsetX, - aCoords.posY + side.offsetY, - aCoords.posZ + side.offsetZ))) - tQueue.add(tCoords); + final long tCoords = asLong(posX + side.offsetX, posY + side.offsetY, posZ + side.offsetZ); + if (visited.add(tCoords)) { + tQueue.enqueue(tCoords); + } } } } } } catch (Exception e) { GT_Mod.GT_FML_LOGGER.error( - "Well this update was broken... " + mCoords + "Well this update was broken... " + initialX + + ", " + + initialY + + ", " + + initialZ + ", mWorld={" + world.getProviderName() + " @dimId " diff --git a/src/main/java/gregtech/api/threads/GT_Runnable_MachineBlockUpdate.java b/src/main/java/gregtech/api/threads/GT_Runnable_MachineBlockUpdate.java index ceb6adfa7a..3670655eaf 100644 --- a/src/main/java/gregtech/api/threads/GT_Runnable_MachineBlockUpdate.java +++ b/src/main/java/gregtech/api/threads/GT_Runnable_MachineBlockUpdate.java @@ -1,30 +1,59 @@ package gregtech.api.threads; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.Queue; -import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import net.minecraft.tileentity.TileEntity; -import net.minecraft.util.ChunkCoordinates; import net.minecraft.world.World; +import net.minecraftforge.common.util.ForgeDirection; import gregtech.GT_Mod; import gregtech.api.GregTech_API; import gregtech.api.interfaces.tileentity.IMachineBlockUpdateable; import gregtech.common.GT_Proxy; +import it.unimi.dsi.fastutil.longs.LongArrayFIFOQueue; +import it.unimi.dsi.fastutil.longs.LongOpenHashSet; +import it.unimi.dsi.fastutil.longs.LongSet; public class GT_Runnable_MachineBlockUpdate implements Runnable { + // Borrowed from Angelica until moving to GTNHLib or something + final static int SIZE_BITS_X = 26; // 1 + log2(MathHelper.roundUpToPowerOfTwo(30000000), RoundingMode.UNNECESSARY); + final static int SIZE_BITS_Z = SIZE_BITS_X; + final static int SIZE_BITS_Y = 64 - SIZE_BITS_X - SIZE_BITS_Z; + final static long BITS_X = (1L << SIZE_BITS_X) - 1L; + final static long BITS_Y = (1L << SIZE_BITS_Y) - 1L; + final static long BITS_Z = (1L << SIZE_BITS_Z) - 1L; + final static int BIT_SHIFT_Z = SIZE_BITS_Y; + final static int BIT_SHIFT_X = SIZE_BITS_Y + SIZE_BITS_Z; + + static long asLong(int x, int y, int z) { + long l = 0L; + l |= ((long) x & BITS_X) << BIT_SHIFT_X; + l |= ((long) y & BITS_Y) << 0; + l |= ((long) z & BITS_Z) << BIT_SHIFT_Z; + return l; + } + + public static int unpackLongX(long packedPos) { + return (int) (packedPos << 64 - BIT_SHIFT_X - SIZE_BITS_X >> 64 - SIZE_BITS_X); + } + + public static int unpackLongY(long packedPos) { + return (int) (packedPos << 64 - SIZE_BITS_Y >> 64 - SIZE_BITS_Y); + } + + public static int unpackLongZ(long packedPos) { + return (int) (packedPos << 64 - BIT_SHIFT_Z - SIZE_BITS_Z >> 64 - SIZE_BITS_Z); + } + // used by runner thread - protected final ChunkCoordinates mCoords; + protected final int initialX, initialY, initialZ; protected final World world; - protected final Set visited = new HashSet<>(80); - protected final Queue tQueue = new LinkedList<>(); + protected final LongSet visited = new LongOpenHashSet(); + protected final LongArrayFIFOQueue tQueue = new LongArrayFIFOQueue(); // Threading private static final ThreadFactory THREAD_FACTORY = r -> { @@ -35,11 +64,14 @@ public class GT_Runnable_MachineBlockUpdate implements Runnable { protected static ExecutorService EXECUTOR_SERVICE; // This class should never be initiated outside of this class! - protected GT_Runnable_MachineBlockUpdate(World aWorld, ChunkCoordinates aCoords) { + protected GT_Runnable_MachineBlockUpdate(World aWorld, int posX, int posY, int posZ) { this.world = aWorld; - this.mCoords = aCoords; - visited.add(aCoords); - tQueue.add(aCoords); + this.initialX = posX; + this.initialY = posY; + this.initialZ = posZ; + final long coords = asLong(posX, posY, posZ); + visited.add(coords); + tQueue.enqueue(coords); } public static boolean isEnabled() { @@ -69,9 +101,9 @@ public class GT_Runnable_MachineBlockUpdate implements Runnable { protected static boolean isEnabled = true; protected static final ThreadLocal perThreadEnable = ThreadLocal.withInitial(() -> true); - public static void setMachineUpdateValues(World aWorld, ChunkCoordinates aCoords) { + public static void setMachineUpdateValues(World aWorld, int posX, int posY, int posZ) { if (isEnabled() && isCurrentThreadEnabled()) { - EXECUTOR_SERVICE.submit(new GT_Runnable_MachineBlockUpdate(aWorld, aCoords)); + EXECUTOR_SERVICE.submit(new GT_Runnable_MachineBlockUpdate(aWorld, posX, posY, posZ)); } } @@ -116,9 +148,14 @@ public class GT_Runnable_MachineBlockUpdate implements Runnable { @Override public void run() { + int posX, posY, posZ; try { while (!tQueue.isEmpty()) { - final ChunkCoordinates aCoords = tQueue.poll(); + final long packedCoords = tQueue.dequeueLong(); + posX = unpackLongX(packedCoords); + posY = unpackLongY(packedCoords); + posZ = unpackLongZ(packedCoords); + final TileEntity tTileEntity; final boolean isMachineBlock; @@ -128,10 +165,9 @@ public class GT_Runnable_MachineBlockUpdate implements Runnable { // ConcurrentModificationException. So, lock that shit. GT_Proxy.TICK_LOCK.lock(); try { - tTileEntity = world.getTileEntity(aCoords.posX, aCoords.posY, aCoords.posZ); - isMachineBlock = GregTech_API.isMachineBlock( - world.getBlock(aCoords.posX, aCoords.posY, aCoords.posZ), - world.getBlockMetadata(aCoords.posX, aCoords.posY, aCoords.posZ)); + tTileEntity = world.getTileEntity(posX, posY, posZ); + isMachineBlock = GregTech_API + .isMachineBlock(world.getBlock(posX, posY, posZ), world.getBlockMetadata(posX, posY, posZ)); } finally { GT_Proxy.TICK_LOCK.unlock(); } @@ -148,25 +184,22 @@ public class GT_Runnable_MachineBlockUpdate implements Runnable { || (tTileEntity instanceof IMachineBlockUpdateable && ((IMachineBlockUpdateable) tTileEntity).isMachineBlockUpdateRecursive()) || isMachineBlock) { - ChunkCoordinates tCoords; - - if (visited.add(tCoords = new ChunkCoordinates(aCoords.posX + 1, aCoords.posY, aCoords.posZ))) - tQueue.add(tCoords); - if (visited.add(tCoords = new ChunkCoordinates(aCoords.posX - 1, aCoords.posY, aCoords.posZ))) - tQueue.add(tCoords); - if (visited.add(tCoords = new ChunkCoordinates(aCoords.posX, aCoords.posY + 1, aCoords.posZ))) - tQueue.add(tCoords); - if (visited.add(tCoords = new ChunkCoordinates(aCoords.posX, aCoords.posY - 1, aCoords.posZ))) - tQueue.add(tCoords); - if (visited.add(tCoords = new ChunkCoordinates(aCoords.posX, aCoords.posY, aCoords.posZ + 1))) - tQueue.add(tCoords); - if (visited.add(tCoords = new ChunkCoordinates(aCoords.posX, aCoords.posY, aCoords.posZ - 1))) - tQueue.add(tCoords); + for (int i = 0; i < ForgeDirection.VALID_DIRECTIONS.length; i++) { + final ForgeDirection side = ForgeDirection.VALID_DIRECTIONS[i]; + final long tCoords = asLong(posX + side.offsetX, posY + side.offsetY, posZ + side.offsetZ); + if (visited.add(tCoords)) { + tQueue.enqueue(tCoords); + } + } } } } catch (Exception e) { GT_Mod.GT_FML_LOGGER.error( - "Well this update was broken... " + mCoords + "Well this update was broken... " + initialX + + ", " + + initialY + + ", " + + initialZ + ", mWorld={" + world.getProviderName() + " @dimId " -- cgit