thread safe data structures

pull/5/head
Evan Hemsley 2019-07-28 19:54:15 -07:00
parent f3a7331ae9
commit 6aca668619
5 changed files with 62 additions and 57 deletions

1
TODO
View File

@ -4,3 +4,4 @@
- emit two packages: one for dev, which includes expensive runtime validation checks, and one for release, which disables them - emit two packages: one for dev, which includes expensive runtime validation checks, and one for release, which disables them
- thread safety - thread safety
- create ConcurrentHashSet based on ConcurrentDictionary

View File

@ -1,38 +1,40 @@
using System; using System;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Encompass.Exceptions; using Encompass.Exceptions;
// we use byte as a dummy argument to ConcurrentDictionary so we can treat it like a HashSet
namespace Encompass namespace Encompass
{ {
internal class ComponentManager internal class ComponentManager
{ {
private readonly DrawLayerManager drawLayerManager; private readonly DrawLayerManager drawLayerManager;
private readonly Dictionary<Guid, Type> componentIDToType = new Dictionary<Guid, Type>(); private readonly ConcurrentDictionary<Guid, Type> componentIDToType = new ConcurrentDictionary<Guid, Type>();
private readonly Dictionary<Guid, IComponent> IDToComponent = new Dictionary<Guid, IComponent>(); private readonly ConcurrentDictionary<Guid, IComponent> IDToComponent = new ConcurrentDictionary<Guid, IComponent>();
private readonly Dictionary<Guid, HashSet<Guid>> entityIDToComponentIDs = new Dictionary<Guid, HashSet<Guid>>(); private readonly ConcurrentDictionary<Guid, ConcurrentDictionary<Guid, byte>> entityIDToComponentIDs = new ConcurrentDictionary<Guid, ConcurrentDictionary<Guid, byte>>();
private readonly Dictionary<Guid, Guid> componentIDToEntityID = new Dictionary<Guid, Guid>(); private readonly ConcurrentDictionary<Guid, Guid> componentIDToEntityID = new ConcurrentDictionary<Guid, Guid>();
private readonly Dictionary<Type, HashSet<Guid>> typeToComponentIDs = new Dictionary<Type, HashSet<Guid>>(); private readonly ConcurrentDictionary<Type, ConcurrentDictionary<Guid, byte>> typeToComponentIDs = new ConcurrentDictionary<Type, ConcurrentDictionary<Guid, byte>>();
private readonly List<Guid> activeComponents = new List<Guid>(); private readonly ConcurrentDictionary<Guid, byte> activeComponents = new ConcurrentDictionary<Guid, byte>();
private readonly List<Guid> inactiveComponents = new List<Guid>(); private readonly ConcurrentDictionary<Guid, byte> inactiveComponents = new ConcurrentDictionary<Guid, byte>();
private readonly HashSet<Guid> componentsMarkedForActivation = new HashSet<Guid>(); private readonly ConcurrentDictionary<Guid, byte> componentsMarkedForDeactivation = new ConcurrentDictionary<Guid, byte>();
private readonly HashSet<Guid> componentsMarkedForDeactivation = new HashSet<Guid>(); private readonly ConcurrentDictionary<Guid, byte> componentsMarkedForRemoval = new ConcurrentDictionary<Guid, byte>();
private readonly HashSet<Guid> componentsMarkedForRemoval = new HashSet<Guid>();
private readonly Dictionary<Guid, IComponent> pendingUpdates = new Dictionary<Guid, IComponent>(); private readonly ConcurrentDictionary<Guid, IComponent> pendingUpdates = new ConcurrentDictionary<Guid, IComponent>();
//shared references with EntityManager //shared references with EntityManager
private readonly HashSet<Guid> entitiesWithAddedComponents; private readonly ConcurrentDictionary<Guid, byte> entitiesWithAddedComponents;
private readonly HashSet<Guid> entitiesWithRemovedComponents; private readonly ConcurrentDictionary<Guid, byte> entitiesWithRemovedComponents;
public ComponentManager( public ComponentManager(
DrawLayerManager drawLayerManager, DrawLayerManager drawLayerManager,
HashSet<Guid> entitiesWithAddedComponents, ConcurrentDictionary<Guid, byte> entitiesWithAddedComponents,
HashSet<Guid> entitiesWithRemovedComponents ConcurrentDictionary<Guid, byte> entitiesWithRemovedComponents
) )
{ {
this.drawLayerManager = drawLayerManager; this.drawLayerManager = drawLayerManager;
@ -42,7 +44,7 @@ namespace Encompass
internal void RegisterEntity(Guid entityID) internal void RegisterEntity(Guid entityID)
{ {
entityIDToComponentIDs.Add(entityID, new HashSet<Guid>()); entityIDToComponentIDs.TryAdd(entityID, new ConcurrentDictionary<Guid, byte>());
} }
internal Guid NextID() internal Guid NextID()
@ -57,17 +59,17 @@ namespace Encompass
if (!typeToComponentIDs.ContainsKey(typeof(TComponent))) if (!typeToComponentIDs.ContainsKey(typeof(TComponent)))
{ {
typeToComponentIDs.Add(typeof(TComponent), new HashSet<Guid>()); typeToComponentIDs.TryAdd(typeof(TComponent), new ConcurrentDictionary<Guid, byte>());
} }
typeToComponentIDs[typeof(TComponent)].Add(componentID); typeToComponentIDs[typeof(TComponent)].TryAdd(componentID, 0);
entityIDToComponentIDs[entity.ID].Add(componentID); entityIDToComponentIDs[entity.ID].TryAdd(componentID, 0);
componentIDToEntityID[componentID] = entity.ID; componentIDToEntityID[componentID] = entity.ID;
activeComponents.Add(componentID); activeComponents.TryAdd(componentID, 0);
entitiesWithAddedComponents.Add(entity.ID); entitiesWithAddedComponents.TryAdd(entity.ID, 0);
return componentID; return componentID;
} }
@ -82,26 +84,26 @@ namespace Encompass
internal IEnumerable<Guid> GetComponentIDsByEntityID(Guid entityID) internal IEnumerable<Guid> GetComponentIDsByEntityID(Guid entityID)
{ {
return entityIDToComponentIDs.ContainsKey(entityID) ? return entityIDToComponentIDs.ContainsKey(entityID) ?
entityIDToComponentIDs[entityID] : entityIDToComponentIDs[entityID].Keys :
Enumerable.Empty<Guid>(); Enumerable.Empty<Guid>();
} }
internal IEnumerable<ValueTuple<Guid, IComponent>> GetComponentsByEntity(Guid entityID) internal IEnumerable<ValueTuple<Guid, IComponent>> GetComponentsByEntity(Guid entityID)
{ {
return GetComponentIDsByEntityID(entityID).Intersect(activeComponents).Select((id) => new ValueTuple<Guid, IComponent>(id, IDToComponent[id])); return GetComponentIDsByEntityID(entityID).Intersect(activeComponents.Keys).Select((id) => new ValueTuple<Guid, IComponent>(id, IDToComponent[id]));
} }
internal IEnumerable<ValueTuple<Guid, Guid, TComponent>> GetActiveComponentsByType<TComponent>() where TComponent : struct, IComponent internal IEnumerable<ValueTuple<Guid, Guid, TComponent>> GetActiveComponentsByType<TComponent>() where TComponent : struct, IComponent
{ {
return typeToComponentIDs.ContainsKey(typeof(TComponent)) ? return typeToComponentIDs.ContainsKey(typeof(TComponent)) ?
typeToComponentIDs[typeof(TComponent)].Intersect(activeComponents).Select((id) => new ValueTuple<Guid, Guid, TComponent>(GetEntityIDByComponentID(id), id, (TComponent)IDToComponent[id])) : typeToComponentIDs[typeof(TComponent)].Keys.Intersect(activeComponents.Keys).Select((id) => new ValueTuple<Guid, Guid, TComponent>(GetEntityIDByComponentID(id), id, (TComponent)IDToComponent[id])) :
Enumerable.Empty<ValueTuple<Guid, Guid, TComponent>>(); Enumerable.Empty<ValueTuple<Guid, Guid, TComponent>>();
} }
internal IEnumerable<ValueTuple<Guid, IComponent>> GetActiveComponentsByType(Type type) internal IEnumerable<ValueTuple<Guid, IComponent>> GetActiveComponentsByType(Type type)
{ {
return typeToComponentIDs.ContainsKey(type) ? return typeToComponentIDs.ContainsKey(type) ?
typeToComponentIDs[type].Intersect(activeComponents).Select((id) => new ValueTuple<Guid, IComponent>(id, IDToComponent[id])) : typeToComponentIDs[type].Keys.Intersect(activeComponents.Keys).Select((id) => new ValueTuple<Guid, IComponent>(id, IDToComponent[id])) :
Enumerable.Empty<ValueTuple<Guid, IComponent>>(); Enumerable.Empty<ValueTuple<Guid, IComponent>>();
} }
@ -156,7 +158,7 @@ namespace Encompass
throw new RepeatUpdateComponentException("Component {0} with ID {1} was updated multiple times this frame", typeof(TComponent).Name, componentID); throw new RepeatUpdateComponentException("Component {0} with ID {1} was updated multiple times this frame", typeof(TComponent).Name, componentID);
} }
pendingUpdates.Add(componentID, newComponentValue); pendingUpdates.TryAdd(componentID, newComponentValue);
} }
internal void PerformComponentUpdates() internal void PerformComponentUpdates()
@ -179,23 +181,23 @@ namespace Encompass
internal void Activate(Guid componentID) internal void Activate(Guid componentID)
{ {
if (inactiveComponents.Remove(componentID)) if (inactiveComponents.TryRemove(componentID, out _))
{ {
activeComponents.Add(componentID); activeComponents.TryAdd(componentID, 0);
} }
var entityID = GetEntityIDByComponentID(componentID); var entityID = GetEntityIDByComponentID(componentID);
entitiesWithAddedComponents.Add(entityID); entitiesWithAddedComponents.TryAdd(entityID, 0);
} }
internal void MarkForDeactivation(Guid componentID) internal void MarkForDeactivation(Guid componentID)
{ {
componentsMarkedForDeactivation.Add(componentID); componentsMarkedForDeactivation.TryAdd(componentID, 0);
} }
internal void DeactivateMarkedComponents() internal void DeactivateMarkedComponents()
{ {
foreach (var componentID in componentsMarkedForDeactivation) foreach (var componentID in componentsMarkedForDeactivation.Keys)
{ {
Deactivate(componentID); Deactivate(componentID);
} }
@ -205,23 +207,23 @@ namespace Encompass
private void Deactivate(Guid componentID) private void Deactivate(Guid componentID)
{ {
if (activeComponents.Remove(componentID)) if (activeComponents.TryRemove(componentID, out _))
{ {
inactiveComponents.Add(componentID); inactiveComponents.TryAdd(componentID, 0);
} }
var entityID = GetEntityIDByComponentID(componentID); var entityID = GetEntityIDByComponentID(componentID);
entitiesWithRemovedComponents.Add(entityID); entitiesWithRemovedComponents.TryAdd(entityID, 0);
} }
internal void MarkForRemoval(Guid componentID) internal void MarkForRemoval(Guid componentID)
{ {
componentsMarkedForRemoval.Add(componentID); componentsMarkedForRemoval.TryAdd(componentID, 0);
} }
internal void RemoveMarkedComponents() internal void RemoveMarkedComponents()
{ {
foreach (var componentID in componentsMarkedForRemoval) foreach (var componentID in componentsMarkedForRemoval.Keys)
{ {
Remove(componentID); Remove(componentID);
} }
@ -234,28 +236,28 @@ namespace Encompass
var component = IDToComponent[componentID]; var component = IDToComponent[componentID];
var type = componentIDToType[componentID]; var type = componentIDToType[componentID];
activeComponents.Remove(componentID); activeComponents.TryRemove(componentID, out _);
inactiveComponents.Remove(componentID); inactiveComponents.TryRemove(componentID, out _);
var entityID = componentIDToEntityID[componentID]; var entityID = componentIDToEntityID[componentID];
if (entityIDToComponentIDs.ContainsKey(entityID)) if (entityIDToComponentIDs.ContainsKey(entityID))
{ {
entityIDToComponentIDs[entityID].Remove(componentID); entityIDToComponentIDs[entityID].TryRemove(componentID, out _);
} }
IDToComponent.Remove(componentID); IDToComponent.TryRemove(componentID, out _);
componentIDToType.Remove(componentID); componentIDToType.TryRemove(componentID, out _);
componentIDToEntityID.Remove(componentID); componentIDToEntityID.TryRemove(componentID, out _);
typeToComponentIDs[type].Remove(componentID); typeToComponentIDs[type].TryRemove(componentID, out _);
drawLayerManager.UnRegisterComponentWithLayer(componentID); drawLayerManager.UnRegisterComponentWithLayer(componentID);
entitiesWithRemovedComponents.Add(entityID); entitiesWithRemovedComponents.TryAdd(entityID, 0);
} }
public void RegisterDestroyedEntity(Guid entityID) public void RegisterDestroyedEntity(Guid entityID)
{ {
entityIDToComponentIDs.Remove(entityID); entityIDToComponentIDs.TryRemove(entityID, out _);
} }
} }
} }

View File

@ -1,3 +1,4 @@
using System.Collections.Concurrent;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -5,22 +6,22 @@ namespace Encompass
{ {
internal class EntityManager internal class EntityManager
{ {
private readonly Dictionary<Guid, Entity> IDToEntity = new Dictionary<Guid, Entity>(); private readonly ConcurrentDictionary<Guid, Entity> IDToEntity = new ConcurrentDictionary<Guid, Entity>();
private readonly HashSet<Guid> entitiesMarkedForDestroy = new HashSet<Guid>(); private readonly HashSet<Guid> entitiesMarkedForDestroy = new HashSet<Guid>();
private readonly Dictionary<Type, HashSet<IEntityTracker>> componentTypeToEntityTrackers = new Dictionary<Type, HashSet<IEntityTracker>>(); private readonly Dictionary<Type, HashSet<IEntityTracker>> componentTypeToEntityTrackers = new Dictionary<Type, HashSet<IEntityTracker>>();
private readonly Dictionary<Guid, HashSet<IEntityTracker>> entityToEntityTrackers = new Dictionary<Guid, HashSet<IEntityTracker>>(); private readonly Dictionary<Guid, HashSet<IEntityTracker>> entityToEntityTrackers = new Dictionary<Guid, HashSet<IEntityTracker>>();
private readonly HashSet<Guid> entitiesWithAddedComponents; private readonly ConcurrentDictionary<Guid, byte> entitiesWithAddedComponents;
private readonly HashSet<Guid> entitiesWithRemovedComponents; private readonly ConcurrentDictionary<Guid, byte> entitiesWithRemovedComponents;
private readonly ComponentManager componentManager; private readonly ComponentManager componentManager;
public EntityManager( public EntityManager(
ComponentManager componentManager, ComponentManager componentManager,
HashSet<Guid> entitiesWithAddedComponents, ConcurrentDictionary<Guid, byte> entitiesWithAddedComponents,
HashSet<Guid> entitiesWithRemovedComponents ConcurrentDictionary<Guid, byte> entitiesWithRemovedComponents
) )
{ {
this.componentManager = componentManager; this.componentManager = componentManager;
@ -57,7 +58,7 @@ namespace Encompass
foreach (var entityID in entitiesMarkedForDestroy) foreach (var entityID in entitiesMarkedForDestroy)
{ {
componentManager.MarkAllComponentsOnEntityForRemoval(entityID); componentManager.MarkAllComponentsOnEntityForRemoval(entityID);
IDToEntity.Remove(entityID); IDToEntity.TryRemove(entityID, out _);
entityToEntityTrackers.Remove(entityID); entityToEntityTrackers.Remove(entityID);
componentManager.RegisterDestroyedEntity(entityID); componentManager.RegisterDestroyedEntity(entityID);
} }
@ -96,17 +97,17 @@ namespace Encompass
public void RegisterDirtyEntityWithAddedComponents(Guid entityID) public void RegisterDirtyEntityWithAddedComponents(Guid entityID)
{ {
entitiesWithAddedComponents.Add(entityID); entitiesWithAddedComponents.TryAdd(entityID, 0);
} }
public void RegisterDirtyEntityWithRemovedComponents(Guid entityID) public void RegisterDirtyEntityWithRemovedComponents(Guid entityID)
{ {
entitiesWithRemovedComponents.Add(entityID); entitiesWithRemovedComponents.TryAdd(entityID, 0);
} }
public void CheckEntitiesWithAddedComponents() public void CheckEntitiesWithAddedComponents()
{ {
foreach (var entityID in entitiesWithAddedComponents) foreach (var entityID in entitiesWithAddedComponents.Keys)
{ {
CheckAndRegisterEntity(entityID); CheckAndRegisterEntity(entityID);
} }
@ -116,7 +117,7 @@ namespace Encompass
public void CheckEntitiesWithRemovedComponents() public void CheckEntitiesWithRemovedComponents()
{ {
foreach (var entityID in entitiesWithRemovedComponents) foreach (var entityID in entitiesWithRemovedComponents.Keys)
{ {
if (entityToEntityTrackers.ContainsKey(entityID)) if (entityToEntityTrackers.ContainsKey(entityID))
{ {

View File

@ -1,3 +1,4 @@
using System.Collections.Concurrent;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Reflection; using System.Reflection;
@ -32,8 +33,8 @@ namespace Encompass
public WorldBuilder() public WorldBuilder()
{ {
var entitiesWithAddedComponents = new HashSet<Guid>(); var entitiesWithAddedComponents = new ConcurrentDictionary<Guid, byte>();
var entitiesWithRemovedComponents = new HashSet<Guid>(); var entitiesWithRemovedComponents = new ConcurrentDictionary<Guid, byte>();
drawLayerManager = new DrawLayerManager(); drawLayerManager = new DrawLayerManager();
componentManager = new ComponentManager(drawLayerManager, entitiesWithAddedComponents, entitiesWithRemovedComponents); componentManager = new ComponentManager(drawLayerManager, entitiesWithAddedComponents, entitiesWithRemovedComponents);
entityManager = new EntityManager(componentManager, entitiesWithAddedComponents, entitiesWithRemovedComponents); entityManager = new EntityManager(componentManager, entitiesWithAddedComponents, entitiesWithRemovedComponents);