Allocate world, material, blockdata, art and entity ids under one lock (#1009)

The id allocators read the counter, add one and store it without a lock, so two threads that see new names at the same time (region threads on Folia, lookup and API threads everywhere) can give the same id to two different names. Allocation now holds the ConfigHandler monitor reloadAndGetId already uses, with a re-check, and queues the insert after releasing it.
This commit is contained in:
Sage Kummer authored and GitHub committed 2026-09-25 14:56:30 -06:00
1 parent 96eec2b768
commit 97fced4cb6
3 files changed
+95 -54

No files matched your search

@@ -38,18 +38,26 @@ public class EntityUtils extends Queue {
id = ConfigHandler.entities.get(name);
}
else if (internal) {
// Check if another server has already added this entity (multi-server setup)
id = ConfigHandler.reloadAndGetId(ConfigHandler.CacheType.ENTITIES, name);
if (id != -1) {
return id;
}
// Same monitor as reloadAndGetId, so two threads cannot allocate the same id
synchronized (ConfigHandler.class) {
Integer existing = ConfigHandler.entities.get(name);
if (existing != null) {
return existing;
}
int entityID = ConfigHandler.entityId + 1;
ConfigHandler.entities.put(name, entityID);
ConfigHandler.entitiesReversed.put(entityID, name);
ConfigHandler.entityId = entityID;
Queue.queueEntityInsert(entityID, name);
id = ConfigHandler.entities.get(name);
// Check if another server has already added this entity (multi-server setup)
id = ConfigHandler.reloadAndGetId(ConfigHandler.CacheType.ENTITIES, name);
if (id != -1) {
return id;
}
id = ConfigHandler.entityId + 1;
ConfigHandler.entities.put(name, id);
ConfigHandler.entitiesReversed.put(id, name);
ConfigHandler.entityId = id;
}
// Queued outside the monitor so it is never held while the queue lock is taken
Queue.queueEntityInsert(id, name);
}
return id;
@@ -48,18 +48,26 @@ public class MaterialUtils extends Queue {
id = ConfigHandler.materials.get(name);
}
else if (internal) {
// Check if another server has already added this material (multi-server setup)
id = ConfigHandler.reloadAndGetId(ConfigHandler.CacheType.MATERIALS, name);
if (id != -1) {
return id;
}
// Same monitor as reloadAndGetId, so two threads cannot allocate the same id
synchronized (ConfigHandler.class) {
Integer existing = ConfigHandler.materials.get(name);
if (existing != null) {
return existing;
}
int mid = ConfigHandler.materialId + 1;
ConfigHandler.materials.put(name, mid);
ConfigHandler.materialsReversed.put(mid, name);
ConfigHandler.materialId = mid;
Queue.queueMaterialInsert(mid, name);
id = ConfigHandler.materials.get(name);
// Check if another server has already added this material (multi-server setup)
id = ConfigHandler.reloadAndGetId(ConfigHandler.CacheType.MATERIALS, name);
if (id != -1) {
return id;
}
id = ConfigHandler.materialId + 1;
ConfigHandler.materials.put(name, id);
ConfigHandler.materialsReversed.put(id, name);
ConfigHandler.materialId = id;
}
// Queued outside the monitor so it is never held while the queue lock is taken
Queue.queueMaterialInsert(id, name);
}
return id;
@@ -76,18 +84,26 @@ public class MaterialUtils extends Queue {
id = ConfigHandler.blockdata.get(data);
}
else if (internal) {
// Check if another server has already added this blockdata (multi-server setup)
id = ConfigHandler.reloadAndGetId(ConfigHandler.CacheType.BLOCKDATA, data);
if (id != -1) {
return id;
}
// Same monitor as reloadAndGetId, so two threads cannot allocate the same id
synchronized (ConfigHandler.class) {
Integer existing = ConfigHandler.blockdata.get(data);
if (existing != null) {
return existing;
}
int bid = ConfigHandler.blockdataId + 1;
ConfigHandler.blockdata.put(data, bid);
ConfigHandler.blockdataReversed.put(bid, data);
ConfigHandler.blockdataId = bid;
Queue.queueBlockDataInsert(bid, data);
id = ConfigHandler.blockdata.get(data);
// Check if another server has already added this blockdata (multi-server setup)
id = ConfigHandler.reloadAndGetId(ConfigHandler.CacheType.BLOCKDATA, data);
if (id != -1) {
return id;
}
id = ConfigHandler.blockdataId + 1;
ConfigHandler.blockdata.put(data, id);
ConfigHandler.blockdataReversed.put(id, data);
ConfigHandler.blockdataId = id;
}
// Queued outside the monitor so it is never held while the queue lock is taken
Queue.queueBlockDataInsert(id, data);
}
return id;
@@ -189,18 +205,26 @@ public class MaterialUtils extends Queue {
id = ConfigHandler.art.get(name);
}
else if (internal) {
// Check if another server has already added this art (multi-server setup)
id = ConfigHandler.reloadAndGetId(ConfigHandler.CacheType.ART, name);
if (id != -1) {
return id;
}
// Same monitor as reloadAndGetId, so two threads cannot allocate the same id
synchronized (ConfigHandler.class) {
Integer existing = ConfigHandler.art.get(name);
if (existing != null) {
return existing;
}
int artID = ConfigHandler.artId + 1;
ConfigHandler.art.put(name, artID);
ConfigHandler.artReversed.put(artID, name);
ConfigHandler.artId = artID;
Queue.queueArtInsert(artID, name);
id = ConfigHandler.art.get(name);
// Check if another server has already added this art (multi-server setup)
id = ConfigHandler.reloadAndGetId(ConfigHandler.CacheType.ART, name);
if (id != -1) {
return id;
}
id = ConfigHandler.artId + 1;
ConfigHandler.art.put(name, id);
ConfigHandler.artReversed.put(id, name);
ConfigHandler.artId = id;
}
// Queued outside the monitor so it is never held while the queue lock is taken
Queue.queueArtInsert(id, name);
}
return id;
@@ -19,17 +19,26 @@ public class WorldUtils extends Queue {
int id = -1;
try {
if (ConfigHandler.worlds.get(name) == null) {
// Check if another server has already added this world (multi-server setup)
id = ConfigHandler.reloadAndGetId(ConfigHandler.CacheType.WORLDS, name);
if (id != -1) {
return id;
}
int wid = -1;
// Same monitor as reloadAndGetId, so two threads cannot allocate the same id
synchronized (ConfigHandler.class) {
if (ConfigHandler.worlds.get(name) == null) {
// Check if another server has already added this world (multi-server setup)
id = ConfigHandler.reloadAndGetId(ConfigHandler.CacheType.WORLDS, name);
if (id != -1) {
return id;
}
int wid = ConfigHandler.worldId + 1;
ConfigHandler.worlds.put(name, wid);
ConfigHandler.worldsReversed.put(wid, name);
ConfigHandler.worldId = wid;
Queue.queueWorldInsert(wid, name);
wid = ConfigHandler.worldId + 1;
ConfigHandler.worlds.put(name, wid);
ConfigHandler.worldsReversed.put(wid, name);
ConfigHandler.worldId = wid;
}
}
// Queued outside the monitor so it is never held while the queue lock is taken
if (wid != -1) {
Queue.queueWorldInsert(wid, name);
}
}
id = ConfigHandler.worlds.get(name);
}