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
+66 -25

No files matched your search

@@ -38,18 +38,26 @@ public class EntityUtils extends Queue {
id = ConfigHandler.entities.get(name);
}
else if (internal) {
// 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;
}
// Check if another server has already added this entity (multi-server setup)
id = ConfigHandler.reloadAndGetId(ConfigHandler.CacheType.ENTITIES, name);
if (id != -1) {
return id;
}
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);
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) {
// 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;
}
// Check if another server has already added this material (multi-server setup)
id = ConfigHandler.reloadAndGetId(ConfigHandler.CacheType.MATERIALS, name);
if (id != -1) {
return id;
}
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);
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) {
// 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;
}
// Check if another server has already added this blockdata (multi-server setup)
id = ConfigHandler.reloadAndGetId(ConfigHandler.CacheType.BLOCKDATA, data);
if (id != -1) {
return id;
}
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);
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) {
// 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;
}
// Check if another server has already added this art (multi-server setup)
id = ConfigHandler.reloadAndGetId(ConfigHandler.CacheType.ART, name);
if (id != -1) {
return id;
}
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);
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;
@@ -18,6 +18,10 @@ public class WorldUtils extends Queue {
}
int id = -1;
try {
if (ConfigHandler.worlds.get(name) == null) {
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);
@@ -25,12 +29,17 @@ public class WorldUtils extends Queue {
return id;
}
int wid = ConfigHandler.worldId + 1;
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);
}
catch (Exception e) {