Skip to content

Controlling the clock

The kernel can publish a clock object that lets you control the advance of virtual time.

runMode: virtualTime means the components advance only when something steps them, so a kernel started that way and left alone sits there doing nothing. That is the mode this page is about. If what you want instead is virtual time that runs on its own, as fast as the components allow, use runMode: virtualTimeRunning: the kernel advances the clock itself and you still get virtual time rather than the system clock. The Execution model lists all four run modes.

You can enable it by adding this to your configuration file:

kernel:
  runMode: virtualTime
  clockMaster: true

# ... rest of the configuration file

This will make the kernel publish an object called "master" in a bus. That bus is clockBus, which defaults to the kernel's own bus, which in turn defaults to local.kernel. You can point the clock somewhere else by setting clockBus, for example:

kernel:
  runMode: virtualTime
  clockMaster: true
  clockBus: se.clocks

# ... rest of the configuration file

The object you get is a VirtualMasterClock. There are three clock classes, and it helps to see them together:

kernel_objects.stl
// Allows inspecting and controlling the advance of virtual time and associated component iteration
class VirtualClock
{
  // the current virtual time
  var time : TimeStamp;
}

// A clock for all the components loaded by the kernel
class VirtualKernelClock: extends VirtualClock
{
  // updates the time, drains the inputs, and cycles components - doesn't flush outputs
  fn processNoFlush(delta : Duration) -> Duration [bestEffort];

  // makes component outputs visible to other components and returns the next step duration
  fn flushOutputs() -> Duration [bestEffort];
}

// Controls all the VirtualKernelClock that it discovers in the bus where it's published.
// One step reaches the next component due to run: each clock reports how long until its
// own next one, and the soonest wins
class VirtualMasterClock: extends VirtualClock
{
  // steps until the virtual time advanced reaches duration, rounding up to a whole
  // step. Returns the wall time the call took
  fn advanceTime(duration: Duration) -> Duration [bestEffort];

  // single step forward
  fn step() -> Duration [bestEffort];

  // perform 'count' steps forward
  fn steps(count: u64) -> Duration [bestEffort];
}

VirtualClock is the part you read: time is the current virtual time. Every kernel publishes a VirtualKernelClock for the components it hosts, and the master discovers those and drives them.

The step size is not yours to set. Each kernel clock reports how long until the next component it hosts is due to run, and the master takes the smallest of those replies. So time stops at the next thing that has to run rather than advancing by a fixed amount, and steps are not all the same length. Components keep their own freqHz: with one at 100 Hz and one at 10 Hz, time stops every 10 ms, and the slower one runs on every tenth of those stops.

Stepping the time

  1. Start your kernel in virtualized time mode.
  2. Tell it to instantiate a clock by setting clockMaster to true.
  3. Find the "master" object and call step(), steps(n) or advanceTime(duration) on it.

For example, here we run the "school" example and use the following kernel config:

kernel:
  runMode: virtualTime
  clockMaster: true

We use the kernel clock to step the time and monitor one object in the explorer, plotting one of its properties. As you step, you can see the properties change and the plot update.

Advancing large chunks of time

Simply call advanceTime(duration) where duration is the length that you want. The explorer shows the results 1.

Controlling multiple clocks across processes and computers

When you have multiple processes and want to control all their clocks from a single location, you should tell all the processes to publish their clock in some common bus. Then select the "master" process (only one) and tell it to publish the master clock to the same bus. The master clock will discover and control all the clocks that it finds in the bus.

For example, the processes being controlled would have something like this:

Controlled process configuration
kernel:
  runMode: virtualTime
  clockBus: se.clocks

This makes them publish their internal clocks to the "se.clocks" bus, but they don't publish any master clock.

In your master process you would do:

Master app configuration
kernel:
  runMode: virtualTime
  clockBus: se.clocks
  clockMaster: true

Now you will find an object called "master" in the "se.clocks" bus that you can use to control the global advancement of time 😃


  1. Note that the explorer is not plotting all the changes that happen to the property (you see some "jumps" in the plot graphic). This is expected. The reason is that the explorer is actually sampling the values of those properties in real time (at 60 Hz or so), while those properties are changing very fast. ↩