diff --git a/src/Amalgam/entity/Entity.cpp b/src/Amalgam/entity/Entity.cpp index 5b170ea4..722ffdfd 100644 --- a/src/Amalgam/entity/Entity.cpp +++ b/src/Amalgam/entity/Entity.cpp @@ -792,14 +792,12 @@ StringInternPool::StringID Entity::GetContainedEntityIdFromIndex(size_t entity_i return contained_entities[entity_index]->GetIdStringId(); } -EntityQueryCaches *Entity::GetOrCreateQueryCaches() +void Entity::CreateQueryCaches() { EnsureHasContainedEntities(); if(!entityRelationships.relationships->queryCaches) entityRelationships.relationships->queryCaches = std::make_unique(this); - - return entityRelationships.relationships->queryCaches.get(); } void Entity::SetRandomState(const std::string &new_state, bool deep_set_seed, std::vector *write_listeners) diff --git a/src/Amalgam/entity/Entity.h b/src/Amalgam/entity/Entity.h index 3cd89a78..b04d3023 100644 --- a/src/Amalgam/entity/Entity.h +++ b/src/Amalgam/entity/Entity.h @@ -278,6 +278,14 @@ class Entity return entityRelationships.container; } + //returns true if the entity has one or more contained entities and has a query cache built + bool HasQueryCaches() + { + if(!hasContainedEntities || !entityRelationships.relationships->queryCaches) + return false; + return true; + } + //clears any query caches if they exist inline void ClearQueryCaches() { @@ -285,9 +293,8 @@ class Entity entityRelationships.relationships->queryCaches.release(); } - //returns a pointer to the query caches for this entity - //creates one if it does not have an active cache - EntityQueryCaches *GetOrCreateQueryCaches(); + //creates a cache if it does not exist + void CreateQueryCaches(); //returns a pointer to the query caches for this entity //returns a nullptr if does not have an active cache @@ -864,20 +871,56 @@ class EntityReferenceWithLock : public EntityReferenceBase //acts as a reference to an Entity that can be treated as an Entity * // but also performs a read-lock on the container if multithreaded, and frees the read lock when goes out of scope -typedef EntityReferenceWithLock EntityReadReference; +//can't be a typedef due to the inability to do forward declarations, so have to include constructors +class EntityReadReference : public EntityReferenceWithLock +{ +public: + EntityReadReference() : EntityReferenceWithLock() + { } + + EntityReadReference(Entity *e) : EntityReferenceWithLock(e) + { } +}; //acts as a reference to an Entity that can be treated as an Entity * // but also performs a write-lock on the container if multithreaded, and frees the read lock when goes out of scope -typedef EntityReferenceWithLock EntityWriteReference; +//can't be a typedef due to the inability to do forward declarations, so have to include constructors +class EntityWriteReference : public EntityReferenceWithLock +{ +public: + EntityWriteReference() : EntityReferenceWithLock() + { } + + EntityWriteReference(Entity *e) : EntityReferenceWithLock(e) + { } +}; #else //not MULTITHREAD_SUPPORT //acts as a reference to an Entity that can be treated as an Entity * // but also performs a read-lock on the container if multithreaded, and frees the read lock when goes out of scope -typedef EntityReferenceBase EntityReadReference; +//can't be a typedef due to the inability to do forward declarations, so have to include constructors +class EntityReadReference : public EntityReferenceBase +{ +public: + EntityReadReference() : EntityReferenceBase() + { } + + EntityReadReference(Entity *e) : EntityReferenceBase(e) + { } +}; //acts as a reference to an Entity that can be treated as an Entity * // but also performs a write-lock on the container if multithreaded, and frees the read lock when goes out of scope -typedef EntityReferenceBase EntityWriteReference; +//can't be a typedef due to the inability to do forward declarations, so have to include constructors +class EntityWriteReference : public EntityReferenceBase +{ +public: + EntityWriteReference() : EntityReferenceBase() + { } + + EntityWriteReference(Entity *e) : EntityReferenceBase(e) + { } +}; #endif diff --git a/src/Amalgam/entity/EntityQueryCaches.cpp b/src/Amalgam/entity/EntityQueryCaches.cpp index a107cef0..339aba00 100644 --- a/src/Amalgam/entity/EntityQueryCaches.cpp +++ b/src/Amalgam/entity/EntityQueryCaches.cpp @@ -920,7 +920,7 @@ EvaluableNodeReference EntityQueryCaches::GetMatchingEntitiesFromQueryCaches(Ent { //get the label existance cache associated with this container // use the first condition as an heuristic for building it if it doesn't exist - EntityQueryCaches *entity_caches = container->GetOrCreateQueryCaches(); + EntityQueryCaches *entity_caches = container->GetQueryCaches(); //starting collection of matching entities, initialized to all entities with the requested labels // reuse existing buffer @@ -1299,10 +1299,27 @@ EvaluableNodeReference EntityQueryCaches::GetMatchingEntitiesFromQueryCaches(Ent } -EvaluableNodeReference EntityQueryCaches::GetEntitiesMatchingQuery(Entity *container, std::vector &conditions, EvaluableNodeManager *enm, bool return_query_value) +EvaluableNodeReference EntityQueryCaches::GetEntitiesMatchingQuery(EntityReadReference &container, std::vector &conditions, EvaluableNodeManager *enm, bool return_query_value) { if(_enable_SBF_datastore && CanUseQueryCaches(conditions)) + { + //if haven't built a cache before, need to build the cache container + //need to lock the entity to prevent multiple caches from being built concurrently and overwritten + if(!container->HasQueryCaches()) + { + #ifdef MULTITHREAD_SUPPORT + container.lock.unlock(); + EntityWriteReference write_lock(container); + container->CreateQueryCaches(); + write_lock.lock.unlock(); + container.lock.lock(); + #else + container->CreateQueryCaches(); + #endif + } + return GetMatchingEntitiesFromQueryCaches(container, conditions, enm, return_query_value); + } if(container == nullptr) return EvaluableNodeReference(enm->AllocNode(ENT_LIST), true); @@ -1324,10 +1341,25 @@ EvaluableNodeReference EntityQueryCaches::GetEntitiesMatchingQuery(Entity *conta if(conditions[cond_index].queryType == ENT_COMPUTE_ENTITY_CONVICTIONS || conditions[cond_index].queryType == ENT_COMPUTE_ENTITY_KL_DIVERGENCES || conditions[cond_index].queryType == ENT_COMPUTE_ENTITY_GROUP_KL_DIVERGENCE || conditions[cond_index].queryType == ENT_COMPUTE_ENTITY_DISTANCE_CONTRIBUTIONS) { - if(CanUseQueryCaches(conditions)) - return GetMatchingEntitiesFromQueryCaches(container, conditions, enm, return_query_value); - else + if(!CanUseQueryCaches(conditions)) return EvaluableNodeReference::Null(); + + //if haven't built a cache before, need to build the cache container + //need to lock the entity to prevent multiple caches from being built concurrently and overwritten + if(!container->HasQueryCaches()) + { + #ifdef MULTITHREAD_SUPPORT + container.lock.unlock(); + EntityWriteReference write_lock(container); + container->CreateQueryCaches(); + write_lock.lock.unlock(); + container.lock.lock(); + #else + container->CreateQueryCaches(); + #endif + } + + return GetMatchingEntitiesFromQueryCaches(container, conditions, enm, return_query_value); } query_return_value = conditions[cond_index].GetMatchingEntities(container, matching_entities, first_condition, (return_query_value && last_condition) ? enm : nullptr); diff --git a/src/Amalgam/entity/EntityQueryCaches.h b/src/Amalgam/entity/EntityQueryCaches.h index 1502bfe7..5e4c250b 100644 --- a/src/Amalgam/entity/EntityQueryCaches.h +++ b/src/Amalgam/entity/EntityQueryCaches.h @@ -17,6 +17,7 @@ //forward declarations: class Entity; class EntityQueryCondition; +class EntityReadReference; //stores all of the types of caches needed for queries on a particular entity class EntityQueryCaches @@ -123,7 +124,7 @@ class EntityQueryCaches //searches container for contained entities matching query. // if return_query_value is false, then returns a list of all IDs of matching contained entities // if return_query_value is true, then returns whatever the appropriate structure is for the query type for the final query - static EvaluableNodeReference GetEntitiesMatchingQuery(Entity *container, std::vector &conditions, EvaluableNodeManager *enm, bool return_query_value); + static EvaluableNodeReference GetEntitiesMatchingQuery(EntityReadReference &container, std::vector &conditions, EvaluableNodeManager *enm, bool return_query_value); //returns the collection of entities (and optionally associated compute values) that satisfy the specified chain of query conditions // uses efficient querying methods with a query database, one database per container