Impl Thread Safety for static methods in EcsWorld

This commit is contained in:
Mikhail 2024-09-09 14:25:32 +08:00
parent 44a8894d65
commit effa02e660
2 changed files with 127 additions and 99 deletions

View File

@ -134,77 +134,83 @@ namespace DCFApixels.DragonECS
public EcsWorld(EcsWorldConfig config, short worldID = -1) : this(config == null ? ConfigContainer.Empty : new ConfigContainer().Set(config), worldID) { } public EcsWorld(EcsWorldConfig config, short worldID = -1) : this(config == null ? ConfigContainer.Empty : new ConfigContainer().Set(config), worldID) { }
public EcsWorld(IConfigContainer configs = null, short worldID = -1) public EcsWorld(IConfigContainer configs = null, short worldID = -1)
{ {
if (configs == null) { configs = ConfigContainer.Empty; } lock (_lock)
bool nullWorld = this is NullWorld;
if (nullWorld == false && worldID == NULL_WORLD_ID)
{ {
EcsDebug.PrintWarning($"The world identifier cannot be {NULL_WORLD_ID}"); if (configs == null) { configs = ConfigContainer.Empty; }
} bool nullWorld = this is NullWorld;
_configs = configs; if (nullWorld == false && worldID == NULL_WORLD_ID)
EcsWorldConfig config = configs.GetWorldConfigOrDefault();
if (worldID < 0 || (worldID == NULL_WORLD_ID && nullWorld == false))
{
worldID = (short)_worldIdDispenser.UseFree();
}
else
{
if (worldID != _worldIdDispenser.NullID)
{ {
_worldIdDispenser.Use(worldID); EcsDebug.PrintWarning($"The world identifier cannot be {NULL_WORLD_ID}");
} }
if (_worlds[worldID] != null) _configs = configs;
EcsWorldConfig config = configs.GetWorldConfigOrDefault();
if (worldID < 0 || (worldID == NULL_WORLD_ID && nullWorld == false))
{ {
_worldIdDispenser.Release(worldID); worldID = (short)_worldIdDispenser.UseFree();
Throw.Exception("The world with the specified ID has already been created\r\n");
} }
else
{
if (worldID != _worldIdDispenser.NullID)
{
_worldIdDispenser.Use(worldID);
}
if (_worlds[worldID] != null)
{
_worldIdDispenser.Release(worldID);
Throw.Exception("The world with the specified ID has already been created\r\n");
}
}
id = worldID;
_worlds[worldID] = this;
_poolsMediator = new PoolsMediator(this);
_executorsMediator = new ExecutorMediator(this);
int poolsCapacity = ArrayUtility.NormalizeSizeToPowerOfTwo(config.PoolsCapacity);
_pools = new IEcsPoolImplementation[poolsCapacity];
_poolSlots = new PoolSlot[poolsCapacity];
ArrayUtility.Fill(_pools, _nullPool);
int entitiesCapacity = ArrayUtility.NormalizeSizeToPowerOfTwo(config.EntitiesCapacity);
_entityDispenser = new IdDispenser(entitiesCapacity, 0, OnEntityDispenserResized);
GetComponentTypeID<NullComponent>();
} }
id = worldID;
_worlds[worldID] = this;
_poolsMediator = new PoolsMediator(this);
_executorsMediator = new ExecutorMediator(this);
int poolsCapacity = ArrayUtility.NormalizeSizeToPowerOfTwo(config.PoolsCapacity);
_pools = new IEcsPoolImplementation[poolsCapacity];
_poolSlots = new PoolSlot[poolsCapacity];
ArrayUtility.Fill(_pools, _nullPool);
int entitiesCapacity = ArrayUtility.NormalizeSizeToPowerOfTwo(config.EntitiesCapacity);
_entityDispenser = new IdDispenser(entitiesCapacity, 0, OnEntityDispenserResized);
GetComponentTypeID<NullComponent>();
} }
public void Destroy() public void Destroy()
{ {
if (_isDestroyed) lock (_lock)
{
EcsDebug.PrintWarning("The world is already destroyed");
return;
}
if (id == NULL_WORLD_ID)
{ {
if (_isDestroyed)
{
EcsDebug.PrintWarning("The world is already destroyed");
return;
}
if (id == NULL_WORLD_ID)
{
#if (DEBUG && !DISABLE_DEBUG) #if (DEBUG && !DISABLE_DEBUG)
Throw.World_WorldCantBeDestroyed(); Throw.World_WorldCantBeDestroyed();
#endif #endif
return; return;
} }
_listeners.InvokeOnWorldDestroy(); _listeners.InvokeOnWorldDestroy();
_entityDispenser = null; _entityDispenser = null;
_pools = null; _pools = null;
_nullPool = null; _nullPool = null;
_worlds[id] = null; _worlds[id] = null;
ReleaseData(id); ReleaseData(id);
_worldIdDispenser.Release(id); _worldIdDispenser.Release(id);
_isDestroyed = true; _isDestroyed = true;
_poolTypeCode_2_CmpTypeIDs = null; _poolTypeCode_2_CmpTypeIDs = null;
_cmpTypeCode_2_CmpTypeIDs = null; _cmpTypeCode_2_CmpTypeIDs = null;
foreach (var item in _executorCoures) foreach (var item in _executorCoures)
{ {
item.Value.Destroy(); item.Value.Destroy();
}
//_entities - не обнуляется для работы entlong.IsAlive
} }
//_entities - не обнуляется для работы entlong.IsAlive
} }
//public void Clear() { } //public void Clear() { }
#endregion #endregion

View File

@ -26,29 +26,30 @@ namespace DCFApixels.DragonECS
private const int DEL_ENT_BUFFER_MIN_SIZE = 64; private const int DEL_ENT_BUFFER_MIN_SIZE = 64;
private static EcsWorld[] _worlds = Array.Empty<EcsWorld>(); private static EcsWorld[] _worlds = Array.Empty<EcsWorld>();
private static IdDispenser _worldIdDispenser = new IdDispenser(4, 0, OnWorldIdDispenser); private static IdDispenser _worldIdDispenser = new IdDispenser(4, 0, n => Array.Resize(ref _worlds, n));
private static List<DataReleaser> _dataReleaseres = new List<DataReleaser>(); private static List<DataReleaser> _dataReleaseres = new List<DataReleaser>();
//public static int Copacity => Worlds.Length; //public static int Copacity => Worlds.Length;
private static readonly object _lock = new object();
static EcsWorld() static EcsWorld()
{ {
_worlds[NULL_WORLD_ID] = new NullWorld(); _worlds[NULL_WORLD_ID] = new NullWorld();
} }
private static void ReleaseData(int worldID) private static void ReleaseData(int worldID)
{ {// ts
for (int i = 0, iMax = _dataReleaseres.Count; i < iMax; i++) lock (_lock)
{ {
_dataReleaseres[i].Release(worldID); for (int i = 0, iMax = _dataReleaseres.Count; i < iMax; i++)
{
_dataReleaseres[i].Release(worldID);
}
} }
} }
public static void OnWorldIdDispenser(int newSize)
{
Array.Resize(ref _worlds, newSize);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static EcsWorld GetWorld(short worldID) public static EcsWorld GetWorld(short worldID)
{ {// ts
return _worlds[worldID]; return _worlds[worldID];
} }
@ -63,6 +64,7 @@ namespace DCFApixels.DragonECS
return ref WorldComponentPool<T>.GetForWorldUnchecked(worldID); return ref WorldComponentPool<T>.GetForWorldUnchecked(worldID);
} }
private abstract class DataReleaser private abstract class DataReleaser
{ {
public abstract void Release(int worldID); public abstract void Release(int worldID);
@ -76,64 +78,84 @@ namespace DCFApixels.DragonECS
private static short _recycledItemsCount; private static short _recycledItemsCount;
private static IEcsWorldComponent<T> _interface = EcsWorldComponentHandler<T>.instance; private static IEcsWorldComponent<T> _interface = EcsWorldComponentHandler<T>.instance;
private static readonly object _lock = new object();
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T Get(int itemIndex) public static ref T Get(int itemIndex)
{ {// ts
return ref _items[itemIndex]; return ref _items[itemIndex];
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T GetForWorld(int worldID) public static ref T GetForWorld(int worldID)
{ {// зависит от GetItemIndex
int index = GetItemIndex(worldID); return ref Get(GetItemIndex(worldID));
return ref _items[index];
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T GetForWorldUnchecked(int worldID) public static ref T GetForWorldUnchecked(int worldID)
{ {// ts
#if (DEBUG && !DISABLE_DEBUG) #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
if (_mapping[worldID] <= 0) { Throw.UndefinedException(); } if (_mapping[worldID] <= 0) { Throw.UndefinedException(); }
#endif #endif
return ref _items[_mapping[worldID]]; return ref _items[_mapping[worldID]];
} }
public static int GetItemIndex(int worldID) public static int GetItemIndex(int worldID)
{ {// ts
if (_mapping.Length < _worlds.Length) if (_mapping.Length < _worlds.Length)
{ {
Array.Resize(ref _mapping, _worlds.Length); lock (_lock)
{
if (_mapping.Length < _worlds.Length)
{
Array.Resize(ref _mapping, _worlds.Length);
}
}
} }
ref short itemIndex = ref _mapping[worldID]; short itemIndex = _mapping[worldID];
if (itemIndex <= 0)
if (itemIndex == 0)
{ {
if (_recycledItemsCount > 0) lock (_lock)
{ {
_count++; itemIndex = _mapping[worldID];
itemIndex = _recycledItems[--_recycledItemsCount]; if (itemIndex <= 0)
{
if (_recycledItemsCount > 0)
{
_count++;
itemIndex = _recycledItems[--_recycledItemsCount];
}
else
{
itemIndex = ++_count;
}
_mapping[worldID] = itemIndex;
if (_items.Length <= itemIndex)
{
Array.Resize(ref _items, _items.Length << 1);
}
_interface.Init(ref _items[itemIndex], _worlds[worldID]);
_dataReleaseres.Add(new Releaser());
}
} }
else
{
itemIndex = ++_count;
}
if (_items.Length <= itemIndex)
{
Array.Resize(ref _items, _items.Length << 1);
}
_interface.Init(ref _items[itemIndex], _worlds[worldID]);
_dataReleaseres.Add(new Releaser());
} }
return itemIndex; return itemIndex;
} }
private static void Release(int worldID) private static void Release(int worldID)
{ {// ts
if (_mapping.Length < _worlds.Length) lock (_lock)
{ {
Array.Resize(ref _mapping, _worlds.Length); if (_mapping.Length < _worlds.Length)
} {
ref short itemIndex = ref _mapping[worldID]; Array.Resize(ref _mapping, _worlds.Length);
if (itemIndex != 0) }
{ ref short itemIndex = ref _mapping[worldID];
_interface.OnDestroy(ref _items[itemIndex], _worlds[worldID]); if (itemIndex != 0)
_recycledItems[_recycledItemsCount++] = itemIndex; {
itemIndex = 0; _interface.OnDestroy(ref _items[itemIndex], _worlds[worldID]);
_recycledItems[_recycledItemsCount++] = itemIndex;
itemIndex = 0;
}
} }
} }
private sealed class Releaser : DataReleaser private sealed class Releaser : DataReleaser