From ebf48b241f39f1faf33cdbc67f9ebf79c6355a4a Mon Sep 17 00:00:00 2001 From: howsohazard <143410553+howsohazard@users.noreply.github.com> Date: Mon, 5 Aug 2024 18:36:01 -0400 Subject: [PATCH] 21133: Adds optional constraints to entity creation, fixes some bugs and resource leaks around failed entity creations, MINOR (#212) --- docs/language.js | 6 +- src/Amalgam/amlg_code/full_test.amlg | 19 ++ src/Amalgam/entity/Entity.h | 42 ++++- src/Amalgam/interpreter/Interpreter.cpp | 129 +++++++++++++- src/Amalgam/interpreter/Interpreter.h | 53 +++++- .../interpreter/InterpreterOpcodesBase.cpp | 2 +- .../InterpreterOpcodesCodeMixing.cpp | 24 ++- .../InterpreterOpcodesEntityAccess.cpp | 6 +- .../InterpreterOpcodesEntityControl.cpp | 15 +- src/Amalgam/out.txt | 163 ++++-------------- 10 files changed, 301 insertions(+), 158 deletions(-) diff --git a/docs/language.js b/docs/language.js index 59e0a8de..0a8a1f5a 100644 --- a/docs/language.js +++ b/docs/language.js @@ -1710,16 +1710,16 @@ var data = [ }, { - "parameter" : "call_entity id entity [string label_name] [assoc arguments] [number operation_limit] [number max_node_allocations] [number max_opcode_execution_depth]", + "parameter" : "call_entity id entity [string label_name] [assoc arguments] [number operation_limit] [number max_node_allocations] [number max_opcode_execution_depth] [number max_contained_entities] [number max_contained_entity_depth] [number max_entity_id_length]", "output" : "*", "permissions" : "e", "new scope" : true, - "description" : "Calls the contained entity specified by id, using the entity as the new entity context. It will evaluate to the return value of the call, null if not found. If string is specified, then it will call the label specified by string. If assoc is specified, then it will pass assoc as the arguments on the scope stack. If operation_limit is specified, it represents the number of operations that are allowed to be performed. If operation_limit is 0 or infinite, then an infinite of operations will be allotted to the entity, but only if its containing entity (the current entity) has infinite operations. The root entity has infinite computing cycles. If max_node_allocations is specified, it represents the maximum number of nodes that are allowed to be allocated, limiting the total memory. If max_node_allocations is 0 or infinite, then there is no limit to the number of nodes to be allotted to the entity as long as the machine has sufficient memory, but only if the containing entity (the current entity) has unlimited memory access. If max_opcode_execution_depth is 0 or infinite and the caller also has no limit, then there is no limit to the depth that opcodes can execute, otherwise max_opcode_execution_depth limits how deep nested opcodes will be called. The execution performed will use a random number stream created from the entity's random number stream.", + "description" : "Calls the contained entity specified by id, using the entity as the new entity context. It will evaluate to the return value of the call, null if not found. If string is specified, then it will call the label specified by string. If assoc is specified, then it will pass assoc as the arguments on the scope stack. If operation_limit is specified, it represents the number of operations that are allowed to be performed. If operation_limit is 0 or infinite, then an infinite of operations will be allotted to the entity, but only if its containing entity (the current entity) has infinite operations. The root entity has infinite computing cycles. If max_node_allocations is specified, it represents the maximum number of nodes that are allowed to be allocated, limiting the total memory. If max_node_allocations is 0 or infinite, then there is no limit to the number of nodes to be allotted to the entity as long as the machine has sufficient memory, but only if the containing entity (the current entity) has unlimited memory access. If max_opcode_execution_depth is 0 or infinite and the caller also has no limit, then there is no limit to the depth that opcodes can execute, otherwise max_opcode_execution_depth limits how deep nested opcodes will be called. The parameters max_contained_entities, max_contained_entity_depth, and max_entity_id_length constrain what they describe, and are primarily useful when ensuring that an entity and all its contained entities can be stored out to the filesystem. The execution performed will use a random number stream created from the entity's random number stream.", "example" : "(create_entities \"TestContainerExec\"\n (lambda (parallel\n ##d (print \"hello \" x)\n )) \n)\n\n(print (call_entity \"TestContainerExec\" \"d\" (assoc x \"goodbye\")))" }, { - "parameter" : "call_entity_get_changes id entity [string label_name] [assoc arguments] [number operation_limit] [number max_node_allocations] [number max_opcode_execution_depth]", + "parameter" : "call_entity_get_changes id entity [string label_name] [assoc arguments] [number operation_limit] [number max_node_allocations] [number max_opcode_execution_depth] [number max_contained_entities] [number max_contained_entity_depth] [number max_entity_id_length]", "output" : "list of *1 *2", "permissions" : "e", "new scope" : true, diff --git a/src/Amalgam/amlg_code/full_test.amlg b/src/Amalgam/amlg_code/full_test.amlg index 5b9f91a3..4147c856 100644 --- a/src/Amalgam/amlg_code/full_test.amlg +++ b/src/Amalgam/amlg_code/full_test.amlg @@ -3415,6 +3415,25 @@ (print (unparse (call_entity "TestContainerExec" "b") (true) (true))) (print (unparse (call_entity "TestContainerExec" "c" (assoc x 5)) (true) (true))) + (print "execution limits tests\n") + (create_entities "ExecLimitsTest" (lambda + (null + #copy_entity + (while (true) + (clone_entities (null) (null)) + ) + + #load + (while + (true) + ) + )) + ) + + (call_entity "ExecLimitsTest" "load" (null) 100) + (call_entity "ExecLimitsTest" "copy_entity" (null) 1000 1000 100 10 3 20) + (print (total_size (flatten_entity "ExecLimitsTest")) "\n") + (print "--call_entity_get_changes--\n") (create_entities "CEGCTest" (lambda (parallel ##a_assign diff --git a/src/Amalgam/entity/Entity.h b/src/Amalgam/entity/Entity.h index 32f09c16..e2733f93 100644 --- a/src/Amalgam/entity/Entity.h +++ b/src/Amalgam/entity/Entity.h @@ -392,6 +392,19 @@ class Entity return 0; } + //returns the total number of all contained entities including indirectly contained entities + inline size_t GetTotalNumContainedEntitiesIncludingSelf() + { + size_t total = 1; + if(hasContainedEntities) + { + for(Entity *e : entityRelationships.relationships->containedEntities) + total += e->GetTotalNumContainedEntitiesIncludingSelf(); + } + + return total; + } + //returns direct access to vector of pointers to Entity objects contained by this Entity inline std::vector &GetContainedEntities() { @@ -480,16 +493,17 @@ class Entity { public: inline EntityReferenceBufferReference() - : bufferReference(nullptr) + : maxEntityPathDepth(0), bufferReference(nullptr) { } inline EntityReferenceBufferReference(std::vector &buffer) - : bufferReference(&buffer) + : maxEntityPathDepth(0), bufferReference(&buffer) { } inline EntityReferenceBufferReference(EntityReferenceBufferReference &&erbr) { bufferReference = erbr.bufferReference; + maxEntityPathDepth = erbr.maxEntityPathDepth; erbr.bufferReference = nullptr; } @@ -504,6 +518,7 @@ class Entity { bufferReference->clear(); bufferReference = nullptr; + maxEntityPathDepth = 0; } } @@ -515,6 +530,7 @@ class Entity bufferReference->clear(); bufferReference = erbr.bufferReference; + maxEntityPathDepth = erbr.maxEntityPathDepth; erbr.bufferReference = nullptr; } return *this; @@ -535,6 +551,9 @@ class Entity return *bufferReference; } + //maximum depth of an id path + size_t maxEntityPathDepth; + protected: std::vector *bufferReference; }; @@ -554,15 +573,21 @@ class Entity else erbr = EntityReferenceBufferReference(entityReadReferenceBuffer); + erbr.maxEntityPathDepth = 0; + if(include_this_entity) { if constexpr(std::is_same::value) entityWriteReferenceBuffer.emplace_back(this); else entityReadReferenceBuffer.emplace_back(this); + + erbr.maxEntityPathDepth++; } - GetAllDeeplyContainedEntityReferencesGroupedByDepthRecurse(); + size_t max_depth = 0; + GetAllDeeplyContainedEntityReferencesGroupedByDepthRecurse(0, max_depth); + erbr.maxEntityPathDepth += max_depth; return erbr; } @@ -570,7 +595,9 @@ class Entity template void AppendAllDeeplyContainedEntityReferencesGroupedByDepth(EntityReferenceBufferReference &erbr) { - GetAllDeeplyContainedEntityReferencesGroupedByDepthRecurse(); + size_t max_depth = 0; + GetAllDeeplyContainedEntityReferencesGroupedByDepthRecurse(0, max_depth); + erbr.maxEntityPathDepth += max_depth; } //gets the current state of the random stream in string form @@ -736,8 +763,11 @@ class Entity //helper function for GetAllDeeplyContainedEntityReadReferencesGroupedByDepth template - bool GetAllDeeplyContainedEntityReferencesGroupedByDepthRecurse() + bool GetAllDeeplyContainedEntityReferencesGroupedByDepthRecurse(size_t cur_depth, size_t &max_depth) { + if(cur_depth > max_depth) + max_depth = cur_depth; + if(!hasContainedEntities) return true; @@ -758,7 +788,7 @@ class Entity for(auto &ce : contained_entities) { - if(!ce->GetAllDeeplyContainedEntityReferencesGroupedByDepthRecurse()) + if(!ce->GetAllDeeplyContainedEntityReferencesGroupedByDepthRecurse(cur_depth + 1, max_depth)) return false; } diff --git a/src/Amalgam/interpreter/Interpreter.cpp b/src/Amalgam/interpreter/Interpreter.cpp index 7f114bd1..e23042c8 100644 --- a/src/Amalgam/interpreter/Interpreter.cpp +++ b/src/Amalgam/interpreter/Interpreter.cpp @@ -767,7 +767,8 @@ EvaluableNode *Interpreter::RewriteByFunction(EvaluableNodeReference function, E return result; } -bool Interpreter::PopulatePerformanceConstraintsFromParams(std::vector ¶ms, size_t perf_constraint_param_offset, PerformanceConstraints &perf_constraints) +bool Interpreter::PopulatePerformanceConstraintsFromParams(std::vector ¶ms, + size_t perf_constraint_param_offset, PerformanceConstraints &perf_constraints, bool include_entity_constraints) { //start with constraints if there are already performance constraints bool any_constraints = (performanceConstraints != nullptr); @@ -792,10 +793,10 @@ bool Interpreter::PopulatePerformanceConstraintsFromParams(std::vector nodes_allowed_offset) + size_t max_num_allocated_nodes_offset = perf_constraint_param_offset + 1; + if(params.size() > max_num_allocated_nodes_offset) { - double value = InterpretNodeIntoNumberValue(params[nodes_allowed_offset]); + double value = InterpretNodeIntoNumberValue(params[max_num_allocated_nodes_offset]); //nan will fail, so don't need a separate nan check if(value >= 1.0) { @@ -805,10 +806,10 @@ bool Interpreter::PopulatePerformanceConstraintsFromParams(std::vector stack_depth_allowed_offset) + size_t max_opcode_execution_depth_offset = perf_constraint_param_offset + 2; + if(params.size() > max_opcode_execution_depth_offset) { - double value = InterpretNodeIntoNumberValue(params[stack_depth_allowed_offset]); + double value = InterpretNodeIntoNumberValue(params[max_opcode_execution_depth_offset]); //nan will fail, so don't need a separate nan check if(value >= 1.0) { @@ -817,10 +818,61 @@ bool Interpreter::PopulatePerformanceConstraintsFromParams(std::vector max_contained_entities_offset) + { + double value = InterpretNodeIntoNumberValue(params[max_contained_entities_offset]); + //nan will fail, so don't need a separate nan check + if(value >= 0.0) + { + perf_constraints.constrainMaxContainedEntities = true; + perf_constraints.maxContainedEntities = static_cast(value); + any_constraints = true; + } + } + + //populate maxContainedEntityDepth + size_t max_contained_entity_depth_offset = perf_constraint_param_offset + 4; + if(params.size() > max_contained_entity_depth_offset) + { + double value = InterpretNodeIntoNumberValue(params[max_contained_entity_depth_offset]); + //nan will fail, so don't need a separate nan check + if(value >= 0.0) + { + perf_constraints.constrainMaxContainedEntityDepth = true; + perf_constraints.maxContainedEntityDepth = static_cast(value); + any_constraints = true; + } + } + + //populate maxEntityIdLength + size_t max_entity_id_length_offset = perf_constraint_param_offset + 5; + if(params.size() > max_entity_id_length_offset) + { + double value = InterpretNodeIntoNumberValue(params[max_entity_id_length_offset]); + //nan will fail, so don't need a separate nan check + if(value >= 1.0) + { + perf_constraints.maxEntityIdLength = static_cast(value); + any_constraints = true; + } + } + } + return any_constraints; } -void Interpreter::PopulatePerformanceCounters(PerformanceConstraints *perf_constraints) +void Interpreter::PopulatePerformanceCounters(PerformanceConstraints *perf_constraints, Entity *entity_to_constrain_from) { if(perf_constraints == nullptr) return; @@ -892,6 +944,67 @@ void Interpreter::PopulatePerformanceCounters(PerformanceConstraints *perf_const perf_constraints->maxOpcodeExecutionDepth = 1; } } + + if(entity_to_constrain_from == nullptr) + return; + + perf_constraints->entityToConstrainFrom = entity_to_constrain_from; + + if(performanceConstraints != nullptr && performanceConstraints->constrainMaxContainedEntities + && performanceConstraints->entityToConstrainFrom != nullptr) + { + perf_constraints->constrainMaxContainedEntities = true; + + //if calling a contained entity, figure out how many this one can create + size_t max_entities = performanceConstraints->maxContainedEntities; + if(performanceConstraints->entityToConstrainFrom->DoesDeepContainEntity(perf_constraints->entityToConstrainFrom)) + { + auto erbr = performanceConstraints->entityToConstrainFrom->GetAllDeeplyContainedEntityReferencesGroupedByDepth(); + size_t container_total_entities = erbr->size(); + erbr.Clear(); + erbr = perf_constraints->entityToConstrainFrom->GetAllDeeplyContainedEntityReferencesGroupedByDepth(); + size_t contained_total_entities = erbr->size(); + erbr.Clear(); + + if(container_total_entities >= performanceConstraints->maxContainedEntities) + max_entities = 0; + else + max_entities = performanceConstraints->maxContainedEntities - (container_total_entities - contained_total_entities); + } + + perf_constraints->maxContainedEntities = std::min(perf_constraints->maxContainedEntities, max_entities); + } + + if(performanceConstraints != nullptr && performanceConstraints->constrainMaxContainedEntityDepth + && performanceConstraints->entityToConstrainFrom != nullptr) + { + perf_constraints->constrainMaxContainedEntityDepth = true; + + size_t max_depth = performanceConstraints->maxContainedEntityDepth; + size_t cur_depth = 0; + if(performanceConstraints->entityToConstrainFrom->DoesDeepContainEntity(perf_constraints->entityToConstrainFrom)) + { + for(Entity *cur_entity = perf_constraints->entityToConstrainFrom; + cur_entity != performanceConstraints->entityToConstrainFrom; + cur_entity = cur_entity->GetContainer()) + cur_depth++; + } + + if(cur_depth >= max_depth) + perf_constraints->maxContainedEntityDepth = 0; + else + perf_constraints->maxContainedEntityDepth = std::min(perf_constraints->maxContainedEntityDepth, + max_depth - cur_depth); + } + + if(performanceConstraints != nullptr && performanceConstraints->maxEntityIdLength > 0) + { + if(perf_constraints->maxEntityIdLength > 0) + perf_constraints->maxEntityIdLength = std::min(perf_constraints->maxEntityIdLength, + performanceConstraints->maxEntityIdLength); + else + perf_constraints->maxNumAllocatedNodes = performanceConstraints->maxEntityIdLength; + } } diff --git a/src/Amalgam/interpreter/Interpreter.h b/src/Amalgam/interpreter/Interpreter.h index 73ddea79..be2845be 100644 --- a/src/Amalgam/interpreter/Interpreter.h +++ b/src/Amalgam/interpreter/Interpreter.h @@ -114,6 +114,21 @@ class PerformanceConstraints //maximum number of nodes allowed to be allocated by this Interpreter and anything called from it. If 0, then unlimited. //will terminate execution if the value is reached size_t maxNumAllocatedNodes; + + //entity from which the constraints are based + Entity *entityToConstrainFrom; + + //constrains the maximum number of contained entities + bool constrainMaxContainedEntities; + size_t maxContainedEntities; + + //constrains how deep entities can be created + bool constrainMaxContainedEntityDepth; + size_t maxContainedEntityDepth; + + //constrains the maximum length of an entity id (primarily to make sure it doesn't cause problems for file systems) + //If 0, then unlimited + size_t maxEntityIdLength; }; class Interpreter @@ -574,10 +589,12 @@ class Interpreter //populates perf_constraints from params starting at the offset perf_constraint_param_offset, in the order of execution cycles, maximum memory, maximum stack depth //returns true if there are any performance constraints, false if not - bool PopulatePerformanceConstraintsFromParams(std::vector ¶ms, size_t perf_constraint_param_offset, PerformanceConstraints &perf_constraints); + //if include_entity_constraints is true, it will include constraints regarding entities + bool PopulatePerformanceConstraintsFromParams(std::vector ¶ms, + size_t perf_constraint_param_offset, PerformanceConstraints &perf_constraints, bool include_entity_constraints = false); //if perf_constraints is not null, populates the counters representing the current state of the interpreter - void PopulatePerformanceCounters(PerformanceConstraints *perf_constraints); + void PopulatePerformanceCounters(PerformanceConstraints *perf_constraints, Entity *entity_to_constrain_from); #ifdef MULTITHREAD_SUPPORT @@ -868,6 +885,38 @@ class Interpreter return (performanceConstraints != nullptr && performanceConstraints->ConstrainedAllocatedNodes()); } + //returns true if it can create a new entity given the constraints + __forceinline bool CanCreateNewEntityFromConstraints(Entity *destination_container, StringInternPool::StringID entity_id, + size_t total_num_new_entities = 1) + { + if(performanceConstraints == nullptr) + return true; + + if(performanceConstraints->maxEntityIdLength > 0 + && string_intern_pool.GetStringFromID(entity_id).size() > performanceConstraints->maxEntityIdLength) + return false; + + //exit early if don't need to lock all contained entities + if(!performanceConstraints->constrainMaxContainedEntities && !performanceConstraints->constrainMaxContainedEntityDepth) + return true; + + auto erbr = performanceConstraints->entityToConstrainFrom->GetAllDeeplyContainedEntityReferencesGroupedByDepth(); + + if(performanceConstraints->constrainMaxContainedEntities) + { + if(erbr->size() + total_num_new_entities > performanceConstraints->maxContainedEntities) + return false; + } + + if(performanceConstraints->constrainMaxContainedEntityDepth) + { + if(1 + erbr.maxEntityPathDepth > performanceConstraints->maxContainedEntityDepth) + return false; + } + + return true; + } + //returns true if there's a max number of execution steps or nodes and at least one is exhausted __forceinline bool AreExecutionResourcesExhausted(bool increment_performance_counters = false) { diff --git a/src/Amalgam/interpreter/InterpreterOpcodesBase.cpp b/src/Amalgam/interpreter/InterpreterOpcodesBase.cpp index 402241e9..9720a4c1 100644 --- a/src/Amalgam/interpreter/InterpreterOpcodesBase.cpp +++ b/src/Amalgam/interpreter/InterpreterOpcodesBase.cpp @@ -524,7 +524,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_CALL_SANDBOXED(EvaluableNo EvaluableNodeReference call_stack = ConvertArgsToCallStack(args, *evaluableNodeManager); node_stack.PushEvaluableNode(call_stack); - PopulatePerformanceCounters(perf_constraints_ptr); + PopulatePerformanceCounters(perf_constraints_ptr, nullptr); Interpreter sandbox(evaluableNodeManager, randomStream.CreateOtherStreamViaRand(), writeListeners, printListener, perf_constraints_ptr); diff --git a/src/Amalgam/interpreter/InterpreterOpcodesCodeMixing.cpp b/src/Amalgam/interpreter/InterpreterOpcodesCodeMixing.cpp index e60f6edc..4dd7d695 100644 --- a/src/Amalgam/interpreter/InterpreterOpcodesCodeMixing.cpp +++ b/src/Amalgam/interpreter/InterpreterOpcodesCodeMixing.cpp @@ -492,6 +492,8 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_INTERSECT_ENTITIES(Evaluab //no longer need entity references erbr.Clear(); + size_t num_new_entities = new_entity->GetTotalNumContainedEntitiesIncludingSelf(); + //get destination if applicable EntityWriteReference destination_entity_parent; StringInternRef new_entity_id; @@ -500,8 +502,12 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_INTERSECT_ENTITIES(Evaluab else destination_entity_parent = EntityWriteReference(curEntity); - if(destination_entity_parent == nullptr) + if(destination_entity_parent == nullptr + || !CanCreateNewEntityFromConstraints(destination_entity_parent, new_entity_id, num_new_entities)) + { + delete new_entity; return EvaluableNodeReference::Null(); + } //accumulate usage if(ConstrainedAllocatedNodes()) @@ -546,6 +552,8 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_UNION_ENTITIES(EvaluableNo //no longer need entity references erbr.Clear(); + size_t num_new_entities = new_entity->GetTotalNumContainedEntitiesIncludingSelf(); + //get destination if applicable EntityWriteReference destination_entity_parent; StringInternRef new_entity_id; @@ -554,8 +562,12 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_UNION_ENTITIES(EvaluableNo else destination_entity_parent = EntityWriteReference(curEntity); - if(destination_entity_parent == nullptr) + if(destination_entity_parent == nullptr + || !CanCreateNewEntityFromConstraints(destination_entity_parent, new_entity_id, num_new_entities)) + { + delete new_entity; return EvaluableNodeReference::Null(); + } //accumulate usage if(ConstrainedAllocatedNodes()) @@ -662,6 +674,8 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_MIX_ENTITIES(EvaluableNode //no longer need entity references erbr.Clear(); + size_t num_new_entities = new_entity->GetTotalNumContainedEntitiesIncludingSelf(); + //get destination if applicable EntityWriteReference destination_entity_parent; StringInternRef new_entity_id; @@ -670,8 +684,12 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_MIX_ENTITIES(EvaluableNode else destination_entity_parent = EntityWriteReference(curEntity); - if(destination_entity_parent == nullptr) + if(destination_entity_parent == nullptr + || !CanCreateNewEntityFromConstraints(destination_entity_parent, new_entity_id, num_new_entities)) + { + delete new_entity; return EvaluableNodeReference::Null(); + } //accumulate usage if(ConstrainedAllocatedNodes()) diff --git a/src/Amalgam/interpreter/InterpreterOpcodesEntityAccess.cpp b/src/Amalgam/interpreter/InterpreterOpcodesEntityAccess.cpp index 8ac1fbf9..467f7f9e 100644 --- a/src/Amalgam/interpreter/InterpreterOpcodesEntityAccess.cpp +++ b/src/Amalgam/interpreter/InterpreterOpcodesEntityAccess.cpp @@ -436,7 +436,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_CALL_ENTITY_and_CALL_ENTIT PerformanceConstraints perf_constraints; PerformanceConstraints *perf_constraints_ptr = nullptr; - if(PopulatePerformanceConstraintsFromParams(ocn, 3, perf_constraints)) + if(PopulatePerformanceConstraintsFromParams(ocn, 3, perf_constraints, true)) perf_constraints_ptr = &perf_constraints; //attempt to get arguments @@ -488,7 +488,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_CALL_ENTITY_and_CALL_ENTIT call_stack = ConvertArgsToCallStack(called_entity_args, called_entity->evaluableNodeManager); } - PopulatePerformanceCounters(perf_constraints_ptr); + PopulatePerformanceCounters(perf_constraints_ptr, called_entity); #ifdef MULTITHREAD_SUPPORT //this interpreter is no longer executing @@ -600,7 +600,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_CALL_CONTAINER(EvaluableNo EvaluableNode *call_stack_args = call_stack->GetOrderedChildNodesReference()[0]; call_stack_args->SetMappedChildNode(ENBISI_accessing_entity, container->evaluableNodeManager.AllocNode(ENT_STRING, cur_entity_sid)); - PopulatePerformanceCounters(perf_constraints_ptr); + PopulatePerformanceCounters(perf_constraints_ptr, container); #ifdef MULTITHREAD_SUPPORT //this interpreter is no longer executing diff --git a/src/Amalgam/interpreter/InterpreterOpcodesEntityControl.cpp b/src/Amalgam/interpreter/InterpreterOpcodesEntityControl.cpp index 25d4530e..a72a36d5 100644 --- a/src/Amalgam/interpreter/InterpreterOpcodesEntityControl.cpp +++ b/src/Amalgam/interpreter/InterpreterOpcodesEntityControl.cpp @@ -378,7 +378,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_CREATE_ENTITIES(EvaluableN entity_container->AddContainedEntityViaReference(new_entity, new_entity_id, writeListeners); - if(new_entity_id == StringInternPool::NOT_A_STRING_ID) + if(new_entity_id == StringInternPool::NOT_A_STRING_ID || !CanCreateNewEntityFromConstraints(entity_container, new_entity_id)) { delete new_entity; new_entity_ids_list->AppendOrderedChildNode(nullptr); @@ -416,11 +416,15 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_CLONE_ENTITIES(EvaluableNo continue; } + auto erbr = source_entity->GetAllDeeplyContainedEntityReferencesGroupedByDepth(); + size_t num_new_entities = erbr->size(); + //create new entity Entity *new_entity = new Entity(source_entity); - //clear previous lock + //clear previous locks source_entity = EntityReadReference(); + erbr.Clear(); //get destination if applicable EntityWriteReference destination_entity_parent; @@ -428,7 +432,8 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_CLONE_ENTITIES(EvaluableNo if(i + 1 < ocn.size()) std::tie(destination_entity_parent, new_entity_id) = InterpretNodeIntoDestinationEntity(ocn[i + 1]); - if(destination_entity_parent == nullptr) + if(destination_entity_parent == nullptr + || !CanCreateNewEntityFromConstraints(destination_entity_parent, new_entity_id, num_new_entities)) { delete new_entity; new_entity_ids_list->AppendOrderedChildNode(nullptr); @@ -571,6 +576,10 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_DESTROY_ENTITIES(Evaluable entity.lock.unlock(); #endif + //accumulate usage -- gain back freed resources + if(ConstrainedAllocatedNodes()) + performanceConstraints->curNumAllocatedNodesAllocatedToEntities -= entity->GetDeepSizeInNodes(); + delete entity; } diff --git a/src/Amalgam/out.txt b/src/Amalgam/out.txt index b18ca1f8..01748b3a 100644 --- a/src/Amalgam/out.txt +++ b/src/Amalgam/out.txt @@ -1042,7 +1042,7 @@ abcdef [1 3] [9 5] --indices-- -["c" "4" "b" "a"] +["c" "b" "a" "4"] [ 0 1 @@ -1054,7 +1054,7 @@ abcdef 7 ] --values-- -[3 "d" 2 1] +[3 2 1 "d"] [ "a" 1 @@ -1075,7 +1075,7 @@ abcdef 4 "d" ] -[3 "d" 2 1 0] +[3 2 1 0 "d"] [ 1 2 @@ -1272,7 +1272,7 @@ current_index: 2 8 ] accum_string "abcdef" - argv ["C:\\Users\\ChristopherHazard\\Desktop\\Howso_repos\\amalgam\\src\\Amalgam\\./amlg_code/full_test.amlg"] + argv ["C:\\Users\\Chris Hazard\\Desktop\\Howso_repos\\amalgam\\src\\Amalgam\\./amlg_code/full_test.amlg"] bar (declare {x 6} (+ x 2) @@ -1285,10 +1285,10 @@ current_index: 2 A {B 2} B 2 } - interpreter "C:\\Users\\ChristopherHazard\\Desktop\\Howso_repos\\amalgam\\x64\\MT_Release_EXE\\Amalgam.exe" + interpreter "C:\\Users\\Chris Hazard\\Desktop\\Howso_repos\\amalgam\\x64\\MT_Release_EXE\\Amalgam.exe" raaa 2 rwww 1 - start_time 1722543041.25241 + start_time 1722895304.196965 www 1 x 12 zz 10 @@ -1315,7 +1315,7 @@ current_index: 2 8 ] accum_string "abcdef" - argv ["C:\\Users\\ChristopherHazard\\Desktop\\Howso_repos\\amalgam\\src\\Amalgam\\./amlg_code/full_test.amlg"] + argv ["C:\\Users\\Chris Hazard\\Desktop\\Howso_repos\\amalgam\\src\\Amalgam\\./amlg_code/full_test.amlg"] bar (declare {x 6} (+ x 2) @@ -1328,10 +1328,10 @@ current_index: 2 A {B 2} B 2 } - interpreter "C:\\Users\\ChristopherHazard\\Desktop\\Howso_repos\\amalgam\\x64\\MT_Release_EXE\\Amalgam.exe" + interpreter "C:\\Users\\Chris Hazard\\Desktop\\Howso_repos\\amalgam\\x64\\MT_Release_EXE\\Amalgam.exe" raaa 2 rwww 1 - start_time 1722543041.25241 + start_time 1722895304.196965 www 1 x 12 zz 10 @@ -1357,7 +1357,7 @@ current_index: 2 8 ] accum_string "abcdef" - argv ["C:\\Users\\ChristopherHazard\\Desktop\\Howso_repos\\amalgam\\src\\Amalgam\\./amlg_code/full_test.amlg"] + argv ["C:\\Users\\Chris Hazard\\Desktop\\Howso_repos\\amalgam\\src\\Amalgam\\./amlg_code/full_test.amlg"] bar (declare {x 6} (+ x 2) @@ -1370,10 +1370,10 @@ current_index: 2 A {B 2} B 2 } - interpreter "C:\\Users\\ChristopherHazard\\Desktop\\Howso_repos\\amalgam\\x64\\MT_Release_EXE\\Amalgam.exe" + interpreter "C:\\Users\\Chris Hazard\\Desktop\\Howso_repos\\amalgam\\x64\\MT_Release_EXE\\Amalgam.exe" raaa 2 rwww 1 - start_time 1722543041.25241 + start_time 1722895304.196965 www 1 x 12 zz 10 @@ -1521,7 +1521,7 @@ infinity test c or d: ["d" @(get (target 2) 0) "c" @(get (target 2) 2)] {a 24 b 52 c 24} -["9" "2" "3"] +["4" "2" "3"] --get_rand_seed-- N9˝UOaVT z @@ -1639,7 +1639,7 @@ e: - .inf 25: {a 1} -current date-time in epoch: 2024-08-01-16.10.41.3242310 +current date-time in epoch: 2024-08-05-18.01.44.7864540 2020-06-07 00:22:59 1391230800 1391230800 @@ -2817,26 +2817,26 @@ _2169689611 ##p ["_3990396532" "_3990396532" "_3330773578" "_3330773578"] ) -_3990396532 +_3330773578 (associate - "E" + "e" 3 - "F" + "f" 4 - "G" + "g" 5 - "H" + "h" 6 ) -_3330773578 +_3990396532 (associate - "e" + "E" 3 - "f" + "F" 4 - "g" + "G" 5 - "h" + "H" 6 ) --difference_entities-- @@ -3167,14 +3167,8 @@ _432807187 [] (lambda { - E (get - (current_value 1) - "E" - ) - F (get - (current_value 1) - "F" - ) + E 3 + F 4 G 5 H 6 } @@ -3199,16 +3193,7 @@ _432807187 _ [] (lambda - { - e (get - (current_value 1) - "e" - ) - f (get - (current_value 1) - "f" - ) - } + {e 3 f 4} ) ) ) @@ -3240,89 +3225,7 @@ contained_entities new_entity: ["DiffEntityChild1" "OnlyIn2" "_350925375" "_2742 difference between DiffEntity2 and new_entity: (declare {_ (null) new_entity (null)} - (assign - "new_entity" - (first - (create_entities - new_entity - (call - (lambda - (declare - {_ (null)} - (replace _) - ) - ) - { - _ (retrieve_entity_root _) - } - ) - ) - ) - ) - (create_entities - (append new_entity "_350925375") - (call - (lambda - (declare - {_ (null)} - (replace - _ - [] - (lambda - { - E (null) - F (null) - G (get - (current_value 1) - "G" - ) - H (get - (current_value 1) - "H" - ) - } - ) - ) - ) - ) - { - _ (retrieve_entity_root - (append _ "_350925375") - ) - } - ) - ) - (create_entities - (append new_entity "_2742810479") - (call - (lambda - (declare - {_ (null)} - (replace - _ - [] - (lambda - {e (null) f (null)} - ) - ) - ) - ) - { - _ (retrieve_entity_root - (append _ "_2742810479") - ) - } - ) - ) - (clone_entities - (append _ "DiffEntityChild1") - (append new_entity "DiffEntityChild1") - ) - (clone_entities - (append _ "OnlyIn2") - (append new_entity "OnlyIn2") - ) - new_entity + (clone_entities _ new_entity) ) (declare {_ (null) new_entity (null)} @@ -3540,7 +3443,7 @@ deep sets --set_entity_root_permission-- RootTest -1722543041.603805 +1722895305.104673 (true) RootTest @@ -4469,6 +4372,8 @@ cyclic test expected: 155, 200, 190 ... deg values of 0 8 and 12: "Child7" ] 6 +execution limits tests +166 --call_entity_get_changes-- 4 6 @@ -4568,7 +4473,7 @@ entity cyclic test: (assign_entity_roots new_entity _) ) ) - (set_entity_rand_seed new_entity "b\tݒ>kER") + (set_entity_rand_seed new_entity "ܑ$[$0") new_entity ) 3 @@ -4598,7 +4503,7 @@ entity cyclic test: (assign_entity_roots new_entity _) ) ) - (set_entity_rand_seed new_entity "b\tݒ>kER") + (set_entity_rand_seed new_entity "ܑ$[$0") new_entity ) cyclic lookup test: @@ -4882,4 +4787,4 @@ concurrent entity writes successful: (true) --clean-up test files-- --total execution time-- -2.8818700313568115 +1.6452000141143799