Skip to content

Commit

Permalink
fix: vastly superier handling of queue chunks
Browse files Browse the repository at this point in the history
 - 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
 - Fixes #2459
 - Addresses #2448
 - Fixes #2388
  • Loading branch information
dordsor21 committed Oct 14, 2023
1 parent 186d24b commit e21d913
Show file tree
Hide file tree
Showing 17 changed files with 275 additions and 152 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,11 @@
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;
Expand All @@ -91,21 +93,23 @@ 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;
private final int minHeight;
private final int maxHeight;
private final int minSectionPosition;
private final int maxSectionPosition;
private final ConcurrentHashMap<Integer, PaperweightGetBlocks_Copy> copies = new ConcurrentHashMap<>();
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);
Expand Down Expand Up @@ -138,13 +142,27 @@ public boolean isCreateCopy() {
}

@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(final int key) {
return copies.remove(key);
}

@Override
public void lockCall() {
this.callLock.lock();
}

@Override
public IChunkGet getCopy() {
return copy;
public void unlockCall() {
this.callLock.unlock();
}

@Override
Expand Down Expand Up @@ -393,8 +411,17 @@ public LevelChunk ensureLoaded(ServerLevel nmsWorld, int chunkX, int chunkZ) {
@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);
Expand Down Expand Up @@ -831,9 +858,7 @@ private char[] loadPrivately(int layer) {
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];
}
}
}
Expand Down Expand Up @@ -946,9 +971,7 @@ private char ordinal(net.minecraft.world.level.block.state.BlockState ibd, Paper

public LevelChunkSection[] getSections(boolean force) {
force &= forceLoadSections;
sectionLock.readLock().lock();
LevelChunkSection[] tmp = sections;
sectionLock.readLock().unlock();
if (tmp == null || force) {
try {
sectionLock.writeLock().lock();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import net.minecraft.world.level.chunk.LevelChunk;

import javax.annotation.Nullable;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
Expand Down Expand Up @@ -99,7 +100,8 @@ public boolean isCreateCopy() {
}

@Override
public void setCreateCopy(boolean createCopy) {
public int setCreateCopy(boolean createCopy) {
return -1L;
}

@Override
Expand Down Expand Up @@ -196,6 +198,10 @@ public boolean hasSection(int layer) {
@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];
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
import com.fastasyncworldedit.core.util.MathMan;
import com.fastasyncworldedit.core.util.collection.AdaptedMap;
import com.google.common.base.Suppliers;
import com.google.common.collect.Iterables;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.ListTag;
import com.sk89q.jnbt.StringTag;
Expand Down Expand Up @@ -66,7 +65,6 @@
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
Expand All @@ -76,13 +74,14 @@
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;
import java.util.stream.StreamSupport;

public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBlocks {

Expand All @@ -95,6 +94,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;
Expand All @@ -104,14 +104,15 @@ 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 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);
Expand Down Expand Up @@ -146,13 +147,27 @@ public boolean isCreateCopy() {
}

@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(final int key) {
return copies.remove(key);
}

@Override
public void lockCall() {
this.callLock.lock();
}

@Override
public IChunkGet getCopy() {
return copy;
public void unlockCall() {
this.callLock.unlock();
}

@Override
Expand Down Expand Up @@ -387,8 +402,17 @@ public LevelChunk ensureLoaded(ServerLevel nmsWorld, int chunkX, int chunkZ) {
@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);
Expand Down Expand Up @@ -882,9 +906,7 @@ private char[] loadPrivately(int layer) {
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];
}
}
}
Expand Down Expand Up @@ -1004,9 +1026,7 @@ private char ordinal(BlockState ibd, PaperweightFaweAdapter adapter) {

public LevelChunkSection[] getSections(boolean force) {
force &= forceLoadSections;
sectionLock.readLock().lock();
LevelChunkSection[] tmp = sections;
sectionLock.readLock().unlock();
if (tmp == null || force) {
try {
sectionLock.writeLock().lock();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_18_R2.nbt.PaperweightLazyCompoundTag;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.biome.BiomeTypes;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockTypesCache;
Expand All @@ -24,6 +23,7 @@
import net.minecraft.world.level.chunk.PalettedContainer;

import javax.annotation.Nullable;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
Expand Down Expand Up @@ -101,7 +101,8 @@ public boolean isCreateCopy() {
}

@Override
public void setCreateCopy(boolean createCopy) {
public int setCreateCopy(boolean createCopy) {
return -1L;
}

@Override
Expand Down Expand Up @@ -187,6 +188,10 @@ public boolean hasSection(int layer) {
@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];
}

Expand Down
Loading

0 comments on commit e21d913

Please sign in to comment.