From c8d1ee38434f465b1b79bf0588b16f4f8d860876 Mon Sep 17 00:00:00 2001 From: Brian Date: Sat, 20 Jan 2024 21:09:41 -0500 Subject: [PATCH] Clean up, doc comments --- .../powers/client/util/SensoryTracking.java | 76 +--------------- .../legobmw99/allomancy/util/SyncList.java | 87 +++++++++++++++++++ 2 files changed, 90 insertions(+), 73 deletions(-) create mode 100644 src/main/java/com/legobmw99/allomancy/util/SyncList.java diff --git a/src/main/java/com/legobmw99/allomancy/modules/powers/client/util/SensoryTracking.java b/src/main/java/com/legobmw99/allomancy/modules/powers/client/util/SensoryTracking.java index 0cc04f83..cf193b4a 100644 --- a/src/main/java/com/legobmw99/allomancy/modules/powers/client/util/SensoryTracking.java +++ b/src/main/java/com/legobmw99/allomancy/modules/powers/client/util/SensoryTracking.java @@ -5,6 +5,7 @@ import com.legobmw99.allomancy.modules.powers.PowerUtils; import com.legobmw99.allomancy.modules.powers.PowersConfig; import com.legobmw99.allomancy.modules.powers.data.AllomancerAttachment; +import com.legobmw99.allomancy.util.SyncList; import it.unimi.dsi.fastutil.longs.LongOpenHashSet; import net.minecraft.Util; import net.minecraft.client.Minecraft; @@ -17,16 +18,11 @@ import net.minecraft.world.phys.Vec3; import java.util.*; -import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; import java.util.function.Consumer; public class SensoryTracking { - private final List metal_entities = new ArrayList<>(); private final SyncList metal_blobs = new SyncList<>(); private final List nearby_allomancers = new ArrayList<>(); @@ -143,79 +139,13 @@ private boolean addSeeked(IAllomancerData data, Player otherPlayer) { return true; } - - private static class SyncList { - private final List list_a = new ArrayList<>(); - private final List list_b = new ArrayList<>(); - - private final Lock swapLock = new ReentrantLock(); - - /** - * When this is even, we are reading A and writing B - */ - private final AtomicInteger AorB = new AtomicInteger(0); - - - /** - * Intended to be invoked from the main thread - */ - public void forEach(Consumer f) { - this.swapLock.lock(); - try { - if (this.AorB.get() % 2 == 0) { - this.list_a.forEach(f); - } else { - this.list_b.forEach(f); - } - } finally { - this.swapLock.unlock(); - } - } - - public void add(T t) { - if (this.AorB.get() % 2 == 1) { - this.list_a.add(t); - } else { - this.list_b.add(t); - } - } - - - /** - * Intended to be invoked from a thread other than main - */ - public void swapAndClearOld() { - this.swapLock.lock(); - int newAB = this.AorB.incrementAndGet(); - this.swapLock.unlock(); - if (newAB % 2 == 1) { - this.list_a.clear(); - } else { - this.list_b.clear(); - } - } - - public void clearBothAsync(ExecutorService ex) { - ex.submit(() -> { - this.swapLock.lock(); - try { - this.list_a.clear(); - this.list_b.clear(); - } finally { - this.swapLock.unlock(); - } - }); - } - } - - public static class MetalBlockBlob { private static final Level level = Minecraft.getInstance().level; private int blocks; private Vec3 center; - public MetalBlockBlob(BlockPos initial, BlockState initialState) { + private MetalBlockBlob(BlockPos initial, BlockState initialState) { this.blocks = 1; this.center = getCenterOfBlock(initial, initialState); } @@ -232,7 +162,7 @@ public int size() { return this.blocks; } - public void add(BlockPos pos, BlockState state) { + private void add(BlockPos pos, BlockState state) { this.blocks += 1; this.center = this.center.scale(this.blocks - 1).add(getCenterOfBlock(pos, state)).scale(1.0D / this.blocks); } diff --git a/src/main/java/com/legobmw99/allomancy/util/SyncList.java b/src/main/java/com/legobmw99/allomancy/util/SyncList.java new file mode 100644 index 00000000..8eca82e5 --- /dev/null +++ b/src/main/java/com/legobmw99/allomancy/util/SyncList.java @@ -0,0 +1,87 @@ +package com.legobmw99.allomancy.util; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; +import java.util.function.Consumer; + +/** + * A wrapper around 2 array lists that allows "concurrent" modification + * while maintaining very fast reads at the cost of slower/buffered writes. + * Writes are issued to a list which is not currently a candidate for iteration, + * and on demand this list is swapped in. The lock required to swap is only held + * for the length of one atomic integer increment. + */ +public class SyncList { + private final List list_a = new ArrayList<>(); + private final List list_b = new ArrayList<>(); + + private final Lock swapLock = new ReentrantLock(); + + /** + * When this is even, we are reading A and writing B + */ + private final AtomicInteger AorB = new AtomicInteger(0); + + /** + * Intended to be invoked from the main thread. + * Holds a lock to prevent swapping for entire iteration duration. + * Writes will still proceed to the unobserved list. + */ + public void forEach(Consumer f) { + this.swapLock.lock(); + try { + if (this.AorB.get() % 2 == 0) { + this.list_a.forEach(f); + } else { + this.list_b.forEach(f); + } + } finally { + this.swapLock.unlock(); + } + } + + /** + * Write to the unobserved list. + * Nothing added here wil be visible until + * a swapAndClearOld is completed. + */ + public void add(T t) { + if (this.AorB.get() % 2 == 1) { + this.list_a.add(t); + } else { + this.list_b.add(t); + } + } + + /** + * Swap which list is observable as soon as the lock can be acquired, + * and clear the now-unobserved list. + * Intended to be invoked from a thread other than main + */ + public void swapAndClearOld() { + this.swapLock.lock(); + int newAB = this.AorB.incrementAndGet(); + this.swapLock.unlock(); + if (newAB % 2 == 1) { + this.list_a.clear(); + } else { + this.list_b.clear(); + } + } + + public void clearBothAsync(ExecutorService ex) { + ex.submit(() -> { + this.swapLock.lock(); + try { + this.list_a.clear(); + this.list_b.clear(); + } finally { + this.swapLock.unlock(); + } + }); + } +}