Skip to content

Commit

Permalink
simplify cache impl
Browse files Browse the repository at this point in the history
now that highlights are rendered by accessing highlights snapshots, there is little need for r/w locking. synchronized maps perform just as well and remove the need for lock management
  • Loading branch information
rfresh2 committed Nov 21, 2024
1 parent 8effb80 commit d0ff3d3
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 128 deletions.
Original file line number Diff line number Diff line change
@@ -1,23 +1,13 @@
package xaeroplus.feature.render.highlights;

import it.unimi.dsi.fastutil.longs.Long2LongMap;
import it.unimi.dsi.fastutil.longs.Long2LongOpenHashMap;
import it.unimi.dsi.fastutil.longs.LongArrayList;
import it.unimi.dsi.fastutil.longs.LongList;
import it.unimi.dsi.fastutil.longs.*;
import net.minecraft.resources.ResourceKey;
import net.minecraft.world.level.Level;
import xaeroplus.XaeroPlus;
import xaeroplus.util.ChunkUtils;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.StampedLock;

import static xaeroplus.util.ChunkUtils.chunkPosToLong;

public abstract class ChunkHighlightBaseCacheHandler implements ChunkHighlightCache {
public final ReadWriteLock lock = new StampedLock().asReadWriteLock();
public final Long2LongMap chunks = new Long2LongOpenHashMap();
public final Long2LongMap chunks = Long2LongMaps.synchronize(new Long2LongOpenHashMap());

@Override
public boolean addHighlight(final int x, final int z) {
Expand All @@ -26,28 +16,14 @@ public boolean addHighlight(final int x, final int z) {

public boolean addHighlight(final int x, final int z, final long foundTime) {
final long chunkPos = chunkPosToLong(x, z);
try {
if (lock.writeLock().tryLock(1, TimeUnit.SECONDS)) {
chunks.put(chunkPos, foundTime);
lock.writeLock().unlock();
}
} catch (final Exception e) {
XaeroPlus.LOGGER.error("Failed to add new highlight: {}, {}", x, z, e);
}
chunks.put(chunkPos, foundTime);
return true;
}

@Override
public boolean removeHighlight(final int x, final int z) {
final long chunkPos = chunkPosToLong(x, z);
try {
if (lock.writeLock().tryLock(1, TimeUnit.SECONDS)) {
chunks.remove(chunkPos);
lock.writeLock().unlock();
}
} catch (final Exception e) {
XaeroPlus.LOGGER.error("Failed to add new highlight: {}, {}", x, z, e);
}
chunks.remove(chunkPos);
return true;
}

Expand All @@ -58,26 +34,11 @@ public boolean isHighlighted(final int x, final int z, ResourceKey<Level> dimens

@Override
public LongList getHighlightsSnapshot(final ResourceKey<Level> dimension) {
try {
if (lock.readLock().tryLock(1, TimeUnit.SECONDS)) {
// copy is memory inefficient but we need a thread safe iterator for rendering
var list = new LongArrayList(chunks.keySet());
lock.readLock().unlock();
return list;
}
} catch (final Exception e) {
XaeroPlus.LOGGER.error("Error getting highlights snapshot in dimension: {}", dimension.location().getPath(), e);
}
return LongList.of();
return new LongArrayList(chunks.keySet());
}

public boolean isHighlighted(final long chunkPos) {
try {
return chunks.containsKey(chunkPos);
} catch (final Exception e) {
XaeroPlus.LOGGER.error("Error checking if chunk is highlighted: {}, {}", ChunkUtils.longToChunkX(chunkPos), ChunkUtils.longToChunkZ(chunkPos), e);
}
return false;
return chunks.containsKey(chunkPos);
}

@Override
Expand All @@ -88,36 +49,15 @@ public Long2LongMap getHighlightsState() {
@Override
public void loadPreviousState(final Long2LongMap state) {
if (state == null) return;
try {
if (lock.writeLock().tryLock(1, TimeUnit.SECONDS)) {
chunks.putAll(state);
lock.writeLock().unlock();
}
} catch (final Exception e) {
XaeroPlus.LOGGER.error("Error loading previous highlight cache state", e);
}
chunks.putAll(state);
}

public void replaceState(final Long2LongOpenHashMap state) {
try {
if (lock.writeLock().tryLock(1, TimeUnit.SECONDS)) {
this.chunks.clear();
this.chunks.putAll(state);
lock.writeLock().unlock();
}
} catch (final Exception e) {
XaeroPlus.LOGGER.error("Failed replacing highlight cache state", e);
}
chunks.clear();
chunks.putAll(state);
}

public void reset() {
try {
if (lock.writeLock().tryLock(1, TimeUnit.SECONDS)) {
chunks.clear();
lock.writeLock().unlock();
}
} catch (final Exception e) {
XaeroPlus.LOGGER.error("Failed resetting highlight cache", e);
}
chunks.clear();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,10 @@
import net.minecraft.resources.ResourceKey;
import net.minecraft.world.level.Level;
import org.jetbrains.annotations.NotNull;
import xaeroplus.XaeroPlus;
import xaeroplus.util.ChunkUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

import static xaeroplus.util.ChunkUtils.chunkPosToLong;
import static xaeroplus.util.ChunkUtils.regionCoordToChunkCoord;
Expand Down Expand Up @@ -51,46 +49,36 @@ private void loadHighlightsInWindow() {
windowRegionX - windowRegionSize, windowRegionX + windowRegionSize,
windowRegionZ - windowRegionSize, windowRegionZ + windowRegionSize
);
try {
if (lock.writeLock().tryLock(1, TimeUnit.SECONDS)) {
for (int i = 0; i < chunks.size(); i++) {
final ChunkHighlightData chunk = chunks.get(i);
this.chunks.put(chunkPosToLong(chunk.x(), chunk.z()), chunk.foundTime());
}
lock.writeLock().unlock();
synchronized (this.chunks) {
for (int i = 0; i < chunks.size(); i++) {
final ChunkHighlightData chunk = chunks.get(i);
this.chunks.put(chunkPosToLong(chunk.x(), chunk.z()), chunk.foundTime());
}
} catch (final Exception e) {
XaeroPlus.LOGGER.error("Failed to load highlights in window for {} disk cache dimension: {}", database.databaseName, dimension.location(), e);
}
});
}

private void writeHighlightsOutsideWindowToDatabase() {
executorService.execute(() -> {
final List<ChunkHighlightData> chunksToWrite = new ArrayList<>();
try {
if (lock.writeLock().tryLock(1L, TimeUnit.SECONDS)) {
var minChunkX = regionCoordToChunkCoord(windowRegionX - windowRegionSize);
var maxChunkX = regionCoordToChunkCoord(windowRegionX + windowRegionSize);
var minChunkZ = regionCoordToChunkCoord(windowRegionZ - windowRegionSize);
var maxChunkZ = regionCoordToChunkCoord(windowRegionZ + windowRegionSize);
for (var it = chunks.long2LongEntrySet().iterator(); it.hasNext(); ) {
var entry = it.next();
final long chunkPos = entry.getLongKey();
final int chunkX = ChunkUtils.longToChunkX(chunkPos);
final int chunkZ = ChunkUtils.longToChunkZ(chunkPos);
if (chunkX < minChunkX
|| chunkX > maxChunkX
|| chunkZ < minChunkZ
|| chunkZ > maxChunkZ) {
chunksToWrite.add(new ChunkHighlightData(chunkX, chunkZ, entry.getLongValue()));
it.remove();
}
var minChunkX = regionCoordToChunkCoord(windowRegionX - windowRegionSize);
var maxChunkX = regionCoordToChunkCoord(windowRegionX + windowRegionSize);
var minChunkZ = regionCoordToChunkCoord(windowRegionZ - windowRegionSize);
var maxChunkZ = regionCoordToChunkCoord(windowRegionZ + windowRegionSize);
synchronized (this.chunks) {
for (var it = chunks.long2LongEntrySet().iterator(); it.hasNext(); ) {
var entry = it.next();
final long chunkPos = entry.getLongKey();
final int chunkX = ChunkUtils.longToChunkX(chunkPos);
final int chunkZ = ChunkUtils.longToChunkZ(chunkPos);
if (chunkX < minChunkX
|| chunkX > maxChunkX
|| chunkZ < minChunkZ
|| chunkZ > maxChunkZ) {
chunksToWrite.add(new ChunkHighlightData(chunkX, chunkZ, entry.getLongValue()));
it.remove();
}
lock.writeLock().unlock();
}
} catch (final Exception e) {
XaeroPlus.LOGGER.error("Error while writing highlights outside window to {} disk cache dimension: {}", database.databaseName, dimension.location(), e);
}
database.insertHighlightList(chunksToWrite, dimension);
});
Expand All @@ -99,19 +87,14 @@ private void writeHighlightsOutsideWindowToDatabase() {
public ListenableFuture<?> writeAllHighlightsToDatabase() {
return executorService.submit(() -> {
final List<ChunkHighlightData> chunksToWrite = new ArrayList<>(chunks.size());
try {
if (lock.readLock().tryLock(1, TimeUnit.SECONDS)) {
for (var it = chunks.long2LongEntrySet().iterator(); it.hasNext(); ) {
var entry = it.next();
final long chunkPos = entry.getLongKey();
final int chunkX = ChunkUtils.longToChunkX(chunkPos);
final int chunkZ = ChunkUtils.longToChunkZ(chunkPos);
chunksToWrite.add(new ChunkHighlightData(chunkX, chunkZ, entry.getLongValue()));
}
lock.readLock().unlock();
synchronized (chunks) {
for (var it = chunks.long2LongEntrySet().iterator(); it.hasNext(); ) {
var entry = it.next();
final long chunkPos = entry.getLongKey();
final int chunkX = ChunkUtils.longToChunkX(chunkPos);
final int chunkZ = ChunkUtils.longToChunkZ(chunkPos);
chunksToWrite.add(new ChunkHighlightData(chunkX, chunkZ, entry.getLongValue()));
}
} catch (final Exception e) {
XaeroPlus.LOGGER.error("Error while writing all chunks to {} disk cache dimension: {}", database.databaseName, dimension.location(), e);
}
database.insertHighlightList(chunksToWrite, dimension);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

import static xaeroplus.util.ChunkUtils.regionCoordToChunkCoord;

Expand Down Expand Up @@ -84,12 +83,16 @@ public void insertHighlightList(final List<ChunkHighlightData> chunks, final Res

private void insertHighlightsListInternal(final List<ChunkHighlightData> chunks, final ResourceKey<Level> dimension) {
try {
String statement = "INSERT OR IGNORE INTO \"" + getTableName(dimension) + "\" VALUES ";
statement += chunks.stream()
.map(chunk -> "(" + chunk.x() + ", " + chunk.z() + ", " + chunk.foundTime() + ")")
.collect(Collectors.joining(", "));
StringBuilder sb = new StringBuilder("INSERT OR IGNORE INTO \"" + getTableName(dimension) + "\" VALUES ");
for (int i = 0; i < chunks.size(); i++) {
ChunkHighlightData chunk = chunks.get(i);
sb.append("(").append(chunk.x()).append(", ").append(chunk.z()).append(", ").append(chunk.foundTime()).append(")");
if (i < chunks.size() - 1) {
sb.append(", ");
}
}
try (var stmt = connection.createStatement()) {
stmt.executeUpdate(statement);
stmt.executeUpdate(sb.toString());
}
} catch (Exception e) {
XaeroPlus.LOGGER.error("Error inserting {} chunks into {} database in dimension: {}", chunks.size(), databaseName, dimension.location(), e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import xaeroplus.XaeroPlus;

import java.util.Map;
import java.util.concurrent.TimeUnit;

public class ChunkHighlightLocalCache extends ChunkHighlightBaseCacheHandler {
private static final int maxNumber = 5000;
Expand All @@ -26,19 +25,15 @@ public boolean addHighlight(final int x, final int z, final long foundTime) {
private void limitChunksSize() {
try {
if (chunks.size() > maxNumber) {
if (lock.readLock().tryLock(1, TimeUnit.SECONDS)) {
synchronized (chunks) {
// remove oldest 500 chunks
var toRemove = chunks.long2LongEntrySet().stream()
.sorted(Map.Entry.comparingByValue())
.limit(500)
.mapToLong(Long2LongMap.Entry::getLongKey)
.toArray();
lock.readLock().unlock();
if (lock.writeLock().tryLock(1, TimeUnit.SECONDS)) {
for (int i = 0; i < toRemove.length; i++) {
chunks.remove(toRemove[i]);
}
lock.writeLock().unlock();
for (int i = 0; i < toRemove.length; i++) {
chunks.remove(toRemove[i]);
}
}
}
Expand Down

0 comments on commit d0ff3d3

Please sign in to comment.