From 6aca66861959ca00034311508741732dcc75f3ad Mon Sep 17 00:00:00 2001 From: Evan Hemsley Date: Sun, 28 Jul 2019 19:54:15 -0700 Subject: [PATCH] thread safe data structures --- TODO | 1 + encompass-cs/ComponentManager.cs | 92 ++++++++++--------- .../Graph/DirectedGraph.cs | 0 encompass-cs/EntityManager.cs | 21 +++-- encompass-cs/WorldBuilder.cs | 5 +- 5 files changed, 62 insertions(+), 57 deletions(-) rename encompass-cs/{ => DataStructures}/Graph/DirectedGraph.cs (100%) diff --git a/TODO b/TODO index e24e2c0..30fab52 100644 --- a/TODO +++ b/TODO @@ -4,3 +4,4 @@ - emit two packages: one for dev, which includes expensive runtime validation checks, and one for release, which disables them - thread safety +- create ConcurrentHashSet based on ConcurrentDictionary \ No newline at end of file diff --git a/encompass-cs/ComponentManager.cs b/encompass-cs/ComponentManager.cs index e137355..90576ae 100644 --- a/encompass-cs/ComponentManager.cs +++ b/encompass-cs/ComponentManager.cs @@ -1,38 +1,40 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using Encompass.Exceptions; +// we use byte as a dummy argument to ConcurrentDictionary so we can treat it like a HashSet + namespace Encompass { internal class ComponentManager { private readonly DrawLayerManager drawLayerManager; - private readonly Dictionary componentIDToType = new Dictionary(); - private readonly Dictionary IDToComponent = new Dictionary(); - private readonly Dictionary> entityIDToComponentIDs = new Dictionary>(); - private readonly Dictionary componentIDToEntityID = new Dictionary(); + private readonly ConcurrentDictionary componentIDToType = new ConcurrentDictionary(); + private readonly ConcurrentDictionary IDToComponent = new ConcurrentDictionary(); + private readonly ConcurrentDictionary> entityIDToComponentIDs = new ConcurrentDictionary>(); + private readonly ConcurrentDictionary componentIDToEntityID = new ConcurrentDictionary(); - private readonly Dictionary> typeToComponentIDs = new Dictionary>(); + private readonly ConcurrentDictionary> typeToComponentIDs = new ConcurrentDictionary>(); - private readonly List activeComponents = new List(); - private readonly List inactiveComponents = new List(); + private readonly ConcurrentDictionary activeComponents = new ConcurrentDictionary(); + private readonly ConcurrentDictionary inactiveComponents = new ConcurrentDictionary(); - private readonly HashSet componentsMarkedForActivation = new HashSet(); - private readonly HashSet componentsMarkedForDeactivation = new HashSet(); - private readonly HashSet componentsMarkedForRemoval = new HashSet(); + private readonly ConcurrentDictionary componentsMarkedForDeactivation = new ConcurrentDictionary(); + private readonly ConcurrentDictionary componentsMarkedForRemoval = new ConcurrentDictionary(); - private readonly Dictionary pendingUpdates = new Dictionary(); + private readonly ConcurrentDictionary pendingUpdates = new ConcurrentDictionary(); //shared references with EntityManager - private readonly HashSet entitiesWithAddedComponents; - private readonly HashSet entitiesWithRemovedComponents; + private readonly ConcurrentDictionary entitiesWithAddedComponents; + private readonly ConcurrentDictionary entitiesWithRemovedComponents; public ComponentManager( DrawLayerManager drawLayerManager, - HashSet entitiesWithAddedComponents, - HashSet entitiesWithRemovedComponents + ConcurrentDictionary entitiesWithAddedComponents, + ConcurrentDictionary entitiesWithRemovedComponents ) { this.drawLayerManager = drawLayerManager; @@ -42,7 +44,7 @@ namespace Encompass internal void RegisterEntity(Guid entityID) { - entityIDToComponentIDs.Add(entityID, new HashSet()); + entityIDToComponentIDs.TryAdd(entityID, new ConcurrentDictionary()); } internal Guid NextID() @@ -57,17 +59,17 @@ namespace Encompass if (!typeToComponentIDs.ContainsKey(typeof(TComponent))) { - typeToComponentIDs.Add(typeof(TComponent), new HashSet()); + typeToComponentIDs.TryAdd(typeof(TComponent), new ConcurrentDictionary()); } - 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; - activeComponents.Add(componentID); + activeComponents.TryAdd(componentID, 0); - entitiesWithAddedComponents.Add(entity.ID); + entitiesWithAddedComponents.TryAdd(entity.ID, 0); return componentID; } @@ -82,26 +84,26 @@ namespace Encompass internal IEnumerable GetComponentIDsByEntityID(Guid entityID) { return entityIDToComponentIDs.ContainsKey(entityID) ? - entityIDToComponentIDs[entityID] : + entityIDToComponentIDs[entityID].Keys : Enumerable.Empty(); } internal IEnumerable> GetComponentsByEntity(Guid entityID) { - return GetComponentIDsByEntityID(entityID).Intersect(activeComponents).Select((id) => new ValueTuple(id, IDToComponent[id])); + return GetComponentIDsByEntityID(entityID).Intersect(activeComponents.Keys).Select((id) => new ValueTuple(id, IDToComponent[id])); } internal IEnumerable> GetActiveComponentsByType() where TComponent : struct, IComponent { return typeToComponentIDs.ContainsKey(typeof(TComponent)) ? - typeToComponentIDs[typeof(TComponent)].Intersect(activeComponents).Select((id) => new ValueTuple(GetEntityIDByComponentID(id), id, (TComponent)IDToComponent[id])) : + typeToComponentIDs[typeof(TComponent)].Keys.Intersect(activeComponents.Keys).Select((id) => new ValueTuple(GetEntityIDByComponentID(id), id, (TComponent)IDToComponent[id])) : Enumerable.Empty>(); } internal IEnumerable> GetActiveComponentsByType(Type type) { return typeToComponentIDs.ContainsKey(type) ? - typeToComponentIDs[type].Intersect(activeComponents).Select((id) => new ValueTuple(id, IDToComponent[id])) : + typeToComponentIDs[type].Keys.Intersect(activeComponents.Keys).Select((id) => new ValueTuple(id, IDToComponent[id])) : Enumerable.Empty>(); } @@ -156,7 +158,7 @@ namespace Encompass 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() @@ -179,23 +181,23 @@ namespace Encompass 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); - entitiesWithAddedComponents.Add(entityID); + entitiesWithAddedComponents.TryAdd(entityID, 0); } internal void MarkForDeactivation(Guid componentID) { - componentsMarkedForDeactivation.Add(componentID); + componentsMarkedForDeactivation.TryAdd(componentID, 0); } internal void DeactivateMarkedComponents() { - foreach (var componentID in componentsMarkedForDeactivation) + foreach (var componentID in componentsMarkedForDeactivation.Keys) { Deactivate(componentID); } @@ -205,23 +207,23 @@ namespace Encompass 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); - entitiesWithRemovedComponents.Add(entityID); + entitiesWithRemovedComponents.TryAdd(entityID, 0); } internal void MarkForRemoval(Guid componentID) { - componentsMarkedForRemoval.Add(componentID); + componentsMarkedForRemoval.TryAdd(componentID, 0); } internal void RemoveMarkedComponents() { - foreach (var componentID in componentsMarkedForRemoval) + foreach (var componentID in componentsMarkedForRemoval.Keys) { Remove(componentID); } @@ -234,28 +236,28 @@ namespace Encompass var component = IDToComponent[componentID]; var type = componentIDToType[componentID]; - activeComponents.Remove(componentID); - inactiveComponents.Remove(componentID); + activeComponents.TryRemove(componentID, out _); + inactiveComponents.TryRemove(componentID, out _); var entityID = componentIDToEntityID[componentID]; if (entityIDToComponentIDs.ContainsKey(entityID)) { - entityIDToComponentIDs[entityID].Remove(componentID); + entityIDToComponentIDs[entityID].TryRemove(componentID, out _); } - IDToComponent.Remove(componentID); - componentIDToType.Remove(componentID); - componentIDToEntityID.Remove(componentID); - typeToComponentIDs[type].Remove(componentID); + IDToComponent.TryRemove(componentID, out _); + componentIDToType.TryRemove(componentID, out _); + componentIDToEntityID.TryRemove(componentID, out _); + typeToComponentIDs[type].TryRemove(componentID, out _); drawLayerManager.UnRegisterComponentWithLayer(componentID); - entitiesWithRemovedComponents.Add(entityID); + entitiesWithRemovedComponents.TryAdd(entityID, 0); } public void RegisterDestroyedEntity(Guid entityID) { - entityIDToComponentIDs.Remove(entityID); + entityIDToComponentIDs.TryRemove(entityID, out _); } } } diff --git a/encompass-cs/Graph/DirectedGraph.cs b/encompass-cs/DataStructures/Graph/DirectedGraph.cs similarity index 100% rename from encompass-cs/Graph/DirectedGraph.cs rename to encompass-cs/DataStructures/Graph/DirectedGraph.cs diff --git a/encompass-cs/EntityManager.cs b/encompass-cs/EntityManager.cs index f319b13..e0385a9 100644 --- a/encompass-cs/EntityManager.cs +++ b/encompass-cs/EntityManager.cs @@ -1,3 +1,4 @@ +using System.Collections.Concurrent; using System; using System.Collections.Generic; @@ -5,22 +6,22 @@ namespace Encompass { internal class EntityManager { - private readonly Dictionary IDToEntity = new Dictionary(); + private readonly ConcurrentDictionary IDToEntity = new ConcurrentDictionary(); private readonly HashSet entitiesMarkedForDestroy = new HashSet(); private readonly Dictionary> componentTypeToEntityTrackers = new Dictionary>(); private readonly Dictionary> entityToEntityTrackers = new Dictionary>(); - private readonly HashSet entitiesWithAddedComponents; - private readonly HashSet entitiesWithRemovedComponents; + private readonly ConcurrentDictionary entitiesWithAddedComponents; + private readonly ConcurrentDictionary entitiesWithRemovedComponents; private readonly ComponentManager componentManager; public EntityManager( ComponentManager componentManager, - HashSet entitiesWithAddedComponents, - HashSet entitiesWithRemovedComponents + ConcurrentDictionary entitiesWithAddedComponents, + ConcurrentDictionary entitiesWithRemovedComponents ) { this.componentManager = componentManager; @@ -57,7 +58,7 @@ namespace Encompass foreach (var entityID in entitiesMarkedForDestroy) { componentManager.MarkAllComponentsOnEntityForRemoval(entityID); - IDToEntity.Remove(entityID); + IDToEntity.TryRemove(entityID, out _); entityToEntityTrackers.Remove(entityID); componentManager.RegisterDestroyedEntity(entityID); } @@ -96,17 +97,17 @@ namespace Encompass public void RegisterDirtyEntityWithAddedComponents(Guid entityID) { - entitiesWithAddedComponents.Add(entityID); + entitiesWithAddedComponents.TryAdd(entityID, 0); } public void RegisterDirtyEntityWithRemovedComponents(Guid entityID) { - entitiesWithRemovedComponents.Add(entityID); + entitiesWithRemovedComponents.TryAdd(entityID, 0); } public void CheckEntitiesWithAddedComponents() { - foreach (var entityID in entitiesWithAddedComponents) + foreach (var entityID in entitiesWithAddedComponents.Keys) { CheckAndRegisterEntity(entityID); } @@ -116,7 +117,7 @@ namespace Encompass public void CheckEntitiesWithRemovedComponents() { - foreach (var entityID in entitiesWithRemovedComponents) + foreach (var entityID in entitiesWithRemovedComponents.Keys) { if (entityToEntityTrackers.ContainsKey(entityID)) { diff --git a/encompass-cs/WorldBuilder.cs b/encompass-cs/WorldBuilder.cs index eea0982..7afe52a 100644 --- a/encompass-cs/WorldBuilder.cs +++ b/encompass-cs/WorldBuilder.cs @@ -1,3 +1,4 @@ +using System.Collections.Concurrent; using System; using System.Collections.Generic; using System.Reflection; @@ -32,8 +33,8 @@ namespace Encompass public WorldBuilder() { - var entitiesWithAddedComponents = new HashSet(); - var entitiesWithRemovedComponents = new HashSet(); + var entitiesWithAddedComponents = new ConcurrentDictionary(); + var entitiesWithRemovedComponents = new ConcurrentDictionary(); drawLayerManager = new DrawLayerManager(); componentManager = new ComponentManager(drawLayerManager, entitiesWithAddedComponents, entitiesWithRemovedComponents); entityManager = new EntityManager(componentManager, entitiesWithAddedComponents, entitiesWithRemovedComponents);