9
0
mirror of https://github.com/BX-Team/DivineMC.git synced 2025-12-21 07:49:18 +00:00

update multithreaded tracker and pwt, some fixes

This commit is contained in:
NONPLAYT
2025-06-29 00:10:19 +03:00
parent 9feb5e9577
commit 5c7a5a8cf4
15 changed files with 1948 additions and 324 deletions

View File

@@ -3,7 +3,6 @@ package org.bxteam.divinemc.entity.tracking;
import ca.spottedleaf.moonrise.common.list.ReferenceList;
import ca.spottedleaf.moonrise.common.misc.NearbyPlayers;
import ca.spottedleaf.moonrise.common.util.TickThread;
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel;
import ca.spottedleaf.moonrise.patches.chunk_system.level.entity.server.ServerEntityLookup;
import ca.spottedleaf.moonrise.patches.entity_tracker.EntityTrackerEntity;
import net.minecraft.server.level.ChunkMap;
@@ -19,7 +18,6 @@ import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
@@ -31,7 +29,7 @@ public class MultithreadedTracker {
private static final Logger LOGGER = LogManager.getLogger(THREAD_PREFIX);
private static long lastWarnMillis = System.currentTimeMillis();
private static final ThreadPoolExecutor trackerExecutor = new ThreadPoolExecutor(
private static final ThreadPoolExecutor TRACKER_EXECUTOR = new ThreadPoolExecutor(
getCorePoolSize(),
getMaxPoolSize(),
getKeepAliveTime(), TimeUnit.SECONDS,
@@ -40,11 +38,7 @@ public class MultithreadedTracker {
getRejectedPolicy()
);
public static Executor getTrackerExecutor() {
return trackerExecutor;
}
public static void tick(ChunkSystemServerLevel level) {
public static void tick(ServerLevel level) {
try {
if (!DivineConfig.AsyncCategory.multithreadedCompatModeEnabled) {
tickAsync(level);
@@ -56,15 +50,14 @@ public class MultithreadedTracker {
}
}
private static void tickAsync(ChunkSystemServerLevel level) {
private static void tickAsync(ServerLevel level) {
final NearbyPlayers nearbyPlayers = level.moonrise$getNearbyPlayers();
final ServerEntityLookup entityLookup = (ServerEntityLookup) level.moonrise$getEntityLookup();
final ReferenceList<Entity> trackerEntities = entityLookup.trackerEntities;
final Entity[] trackerEntitiesRaw = trackerEntities.getRawDataUnchecked();
// Move tracking to off-main
trackerExecutor.execute(() -> {
TRACKER_EXECUTOR.execute(() -> {
for (final Entity entity : trackerEntitiesRaw) {
if (entity == null) continue;
@@ -72,19 +65,23 @@ public class MultithreadedTracker {
if (tracker == null) continue;
tracker.moonrise$tick(nearbyPlayers.getChunk(entity.chunkPosition()));
tracker.serverEntity.sendChanges();
synchronized (tracker) {
var trackedChunk = nearbyPlayers.getChunk(entity.chunkPosition());
tracker.moonrise$tick(trackedChunk);
tracker.serverEntity.sendChanges();
}
}
});
}
private static void tickAsyncWithCompatMode(ChunkSystemServerLevel level) {
private static void tickAsyncWithCompatMode(ServerLevel level) {
final NearbyPlayers nearbyPlayers = level.moonrise$getNearbyPlayers();
final ServerEntityLookup entityLookup = (ServerEntityLookup) level.moonrise$getEntityLookup();
final ReferenceList<Entity> trackerEntities = entityLookup.trackerEntities;
final Entity[] trackerEntitiesRaw = trackerEntities.getRawDataUnchecked();
final Runnable[] sendChangesTasks = new Runnable[trackerEntitiesRaw.length];
final Runnable[] tickTask = new Runnable[trackerEntitiesRaw.length];
int index = 0;
for (final Entity entity : trackerEntitiesRaw) {
@@ -94,12 +91,19 @@ public class MultithreadedTracker {
if (tracker == null) continue;
tracker.moonrise$tick(nearbyPlayers.getChunk(entity.chunkPosition()));
sendChangesTasks[index++] = () -> tracker.serverEntity.sendChanges(); // Collect send changes to task array
synchronized (tracker) {
tickTask[index] = tracker.tickCompact(nearbyPlayers.getChunk(entity.chunkPosition()));
sendChangesTasks[index] = () -> tracker.serverEntity.sendChanges();
}
index++;
}
// batch submit tasks
trackerExecutor.execute(() -> {
TRACKER_EXECUTOR.execute(() -> {
for (final Runnable tick : tickTask) {
if (tick == null) continue;
tick.run();
}
for (final Runnable sendChanges : sendChangesTasks) {
if (sendChanges == null) continue;

View File

@@ -0,0 +1,326 @@
package org.bxteam.divinemc.util.map;
import net.minecraft.core.Holder;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.world.entity.ai.attributes.Attribute;
import net.minecraft.world.entity.ai.attributes.AttributeInstance;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.AbstractMap.SimpleEntry;
/**
* @author hayanesuru
*/
public final class AttributeInstanceArrayMap implements Map<Holder<Attribute>, AttributeInstance>, Cloneable {
private static final int VANILLA_ATTRIBUTE_SIZE = 35; // 1.21.6
private int size = 0;
private transient AttributeInstance[] a = new AttributeInstance[VANILLA_ATTRIBUTE_SIZE];
private transient KeySet keys;
private transient Values values;
private transient EntrySet entries;
public AttributeInstanceArrayMap() {
if (BuiltInRegistries.ATTRIBUTE.size() != VANILLA_ATTRIBUTE_SIZE) {
throw new IllegalStateException("Unexpected registry minecraft:attribute size");
}
}
public AttributeInstanceArrayMap(final @NotNull Map<Holder<Attribute>, AttributeInstance> m) {
this();
putAll(m);
}
private void setByIndex(int index, @Nullable AttributeInstance instance) {
boolean empty = a[index] == null;
if (instance == null) {
if (!empty) {
size--;
a[index] = null;
}
} else {
if (empty) {
size++;
}
a[index] = instance;
}
}
@Override
public int size() {
return size;
}
@Override
public boolean isEmpty() {
return size == 0;
}
@Override
public boolean containsKey(Object key) {
if (key instanceof Holder<?> holder && holder.value() instanceof Attribute attribute) {
int uid = attribute.uid;
return uid >= 0 && uid < a.length && a[uid] != null;
}
return false;
}
@Override
public boolean containsValue(Object value) {
return value instanceof AttributeInstance val && Objects.equals(getInstance(val.getAttribute().value().uid), val);
}
@Override
public AttributeInstance get(Object key) {
return key instanceof Holder<?> holder && holder.value() instanceof Attribute attribute ? a[attribute.uid] : null;
}
@Nullable
public AttributeInstance getInstance(int key) {
return a[key];
}
@Override
public AttributeInstance put(@NotNull Holder<Attribute> key, AttributeInstance value) {
int uid = key.value().uid;
AttributeInstance prev = a[uid];
setByIndex(uid, value);
return prev;
}
@Override
public AttributeInstance remove(Object key) {
if (!(key instanceof Holder<?> holder) || !(holder.value() instanceof Attribute attribute)) return null;
int uid = attribute.uid;
AttributeInstance prev = a[uid];
setByIndex(uid, null);
return prev;
}
@Override
public void putAll(@NotNull Map<? extends Holder<Attribute>, ? extends AttributeInstance> m) {
for (AttributeInstance e : m.values()) {
if (e != null) {
setByIndex(e.getAttribute().value().uid, e);
}
}
}
@Override
public void clear() {
Arrays.fill(a, null);
size = 0;
}
@Override
public @NotNull Set<Holder<Attribute>> keySet() {
if (keys == null) {
keys = new KeySet();
}
return keys;
}
@Override
public @NotNull Collection<AttributeInstance> values() {
if (values == null) {
values = new Values();
}
return values;
}
@Override
public @NotNull Set<Entry<Holder<Attribute>, AttributeInstance>> entrySet() {
if (entries == null) {
entries = new EntrySet();
}
return entries;
}
@Override
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof Map<?, ?> s)) return false;
if (s.size() != size()) return false;
if (o instanceof AttributeInstanceArrayMap that) {
return Arrays.equals(a, that.a);
}
for (Entry<?, ?> e : s.entrySet()) {
if (!Objects.equals(get(e.getKey()), e.getValue())) {
return false;
}
}
return true;
}
@Override
public int hashCode() {
return Arrays.hashCode(a);
}
@Override
public AttributeInstanceArrayMap clone() {
AttributeInstanceArrayMap c;
try {
c = (AttributeInstanceArrayMap) super.clone();
} catch (CloneNotSupportedException cantHappen) {
throw new InternalError();
}
c.a = a.clone();
c.entries = null;
c.keys = null;
c.values = null;
return c;
}
private final class KeySet extends AbstractSet<Holder<Attribute>> {
@Override
public @NotNull Iterator<Holder<Attribute>> iterator() {
return new KeyIterator();
}
@Override
public int size() {
return size;
}
@Override
public boolean contains(Object o) {
return AttributeInstanceArrayMap.this.containsKey(o);
}
}
private final class KeyIterator implements Iterator<Holder<Attribute>> {
private int currentIndex = -1;
private int nextIndex = findNextOccupied(0);
@Override
public boolean hasNext() {
return nextIndex != -1;
}
@Override
public Holder<Attribute> next() {
if (!hasNext()) throw new NoSuchElementException();
currentIndex = nextIndex;
nextIndex = findNextOccupied(nextIndex + 1);
return BuiltInRegistries.ATTRIBUTE.get(currentIndex).orElseThrow();
}
@Override
public void remove() {
if (currentIndex == -1) throw new IllegalStateException();
setByIndex(currentIndex, null);
currentIndex = -1;
}
}
private final class Values extends AbstractCollection<AttributeInstance> {
@Override
public @NotNull Iterator<AttributeInstance> iterator() {
return new ValueIterator();
}
@Override
public int size() {
return size;
}
@Override
public boolean contains(Object o) {
return containsValue(o);
}
}
private final class ValueIterator implements Iterator<AttributeInstance> {
private int currentIndex = -1;
private int nextIndex = findNextOccupied(0);
@Override
public boolean hasNext() {
return nextIndex != -1;
}
@Override
public AttributeInstance next() {
if (!hasNext()) throw new NoSuchElementException();
currentIndex = nextIndex;
AttributeInstance value = a[nextIndex];
nextIndex = findNextOccupied(nextIndex + 1);
return value;
}
@Override
public void remove() {
if (currentIndex == -1) throw new IllegalStateException();
setByIndex(currentIndex, null);
currentIndex = -1;
}
}
private final class EntrySet extends AbstractSet<Entry<Holder<Attribute>, AttributeInstance>> {
@Override
public @NotNull Iterator<Entry<Holder<Attribute>, AttributeInstance>> iterator() {
return new EntryIterator();
}
@Override
public int size() {
return size;
}
@Override
public boolean contains(Object o) {
if (!(o instanceof Entry<?, ?> e)) {
return false;
}
return Objects.equals(get(e.getKey()), e.getValue());
}
}
private final class EntryIterator implements Iterator<Entry<Holder<Attribute>, AttributeInstance>> {
private int currentIndex = -1;
private int nextIndex = findNextOccupied(0);
@Override
public boolean hasNext() {
return nextIndex != -1;
}
@Override
public Entry<Holder<Attribute>, AttributeInstance> next() {
if (!hasNext()) throw new NoSuchElementException();
currentIndex = nextIndex;
Holder<Attribute> key = BuiltInRegistries.ATTRIBUTE.get(nextIndex).orElseThrow();
AttributeInstance value = a[nextIndex];
nextIndex = findNextOccupied(nextIndex + 1);
return new SimpleEntry<>(key, value) {
@Override
public AttributeInstance setValue(AttributeInstance newValue) {
AttributeInstance old = put(key, newValue);
super.setValue(newValue);
return old;
}
};
}
@Override
public void remove() {
if (currentIndex == -1) {
throw new IllegalStateException();
}
setByIndex(currentIndex, null);
currentIndex = -1;
}
}
private int findNextOccupied(int start) {
for (int i = start; i < a.length; i++) {
if (a[i] != null) {
return i;
}
}
return -1;
}
}

View File

@@ -0,0 +1,116 @@
package org.bxteam.divinemc.util.map;
import it.unimi.dsi.fastutil.ints.IntArraySet;
import it.unimi.dsi.fastutil.ints.IntSet;
import net.minecraft.world.entity.ai.attributes.AttributeInstance;
import org.jetbrains.annotations.NotNull;
import java.lang.reflect.Array;
import java.util.*;
/**
* @author hayanesuru
*/
public final class AttributeInstanceSet extends AbstractCollection<AttributeInstance> implements Set<AttributeInstance> {
public final IntSet inner;
public final AttributeInstanceArrayMap map;
public AttributeInstanceSet(AttributeInstanceArrayMap map) {
this.map = map;
inner = new IntArraySet();
}
@Override
public boolean add(AttributeInstance instance) {
return inner.add(instance.getAttribute().value().uid);
}
@Override
public boolean remove(Object o) {
return o instanceof AttributeInstance instance && inner.remove(instance.getAttribute().value().uid);
}
@Override
public @NotNull Iterator<AttributeInstance> iterator() {
return new CloneIterator(inner.toIntArray(), map);
}
@Override
public int size() {
return inner.size();
}
@Override
public boolean isEmpty() {
return inner.isEmpty();
}
@Override
public void clear() {
inner.clear();
}
@Override
public boolean contains(Object o) {
if (o instanceof AttributeInstance instance) {
return inner.contains(instance.getAttribute().value().uid);
}
return false;
}
@Override
public AttributeInstance @NotNull [] toArray() {
int[] innerClone = inner.toIntArray();
AttributeInstance[] arr = new AttributeInstance[innerClone.length];
for (int i = 0; i < arr.length; i++) {
arr[i] = map.getInstance(innerClone[i]);
}
return arr;
}
@SuppressWarnings({"unchecked"})
@Override
public <T> T @NotNull [] toArray(T[] a) {
if (a == null || (a.getClass() == AttributeInstance[].class && a.length == 0)) {
return (T[]) toArray();
}
if (a.length < size()) {
a = (T[]) Array.newInstance(a.getClass().getComponentType(), size());
}
System.arraycopy((T[]) toArray(), 0, a, 0, size());
if (a.length > size()) {
a[size()] = null;
}
return a;
}
static class CloneIterator implements Iterator<AttributeInstance> {
private final int[] array;
private int index = 0;
private final AttributeInstanceArrayMap map;
CloneIterator(int[] array, AttributeInstanceArrayMap map) {
this.array = array;
this.map = map;
}
@Override
public boolean hasNext() {
return index < array.length;
}
@Override
public AttributeInstance next() {
if (!hasNext()) throw new NoSuchElementException();
return map.getInstance(array[index++]);
}
}
@Override
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof Set<?> s)) return false;
if (s.size() != size()) return false;
return containsAll(s);
}
}

View File

@@ -6,102 +6,136 @@ import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import it.unimi.dsi.fastutil.longs.LongSet;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
/**
* A thread-safe implementation of {@link LongOpenHashSet} using ConcurrentHashMap.KeySetView as backing storage.
* This implementation provides concurrent access and high performance for concurrent operations.
* Optimized thread-safe implementation of {@link LongSet} that uses striped locking
* and primitive long arrays to minimize boxing/unboxing overhead.
*
* @author HaHaWTH at Leaf
*/
@SuppressWarnings({"unused", "deprecation"})
public final class ConcurrentLongHashSet extends LongOpenHashSet implements LongSet {
private final ConcurrentHashMap.KeySetView<Long, Boolean> backing;
// Number of lock stripes - higher number means more concurrency but more memory
private static final int DEFAULT_CONCURRENCY_LEVEL = 16;
// Load factor - when to resize the hash table
private static final float DEFAULT_LOAD_FACTOR = 0.75f;
// Initial capacity per stripe
private static final int DEFAULT_INITIAL_CAPACITY = 16;
// Array of segments (stripes)
private final Segment[] segments;
// Total size, cached for faster size() operation
private final AtomicInteger size;
/**
* Creates a new empty concurrent long set.
* Creates a new empty concurrent long set with default parameters.
*/
public ConcurrentLongHashSet() {
this.backing = ConcurrentHashMap.newKeySet();
this(DEFAULT_CONCURRENCY_LEVEL * DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL);
}
/**
* Creates a new concurrent long set with the specified parameters.
*
* @param initialCapacity the initial capacity
* @param loadFactor the load factor
* @param concurrencyLevel the concurrency level
*/
public ConcurrentLongHashSet(int initialCapacity, float loadFactor, int concurrencyLevel) {
// Need to call super() even though we don't use its state
super();
// Validate parameters
if (initialCapacity < 0) {
throw new IllegalArgumentException("Initial capacity must be positive");
}
if (loadFactor <= 0 || Float.isNaN(loadFactor)) {
throw new IllegalArgumentException("Load factor must be positive");
}
if (concurrencyLevel <= 0) {
throw new IllegalArgumentException("Concurrency level must be positive");
}
// Calculate segment count (power of 2)
int segmentCount = 1;
while (segmentCount < concurrencyLevel) {
segmentCount <<= 1;
}
// Calculate capacity per segment
int segmentCapacity = Math.max(initialCapacity / segmentCount, DEFAULT_INITIAL_CAPACITY);
// Create segments
this.segments = new Segment[segmentCount];
for (int i = 0; i < segmentCount; i++) {
this.segments[i] = new Segment(segmentCapacity, loadFactor);
}
this.size = new AtomicInteger(0);
}
@Override
public int size() {
return backing.size();
return size.get();
}
@Override
public boolean isEmpty() {
return backing.isEmpty();
}
@Override
public @NotNull LongIterator iterator() {
return new WrappingLongIterator(backing.iterator());
}
@NotNull
@Override
public Object @NotNull [] toArray() {
return backing.toArray();
}
@NotNull
@Override
public <T> T @NotNull [] toArray(@NotNull T @NotNull [] array) {
Objects.requireNonNull(array, "Array cannot be null");
return backing.toArray(array);
}
@Override
public boolean containsAll(@NotNull Collection<?> collection) {
Objects.requireNonNull(collection, "Collection cannot be null");
return backing.containsAll(collection);
}
@Override
public boolean addAll(@NotNull Collection<? extends Long> collection) {
Objects.requireNonNull(collection, "Collection cannot be null");
return backing.addAll(collection);
}
@Override
public boolean removeAll(@NotNull Collection<?> collection) {
Objects.requireNonNull(collection, "Collection cannot be null");
return backing.removeAll(collection);
}
@Override
public boolean retainAll(@NotNull Collection<?> collection) {
Objects.requireNonNull(collection, "Collection cannot be null");
return backing.retainAll(collection);
}
@Override
public void clear() {
backing.clear();
return size.get() == 0;
}
@Override
public boolean add(long key) {
return backing.add(key);
Segment segment = segmentFor(key);
int delta = segment.add(key) ? 1 : 0;
if (delta > 0) {
size.addAndGet(delta);
}
return delta > 0;
}
@Override
public boolean contains(long key) {
return backing.contains(key);
return segmentFor(key).contains(key);
}
@Override
public boolean remove(long key) {
Segment segment = segmentFor(key);
int delta = segment.remove(key) ? -1 : 0;
if (delta < 0) {
size.addAndGet(delta);
}
return delta < 0;
}
@Override
public void clear() {
for (Segment segment : segments) {
segment.clear();
}
size.set(0);
}
@Override
public @NotNull LongIterator iterator() {
return new ConcurrentLongIterator();
}
@Override
public long[] toLongArray() {
int size = backing.size();
long[] result = new long[size];
int i = 0;
for (Long value : backing) {
result[i++] = value;
long[] result = new long[size()];
int index = 0;
for (Segment segment : segments) {
index = segment.toLongArray(result, index);
}
return result;
}
@@ -120,6 +154,104 @@ public final class ConcurrentLongHashSet extends LongOpenHashSet implements Long
return array;
}
@NotNull
@Override
public Object @NotNull [] toArray() {
Long[] result = new Long[size()];
int index = 0;
for (Segment segment : segments) {
index = segment.toObjectArray(result, index);
}
return result;
}
@NotNull
@Override
public <T> T @NotNull [] toArray(@NotNull T @NotNull [] array) {
Objects.requireNonNull(array, "Array cannot be null");
Long[] result = new Long[size()];
int index = 0;
for (Segment segment : segments) {
index = segment.toObjectArray(result, index);
}
if (array.length < result.length) {
return (T[]) result;
}
System.arraycopy(result, 0, array, 0, result.length);
if (array.length > result.length) {
array[result.length] = null;
}
return array;
}
@Override
public boolean containsAll(@NotNull Collection<?> collection) {
Objects.requireNonNull(collection, "Collection cannot be null");
for (Object o : collection) {
if (o instanceof Long) {
if (!contains(((Long) o).longValue())) {
return false;
}
} else {
return false;
}
}
return true;
}
@Override
public boolean addAll(@NotNull Collection<? extends Long> collection) {
Objects.requireNonNull(collection, "Collection cannot be null");
boolean modified = false;
for (Long value : collection) {
modified |= add(value);
}
return modified;
}
@Override
public boolean removeAll(@NotNull Collection<?> collection) {
Objects.requireNonNull(collection, "Collection cannot be null");
boolean modified = false;
for (Object o : collection) {
if (o instanceof Long) {
modified |= remove(((Long) o).longValue());
}
}
return modified;
}
@Override
public boolean retainAll(@NotNull Collection<?> collection) {
Objects.requireNonNull(collection, "Collection cannot be null");
// Convert collection to a set of longs for faster lookups
LongOpenHashSet toRetain = new LongOpenHashSet();
for (Object o : collection) {
if (o instanceof Long) {
toRetain.add(((Long) o).longValue());
}
}
boolean modified = false;
for (Segment segment : segments) {
modified |= segment.retainAll(toRetain);
}
if (modified) {
// Recalculate size
int newSize = 0;
for (Segment segment : segments) {
newSize += segment.size();
}
size.set(newSize);
}
return modified;
}
@Override
public boolean addAll(LongCollection c) {
Objects.requireNonNull(c, "Collection cannot be null");
@@ -157,12 +289,23 @@ public final class ConcurrentLongHashSet extends LongOpenHashSet implements Long
@Override
public boolean retainAll(LongCollection c) {
Objects.requireNonNull(c, "Collection cannot be null");
return backing.retainAll(c);
}
@Override
public boolean remove(long k) {
return backing.remove(k);
// For LongCollection we can directly use it
boolean modified = false;
for (Segment segment : segments) {
modified |= segment.retainAll(c);
}
if (modified) {
// Recalculate size
int newSize = 0;
for (Segment segment : segments) {
newSize += segment.size();
}
size.set(newSize);
}
return modified;
}
@Override
@@ -175,39 +318,382 @@ public final class ConcurrentLongHashSet extends LongOpenHashSet implements Long
@Override
public int hashCode() {
return backing.hashCode();
int hash = 0;
for (Segment segment : segments) {
hash += segment.hashCode();
}
return hash;
}
@Override
public String toString() {
return backing.toString();
StringBuilder sb = new StringBuilder();
sb.append('[');
LongIterator it = iterator();
boolean hasNext = it.hasNext();
while (hasNext) {
sb.append(it.nextLong());
hasNext = it.hasNext();
if (hasNext) {
sb.append(", ");
}
}
sb.append(']');
return sb.toString();
}
static class WrappingLongIterator implements LongIterator {
private final Iterator<Long> backing;
/**
* Find the segment for a given key.
*/
private Segment segmentFor(long key) {
// Use high bits of hash to determine segment
// This helps spread keys more evenly across segments
return segments[(int) ((spread(key) >>> segmentShift()) & segmentMask())];
}
WrappingLongIterator(Iterator<Long> backing) {
this.backing = Objects.requireNonNull(backing);
/**
* Spread bits to reduce clustering for keys with similar hash codes.
*/
private static long spread(long key) {
long h = key;
h ^= h >>> 32;
h ^= h >>> 16;
h ^= h >>> 8;
return h;
}
private int segmentShift() {
return Integer.numberOfLeadingZeros(segments.length);
}
private int segmentMask() {
return segments.length - 1;
}
/**
* A segment is a striped portion of the hash set with its own lock.
*/
private static class Segment {
private final ReentrantLock lock = new ReentrantLock();
private long[] keys;
private boolean[] used;
private int size;
private int threshold;
private final float loadFactor;
Segment(int initialCapacity, float loadFactor) {
int capacity = MathUtil.nextPowerOfTwo(initialCapacity);
this.keys = new long[capacity];
this.used = new boolean[capacity];
this.size = 0;
this.loadFactor = loadFactor;
this.threshold = (int) (capacity * loadFactor);
}
int size() {
lock.lock();
try {
return size;
} finally {
lock.unlock();
}
}
boolean contains(long key) {
lock.lock();
try {
int index = indexOf(key);
return used[index] && keys[index] == key;
} finally {
lock.unlock();
}
}
boolean add(long key) {
lock.lock();
try {
int index = indexOf(key);
// Key already exists
if (used[index] && keys[index] == key) {
return false;
}
// Insert key
keys[index] = key;
if (!used[index]) {
used[index] = true;
size++;
// Check if rehash is needed
if (size > threshold) {
rehash();
}
}
return true;
} finally {
lock.unlock();
}
}
boolean remove(long key) {
lock.lock();
try {
int index = indexOf(key);
// Key not found
if (!used[index] || keys[index] != key) {
return false;
}
// Mark slot as unused
used[index] = false;
size--;
// If the next slot is also used, we need to handle the removal properly
// to maintain the open addressing property
// This rehashing serves as a "cleanup" after removal
if (size > 0) {
rehashFromIndex(index);
}
return true;
} finally {
lock.unlock();
}
}
void clear() {
lock.lock();
try {
Arrays.fill(used, false);
size = 0;
} finally {
lock.unlock();
}
}
int toLongArray(long[] array, int offset) {
lock.lock();
try {
for (int i = 0; i < keys.length; i++) {
if (used[i]) {
array[offset++] = keys[i];
}
}
return offset;
} finally {
lock.unlock();
}
}
int toObjectArray(Long[] array, int offset) {
lock.lock();
try {
for (int i = 0; i < keys.length; i++) {
if (used[i]) {
array[offset++] = keys[i];
}
}
return offset;
} finally {
lock.unlock();
}
}
boolean retainAll(LongCollection toRetain) {
lock.lock();
try {
boolean modified = false;
for (int i = 0; i < keys.length; i++) {
if (used[i] && !toRetain.contains(keys[i])) {
used[i] = false;
size--;
modified = true;
}
}
// Rehash to clean up if needed
if (modified && size > 0) {
rehash();
}
return modified;
} finally {
lock.unlock();
}
}
/**
* Find the index where a key should be stored.
* Uses linear probing for collision resolution.
*/
private int indexOf(long key) {
int mask = keys.length - 1;
int index = (int) (spread(key) & mask);
while (used[index] && keys[index] != key) {
index = (index + 1) & mask;
}
return index;
}
/**
* Rehash the segment with a larger capacity.
*/
private void rehash() {
int oldCapacity = keys.length;
int newCapacity = oldCapacity * 2;
long[] oldKeys = keys;
boolean[] oldUsed = used;
keys = new long[newCapacity];
used = new boolean[newCapacity];
size = 0;
threshold = (int) (newCapacity * loadFactor);
// Re-add all keys
for (int i = 0; i < oldCapacity; i++) {
if (oldUsed[i]) {
add(oldKeys[i]);
}
}
}
/**
* Rehash from a specific index after removal to maintain proper open addressing.
*/
private void rehashFromIndex(int startIndex) {
int mask = keys.length - 1;
int currentIndex = startIndex;
int nextIndex = (currentIndex + 1) & mask;
// For each cluster of used slots following the removal point
while (used[nextIndex]) {
long key = keys[nextIndex];
int targetIndex = (int) (spread(key) & mask);
// If the key's ideal position is between the removal point and the current position,
// move it to the removal point
if ((targetIndex <= currentIndex && currentIndex < nextIndex) ||
(nextIndex < targetIndex && targetIndex <= currentIndex) ||
(currentIndex < nextIndex && nextIndex < targetIndex)) {
keys[currentIndex] = keys[nextIndex];
used[currentIndex] = true;
used[nextIndex] = false;
currentIndex = nextIndex;
}
nextIndex = (nextIndex + 1) & mask;
}
}
@Override
public int hashCode() {
lock.lock();
try {
int hash = 0;
for (int i = 0; i < keys.length; i++) {
if (used[i]) {
hash += Long.hashCode(keys[i]);
}
}
return hash;
} finally {
lock.unlock();
}
}
}
/**
* Concurrent iterator for the set.
*/
private class ConcurrentLongIterator implements LongIterator {
private int segmentIndex;
private int keyIndex;
private long lastReturned;
private boolean lastReturnedValid;
ConcurrentLongIterator() {
segmentIndex = 0;
keyIndex = 0;
lastReturnedValid = false;
advance();
}
@Override
public boolean hasNext() {
return backing.hasNext();
return segmentIndex < segments.length;
}
@Override
public long nextLong() {
return backing.next();
if (!hasNext()) {
throw new java.util.NoSuchElementException();
}
lastReturned = segments[segmentIndex].keys[keyIndex];
lastReturnedValid = true;
advance();
return lastReturned;
}
@Override
public Long next() {
return backing.next();
return nextLong();
}
@Override
public void remove() {
backing.remove();
if (!lastReturnedValid) {
throw new IllegalStateException();
}
ConcurrentLongHashSet.this.remove(lastReturned);
lastReturnedValid = false;
}
private void advance() {
while (segmentIndex < segments.length) {
Segment segment = segments[segmentIndex];
// Lock the segment to get a consistent view
segment.lock.lock();
try {
while (keyIndex < segment.keys.length) {
if (segment.used[keyIndex]) {
// Found next element
return;
}
keyIndex++;
}
} finally {
segment.lock.unlock();
}
// Move to next segment
segmentIndex++;
keyIndex = 0;
}
}
}
/**
* Utility class for math operations.
*/
private static class MathUtil {
/**
* Returns the next power of two greater than or equal to the given value.
*/
static int nextPowerOfTwo(int value) {
int highestBit = Integer.highestOneBit(value);
return value > highestBit ? highestBit << 1 : value;
}
}
}