fix: vastly superier handling of queue chunks (#2461)

- remove ChunkHolder locking concept as this is no longer needed
 - previously we obtained the copy from chunk GET on finalize, meaning the copy could be replaced by a "newer" one (bad)
 - work around this issue by introducing concept of "unique" keys to map chunk GET copies to
 - correctly handle resetting of various chunk-related classes to actually allow pooling to work
 - remove chunks as they are submitted when flushing a SingleThreadQueueExtenting
This commit is contained in:
Jordan
2023-10-22 11:04:19 +01:00
committed by GitHub
parent 9489e5448f
commit f5803a09f6
17 changed files with 275 additions and 152 deletions

View File

@ -75,9 +75,11 @@ import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Future;
import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Function;
import java.util.stream.Collectors;
@ -95,6 +97,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
.getInstance()
.getBukkitImplAdapter());
private final ReadWriteLock sectionLock = new ReentrantReadWriteLock();
private final ReentrantLock callLock = new ReentrantLock();
private final ServerLevel serverLevel;
private final int chunkX;
private final int chunkZ;
@ -104,15 +107,16 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
private final int maxSectionPosition;
private final Registry<Biome> biomeRegistry;
private final IdMap<Holder<Biome>> biomeHolderIdMap;
private final ConcurrentHashMap<Integer, PaperweightGetBlocks_Copy> copies = new ConcurrentHashMap<>();
private final Object sendLock = new Object();
private LevelChunkSection[] sections;
private LevelChunk levelChunk;
private DataLayer[] blockLight;
private DataLayer[] skyLight;
private boolean createCopy = false;
private PaperweightGetBlocks_Copy copy = null;
private boolean forceLoadSections = true;
private boolean lightUpdate = false;
private int copyKey = 0;
public PaperweightGetBlocks(World world, int chunkX, int chunkZ) {
this(((CraftWorld) world).getHandle(), chunkX, chunkZ);
@ -147,13 +151,27 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
}
@Override
public void setCreateCopy(boolean createCopy) {
public int setCreateCopy(boolean createCopy) {
if (!callLock.isHeldByCurrentThread()) {
throw new IllegalStateException("Attempting to set if chunk GET should create copy, but it is not call-locked.");
}
this.createCopy = createCopy;
return ++this.copyKey;
}
@Override
public IChunkGet getCopy() {
return copy;
public IChunkGet getCopy(final int key) {
return copies.remove(key);
}
@Override
public void lockCall() {
this.callLock.lock();
}
@Override
public void unlockCall() {
this.callLock.unlock();
}
@Override
@ -389,8 +407,17 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
@Override
@SuppressWarnings("rawtypes")
public synchronized <T extends Future<T>> T call(IChunkSet set, Runnable finalizer) {
if (!callLock.isHeldByCurrentThread()) {
throw new IllegalStateException("Attempted to call chunk GET but chunk was not call-locked.");
}
forceLoadSections = false;
copy = createCopy ? new PaperweightGetBlocks_Copy(getChunk()) : null;
PaperweightGetBlocks_Copy copy = createCopy ? new PaperweightGetBlocks_Copy(levelChunk) : null;
if (createCopy) {
if (copies.containsKey(copyKey)) {
throw new IllegalStateException("Copy key already used.");
}
copies.put(copyKey, copy);
}
try {
ServerLevel nmsWorld = serverLevel;
LevelChunk nmsChunk = ensureLoaded(nmsWorld, chunkX, chunkZ);
@ -883,9 +910,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
if (super.sections[layer] != null) {
synchronized (super.sectionLocks[layer]) {
if (super.sections[layer].isFull() && super.blocks[layer] != null) {
char[] blocks = new char[4096];
System.arraycopy(super.blocks[layer], 0, blocks, 0, 4096);
return blocks;
return super.blocks[layer];
}
}
}
@ -1007,9 +1032,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
public LevelChunkSection[] getSections(boolean force) {
force &= forceLoadSections;
sectionLock.readLock().lock();
LevelChunkSection[] tmp = sections;
sectionLock.readLock().unlock();
if (tmp == null || force) {
try {
sectionLock.writeLock().lock();

View File

@ -26,6 +26,7 @@ import net.minecraft.world.level.chunk.PalettedContainerRO;
import org.apache.logging.log4j.Logger;
import javax.annotation.Nullable;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@ -105,7 +106,8 @@ public class PaperweightGetBlocks_Copy implements IChunkGet {
}
@Override
public void setCreateCopy(boolean createCopy) {
public int setCreateCopy(boolean createCopy) {
return -1;
}
@Override
@ -199,6 +201,10 @@ public class PaperweightGetBlocks_Copy implements IChunkGet {
@Override
public char[] load(int layer) {
layer -= getMinSectionPosition();
if (blocks[layer] == null) {
blocks[layer] = new char[4096];
Arrays.fill(blocks[layer], (char) BlockTypesCache.ReservedIDs.AIR);
}
return blocks[layer];
}