feat(furnidata): shared lock serializing watcher reindex and editor writes

This commit is contained in:
simoleo89
2026-06-06 01:57:48 +02:00
parent 258a95a269
commit caf6ad35fa
2 changed files with 35 additions and 17 deletions
@@ -0,0 +1,13 @@
package com.eu.habbo.habbohotel.items;
import java.util.concurrent.locks.ReentrantLock;
/**
* One process-wide lock serializing every furnidata reindex and every editor-driven
* furnidata write, so an editor write never races the file watcher's reindex and the
* volatile index is never observed mid-swap by two writers.
*/
public final class FurnidataLock {
public static final ReentrantLock LOCK = new ReentrantLock();
private FurnidataLock() {}
}
@@ -116,26 +116,31 @@ public class FurnidataWatcher {
} }
private void onChange() { private void onChange() {
Path source = this.provider.getSource(); FurnidataLock.LOCK.lock();
if (source == null) return; try {
Path source = this.provider.getSource();
if (source == null) return;
List<FurnidataEntry> delta = this.provider.reindex(new FurnidataReader(source, this.maxBytes).read()); List<FurnidataEntry> delta = this.provider.reindex(new FurnidataReader(source, this.maxBytes).read());
if (delta.isEmpty()) return; if (delta.isEmpty()) return;
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
if (now - this.lastBroadcast < this.minIntervalMs) { if (now - this.lastBroadcast < this.minIntervalMs) {
LOGGER.info("FurnidataWatcher: {} changes indexed but broadcast skipped (min interval) — clients update on next change or reconnect", delta.size()); LOGGER.info("FurnidataWatcher: {} changes indexed but broadcast skipped (min interval) — clients update on next change or reconnect", delta.size());
return; return;
}
this.lastBroadcast = now;
FurnitureDataReloadComposer composer = (delta.size() > this.deltaCap)
? new FurnitureDataReloadComposer(FurnitureDataReloadComposer.MODE_RELOAD_HINT, List.of())
: new FurnitureDataReloadComposer(FurnitureDataReloadComposer.MODE_DELTA, delta);
broadcast(composer);
LOGGER.info("FurnidataWatcher: broadcast {} ({} entries)",
delta.size() > this.deltaCap ? "reload-hint" : "delta", delta.size());
} finally {
FurnidataLock.LOCK.unlock();
} }
this.lastBroadcast = now;
FurnitureDataReloadComposer composer = (delta.size() > this.deltaCap)
? new FurnitureDataReloadComposer(FurnitureDataReloadComposer.MODE_RELOAD_HINT, List.of())
: new FurnitureDataReloadComposer(FurnitureDataReloadComposer.MODE_DELTA, delta);
broadcast(composer);
LOGGER.info("FurnidataWatcher: broadcast {} ({} entries)",
delta.size() > this.deltaCap ? "reload-hint" : "delta", delta.size());
} }
private void broadcast(FurnitureDataReloadComposer composer) { private void broadcast(FurnitureDataReloadComposer composer) {