Skip to content

Model Execution in ERS: Threading, SubModels, and Safety

In ERS, simulation execution is highly parallel, but also strictly controlled. To ensure both scalability and safety, ERS adopts a model where:

  • Each Simulator is processed by only one thread at a time.
  • All simulation data is encapsulated in a SubModel.
  • The EventMechanism ensures safety, threads enter and exit SubModels.
    • All runtime state is resolved through thread-local context.

This approach eliminates race conditions and avoids the need for fine-grained locks while modeling.

Why Entering & Exiting SubModels matters

A SubModel contains the data and logic for a single Simulator. It includes:

  • The entities and components in that part of the simulation.
  • Scheduled events (local and sync).
  • The Simulator's internal simulation time.

Before a thread can safely operate on a SubModel, it must enter it explicitly:

NOTE: This is handled automatically during execution of the model by the EventMechanism, see diagram below

EnterSubModel(); 
// ... access submodel data, entities, components, schedule events ...
ExitSubModel();

This ensures that:

  1. Only one thread touches that SubModel's data at a time.
  2. The thread-local pointer is updated so runtime queries (like getting the current simulator ID, or sync-event context) resolve correctly.
  3. The SubModel becomes the active simulation context, ensuring correct behavior for all local logic.
  4. User code doesn't need to become complex to retrieve & update submodel data.

Without this entry/exit mechanism, it would be possible for threads to operate on the wrong data or interfere with each other — especially during nested callbacks or sync-events.

Thread-Local Architecture: No Locks Needed

ERS uses thread-local state to store context about the current SubModel and the currently active SyncEvent (if any).

This design has major advantages:

  • All core functions like GetCurrentSimulatorID() or IsInsideSyncEvent() resolve via thread_local variables.
  • Sync-events (with SenderSide and TargetSide) can be nested without confusion.
  • No synchronization primitives (mutexes, atomics) are needed for SubModel access, as threads are isolated by design.

Update flow diagram, showing a bit of the ERS update loop

ModelContainer Update()Give work to JobSystemWorker Thread 1This expanded view represents what allworker threads are doing in parallel.Enter ModelSelection is not determinisic, and doesn't need to beSelect a random simulatorThis step is where the ERS magic happensEstimate time before conflict with dependencyFrom this point this thread executes as the simulator that is selected.All thread_local functions to find information related to the submodel,is found using this referenceEnter SubModelProgress SubModel TimeWe call the event callback here,this callback dictates what kind of event we are executing.There are 3 types of events in ERS: Local, Sync and Internal(hidden)Execute EventLocal Event (UserCode)Sync-EventAll thread_local sync-event information isresolved using this referenceSet Sync-Event as activeSenderSide (UserCode)Move event to target simulatorTargetSide (UserCode)Clear active sync-eventyesevent due and no time conflict with simulator dependencies?time conflict, retry in other simulatorExit SubModelnoReached model endtime?worker exits the modelExit ModelWorker Thread N... same loop as others ...ModelContainer Update calling ThreadEnter jobsystem as a worker thread to help model executionAre There workers left in the model?yesAll workers have exited the model, end time was reachedThe update step is finished:Job System execution:Job System execution:Primary Execution Path

Was this page helpful?