microtimer
Main loops need bounded software timers without allocation or a scheduler.
A fixed-capacity manager fires caller callbacks from explicit tick calls.
The application owns time and calls the tick
microtimer is a fixed-capacity C99 software timer manager for a serialized execution context. It does not create a background thread, own a clock source, allocate a scheduler or hide synchronization. The caller supplies a millisecond clock function and invokes mtimer_tick() from its chosen loop.
Storage size is part of the public manager layout
The manager contains a fixed array sized by MTIMER_MAX_TIMERS. The configuration header rejects values below 1 or above 255. Creation returns a nonnegative slot ID or a negative error; destroying a timer invalidates that ID immediately, and a later create may reuse its slot.
Names are optional. A non-null name remains caller-owned and must stay valid, immutable and unique within the manager for the timer's lifetime. The timer stores a pointer, not a managed copy.
Missed periods are skipped, not replayed in a burst
Clock values are milliseconds on an unsigned 32-bit modulo counter. Natural wrap is supported when the clock is not reset while timers are active and operations remain within a full counter cycle. Each tick captures one clock value and checks timers in ascending slot order.
A periodic timer fires at most once per successful tick. If one interval is due, phase is retained. If several intervals were missed, intermediate callbacks are skipped and the start position is deterministically advanced to account for elapsed whole periods. This bounds callback work after a delayed loop; it does not provide catch-up semantics.
Callbacks run synchronously inside tick
The callback receives a timer slot ID and caller context pointer. Since callbacks execute as part of the tick path, their latency and blocking behavior are charged to the caller's loop. Mutation of the same manager during a callback returns MTIMER_ERR_BUSY.
The default ownership model is one execution context per manager. Shared use requires external serialization around every API call. ISR use has stricter requirements: exclusive ownership or external protection, an ISR-safe bounded clock callback, and callbacks that are ISR-safe, bounded, non-blocking and avoid prohibited same-manager mutations.
Initialization checks caller and library layout
The public manager structure contains the timer array and counters; its layout is part of the ABI. mtimer_init_sized() accepts the caller-visible manager size and returns MTIMER_ERR_ABI when size or resolved configuration disagrees. The inline mtimer_init() passes sizeof(*tm) automatically for a consistently built consumer.
Query APIs use explicit status-plus-output patterns for state, remaining time and fire counts. mtimer_tick(NULL) returns MTIMER_ERR_NULL; callers must not interpret a positive/zero success convention from a different API generation.
mtimer_t tm;
if (mtimer_init(&tm, platform_millis) != MTIMER_OK) return 1;
int id = mtimer_create(&tm, "blink", 500u,
MTIMER_PERIODIC, blink_cb, ctx);
if (id < 0) return 1;
if (mtimer_start(&tm, (uint8_t)id) != MTIMER_OK) return 1;
for (;;) {
if (mtimer_tick(&tm) < 0) return 1;
}Test fixtures exist; the verification record has not run them
The repository contains runtime tests, compile-fail configuration tests, C/C++ consumers, CMake and Make integration, and install-package consumer fixtures. Its checked-in verification status explicitly lists local compilation, runtime tests, compile-fail execution, sanitizers, static analysis, ARM cross-compilation and installed consumer execution as not verified in that record.
CI and release workflows exist, but their presence is configuration evidence. The repository's verification notes do not establish a passing workflow or release for the currently reviewed state.
A software timer table, not a real-time scheduler
- The application must call tick regularly; callback timing follows that cadence and the work in the tick path.
- Missed periodic intervals are skipped, so this is not a queued catch-up timer.
- Timer capacity is compile-time bounded, and IDs are reusable slot indexes.
- Manager access must be serialized by the application; no hidden locks exist.
- ISR usage is conditional and requires bounded ISR-safe callbacks and clock source.
- Longjmp or abnormal exit during a callback has no cleanup guarantee and may leave the active-tick guard set.
- Current verification documentation lists execution and hardware/toolchain checks as unverified.