Under development — the help is being filled in plugin by plugin.
Plugin reference

timer

Clocks, sleeping, and scheduled one-shot or repeating callbacks.

7 functionsnamespace timersource plugins/timer/TimerPlugin.c

Written from a line-by-line source review; deterministic examples from a real run.

Introduction

The timer plugin provides time measurement and script-driven delays: two clocks (monotonic — never moves backward, and wall-clock UTC epoch — for logging), formatted local time, blocking wait, and asynchronous, callback-based one-shot or repeating schedules. It offers two distinct styles: SYNCHRONOUS operations on the main thread (MonotonicMs / EpochMs / NowFormat / SleepMs), and ASYNCHRONOUS scheduling with a background thread (Schedule / ScheduleRepeating / Cancel).

Two clocks: monotonic and epoch

MonotonicMs returns the milliseconds elapsed since program start. Guaranteed MONOTONIC: never moves backward, even if the system clock does (NTP synchronization, manual adjustment). It is the foundation for benchmarks, elapsed-time measurement, and timeouts. EpochMs returns the milliseconds elapsed since 1970-01-01 UTC — it tracks the system wall-clock, so use it for logging, timestamping, and aligning with other systems' time data, but NOT for benchmarking, because it can jump backward.

The background thread

Asynchronous scheduling is implemented by a single-threaded background worker that queues scheduled callbacks and invokes them at the right time. The background thread starts on the first Schedule/ScheduleRepeating call, and stops automatically when the script ends. The callbacks run on the DominScript runtime thread (NOT on the background thread), so they can safely use the script's own data state.

The callback signature

The scheduled callback signature is `callback i32 NAME(ref blob $X)`, where the borrowed buffer holds a TimerEvent struct in little-endian: i64 timerId (the id returned by Schedule), i32 sequence (0 = first shot, 1+ = repeats), i32 _padding (for alignment), i64 scheduledEpochMs (when it *should have* fired), i64 actualEpochMs (when it *actually* fired). The difference between scheduledEpochMs and actualEpochMs gives the real lag — on a loaded system it can be several ms.

Loading the plugin

plugin "../plugins/print/PrintPlugin";
plugin "../plugins/timer/TimerPlugin";

A typical flow — measuring elapsed time

i32 $a;
i32 $b;
$a = timer.MonotonicMs();
// ... operation under measurement ...
$b = timer.MonotonicMs();
printf("elapsed: %d ms\n", $b - $a);

A typical flow — repeating callback

callback i32 OnTick(ref blob $X)
{
// ... processing every 100 ms ...
return(1);
}
i64 $tid;
$tid = timer.ScheduleRepeating("OnTick", 100, 100);
// ... later:
timer.Cancel($tid);

What to know about every function (the basics)

  • MonotonicMs never moves backward; EpochMs can (due to NTP synchronization). Choice: benchmark vs log.

  • NowFormat takes a strftime format (e.g. %Y-%m-%d %H:%M:%S) and returns LOCAL time — not UTC.

  • SleepMs blocks: the whole script thread waits. For asynchronous waiting, use Schedule.

  • Schedule and ScheduleRepeating return immediately with a timerId (i64); the callback fires on the main script thread under the background thread's timing.

  • Cancel returns 1 if the timer was removed from the queue; 0 if it has already fired, the ID doesn't exist, or the callback is currently running.

  • A negative ms value (SleepMs, Schedule delay) raises a runtime error. ScheduleRepeating requires a positive intervalMs.

  • An error (stopping the script) is caused by a wrong argument count or type, a negative value, an invalid callback name, and a failed background-thread startup.

How to read the signatures

ms is the time in milliseconds (i32). timerId is a 64-bit identifier returned by Schedule/ScheduleRepeating and accepted by Cancel. callbackName is the textual name of a script-level callback function. The type after the -> arrow is the return type.

Timestamps

Two clocks and a formatted text time: the monotonic clock for elapsed-time measurement, the wall-clock epoch clock for logging, and NowFormat for human-readable timestamps.

timer.MonotonicMs

timer.MonotonicMs() -> int

Returns the milliseconds elapsed since program start. Guaranteed monotonic — never moves backward.

Parameters

This function takes no arguments.

Return value

The elapsed ms (i64).

Example
i32 $a;
i32 $b;
$a = timer.MonotonicMs();
timer.SleepMs(30);
$b = timer.MonotonicMs();
printf("%d\n", ($b - $a >= 30));
Output after running
1
When to use

For elapsed-time measurement, benchmarks, frame timing, timeouts. Never use it for time-of-day logging (its zero point is not fixed; it's only program start).

timer.EpochMs

timer.EpochMs() -> int

Returns the milliseconds elapsed since 1970-01-01 UTC (Unix epoch, wall-clock time).

Parameters

This function takes no arguments.

Return value

The epoch ms (i64).

Example
printf("%d\n", (timer.EpochMs() > 1700000000000));
Output after running
1
When to use

For timestamping log entries, aligning with other systems' time values, and business logic. Do NOT use it for benchmarking: NTP synchronization or manual adjustment can jump it backward.

timer.NowFormat

timer.NowFormat(format) -> string

Returns the text representation of local time according to the strftime-like format.

Parameters
Parameter Type Description
format string An strftime format, for example "%Y-%m-%d %H:%M:%S" (year-month-day hour:minute:second). C-standard strftime directives are available.
Return value

The formatted time as a string (LOCAL time, not UTC).

Example
printf("%s\n", timer.NowFormat("%Y-%m-%d %H:%M:%S"));
Output after running
2026-05-27 14:32:51
When to use

For human-readable timestamps (in logs, headers, user feedback). The format can be assembled at will; the time zone is the host environment's local zone.

Synchronous sleep

Blocks the whole script thread for a given time. For asynchronous waiting, use Schedule instead.

timer.SleepMs

timer.SleepMs(ms) -> int

Blocks the calling thread for the given milliseconds.

Parameters
Parameter Type Description
ms int The wait length in milliseconds (non-negative). 0 returns immediately.
Return value

The given ms value. A negative value raises a runtime error.

Example
printf("%d\n", timer.SleepMs(20));
Output after running
20
When to use

For frame pacing (e.g. 16 ms for ~60 fps), polling delays, or fixed-interval repetition. For longer waits or event-driven logic, the asynchronous Schedule is more appropriate.

Asynchronous scheduling

Schedules callbacks via a background thread. The callback fires on the main script thread at the scheduled time.

timer.Schedule

timer.Schedule(callbackName, delayMs) -> int

Schedules a single callback fire with the given delay.

Parameters
Parameter Type Description
callbackName string The name of the script-level callback to call. Signature: `callback i32 NAME(ref blob $X)`.
delayMs int The delay in milliseconds (non-negative).
Return value

The timerId (positive i64) that you can pass to Cancel. A bad argument or callback name raises a runtime error.

Example
callback i32 OnTick(ref blob $X) { return(1); }
i64 $tid;
$tid = timer.Schedule("OnTick", 50);
printf("%d\n", ($tid > 0));
Output after running
1
When to use

For a delayed action (e.g. a notification after 5 s), or a one-shot feedback. The callback fires AFTER delayMs has elapsed; the actual firing time is shown by the difference of scheduledEpochMs and actualEpochMs.

timer.ScheduleRepeating

timer.ScheduleRepeating(callbackName, firstMs, intervalMs) ->
int

Schedules a repeating callback series: the first fire after firstMs, then every intervalMs.

Parameters
Parameter Type Description
callbackName string The name of the script-level callback to call.
firstMs int The delay of the first fire (non-negative).
intervalMs int The repeat interval in milliseconds (positive).
Return value

The timerId (positive i64). A negative firstMs or a non-positive intervalMs raises a runtime error.

Example
callback i32 OnTick(ref blob $X) { return(1); }
i64 $tid;
$tid = timer.ScheduleRepeating("OnTick", 30, 30);
// about 3 fires in 100 ms
printf("%d\n", ($tid > 0));
Output after running
1
When to use

For periodic work (heartbeat, polling, animation refresh). The `$X.sequence` field of the callback starts at 0 and increments by one per fire — so you can distinguish the first from the later fires.

timer.Cancel

timer.Cancel(timerId) -> int

Attempts to remove a scheduled timer.

Parameters
Parameter Type Description
timerId int The timer id returned by Schedule or ScheduleRepeating.
Return value

1 if it was removed from the queue; 0 if it has already fired, doesn't exist, or is currently running.

Example
i64 $tid;
$tid = timer.ScheduleRepeating("OnTick", 30, 30);
timer.SleepMs(100);
printf("%d\n", timer.Cancel($tid));
Output after running
1
When to use

To stop a repeating callback (worth calling before the end of the main script thread), or to cancel a delayed action. A 0 return is normal even if the timer simply expired.

Practical notes

What the timer plugin is good for

  • Measuring elapsed time and benchmarking (MonotonicMs).

  • Timestamping log entries (EpochMs, NowFormat).

  • Simple synchronous waits (SleepMs) for frame pacing or polling delays.

  • Asynchronous, callback-based scheduling (Schedule, ScheduleRepeating) — timed events, periodic tasks, heartbeats.

Monotonic vs epoch clock: which to use when?

Two clocks, two purposes. MonotonicMs is the rule for benchmarks, elapsed-time measurements, and timeouts: it never moves backward, so every delta is correct. EpochMs is the wall-clock: log entry timestamps, cross-system time alignment, business date/time operations. Never swap the two: if NTP happens to step the system clock backward, an EpochMs-based benchmark can produce an incorrect negative delta.

Callback signature in detail

The timer callback signature is `callback i32 NAME(ref blob $X)`. After `$X assign TimerEvent;`, every timing metadata is accessible from the callback. `$X.timerId` is the id given by the scheduler (same as the return of Schedule). `$X.sequence` is 0 on the first fire and increments by one per fire — so within a single callback you can distinguish the first from later fires. `$X.scheduledEpochMs` and `$X.actualEpochMs` reveal how much the actual fire lagged behind the planned one.

Sleep vs Schedule

SleepMs BLOCKS on the calling thread, so it is simple and predictable, but the script does nothing else in the meantime. Schedule/ScheduleRepeating return immediately, and the background thread schedules the callbacks — suitable for parallel tasks, periodic background work, and scripts that meanwhile do other things on the main thread. For longer (e.g. >1 s) waits, Schedule is almost always the better choice.

Cancel behavior

A success (1) from Cancel means the timer was in the background thread's queue and was removed. A 0 may mean three things: (a) the timer has already fired (a one-shot Schedule earlier), (b) such a timerId never existed, (c) the callback is firing right at this moment. A 0 return is NOT an error, just a state indicator. For repeating timers, Cancel prevents future fires — a fire already in progress can complete.

Error handling

A wrong argument count or type, a negative ms value, a 0 or negative intervalMs (ScheduleRepeating), an empty or unknown callback name, and a failed background-thread startup are reported by the runtime as a runtime error, and the script stops. On a successful call, both timestamps (MonotonicMs/EpochMs) return a meaningful positive value, SleepMs returns the given ms, Schedule* a positive timerId, and Cancel either 1 or 0.

The image image-loading plugin

Loading BMP and PNG images into an OMVF frame packet — complete function reference