diff options
| author | eunhhu <[email protected]> | 2026-09-29 18:04:24 +0100 |
|---|---|---|
| committer | eunhhu <[email protected]> | 2026-09-29 18:04:43 +0100 |
| commit | 8fe9ce66573be53ff56fe2a8afc693dabeae9585 (patch) | |
| tree | b556788225296e84a6a5082c2927295588a6955a /serve/serve.c | |
| parent | e710b93588782135365f01bb8351edb3bf4f95ac (diff) | |
| download | pure-8fe9ce66573be53ff56fe2a8afc693dabeae9585.tar.gz pure-8fe9ce66573be53ff56fe2a8afc693dabeae9585.zip | |
Tick a stateless app's timers, and wait for them on the monotonic clock
run_timers ticked nothing in an app without states, taking a timer to
be able to change nothing else; but a tick may send, and the outbox is
the app's state (the host gives such an app an owner for it). A timer
that only sends never ticked (review 004, F4). Now an app ticks when it
has states or outputs; one with neither runs on every connection thread
and has nothing a tick could do.
When a timer is due was reckoned on the wall clock: set back, a timer
waited for the time it had reached again (review 004, an earlier P2).
The next tick is now reckoned on the monotonic clock; events still see
the wall clock (now).
check.py's server_timers runs the review's heartbeat (a timer, an
output, no state) and sees it tick, then again with the wall clock set
back an hour 0.2 s in (build/clock-back.so, an LD_PRELOAD of
clock_gettime): 29 ticks in 0.6 s, where the server before this stops at
9.
Diffstat (limited to 'serve/serve.c')
| -rw-r--r-- | serve/serve.c | 23 |
1 files changed, 14 insertions, 9 deletions
diff --git a/serve/serve.c b/serve/serve.c index b271d14..96e1b1d 100644 --- a/serve/serve.c +++ b/serve/serve.c @@ -183,7 +183,7 @@ typedef struct { bool wrote; /* the last round saved an event or an edit */ uint64_t save_waited; /* ns the last save waited for the device (a sync), not working */ bool reads_first; /* the owner's: a round's reads before its writes */ - double next[PURE_MODULE_INPUTS]; /* when each timer ticks next (wall clock, ms) */ + uint64_t next[PURE_MODULE_INPUTS]; /* when each timer ticks next (monotonic clock, ms; 0: not yet begun) */ uint64_t ticks; /* The owner's: the last save's snapshot (NULL: the owner shows views), * taken under the lock, and the app's changes it shows. */ @@ -1648,27 +1648,32 @@ static void execute(Executor *ex, const Job *jobs, size_t count, void (*finish)( if (pinned) pure_store_pin(ex->store, false); } -/* Tick every timer that is due, with one save for them (an app with states - * only: a timer can change nothing else). The milliseconds until the next - * is due, or -1 without timers. Missed ticks are not made up. */ +/* Tick every timer that is due, with one save for them. A tick can change + * states and send (the outbox is the app's state too): an app with neither + * has nothing a tick could do, and runs on every thread, so it ticks none. + * The milliseconds until the next is due, or -1 without timers. Missed + * ticks are not made up. When a timer is due is the monotonic clock's + * business (the wall clock may be set back); events see the wall clock. */ static long run_timers(Executor *ex) { const PureModule *m = ex->module; - if (!m->states) return -1; - double now = wall_ms(), soonest = -1; + bool changes = m->states > 0; + for (uint32_t i = 0; i < m->inputs && !changes; ++i) changes = m->input[i].kind == PURE_KIND_OUTPUT; + if (!changes) return -1; + uint64_t now = now_ms(), soonest = UINT64_MAX; bool ticked = false; for (uint32_t i = 0; i < m->inputs; ++i) { if (m->input[i].kind != PURE_KIND_TIMER) continue; if (!ex->next[i]) ex->next[i] = now + m->input[i].period; /* the first period starts now */ if (ex->next[i] <= now) { - ex->app.now = now; + ex->app.now = wall_ms(); if (pure_app_tick(&ex->app, i) == PURE_OK) ++ex->ticks; ticked = true; ex->next[i] = now + m->input[i].period; } - if (soonest < 0 || ex->next[i] - now < soonest) soonest = ex->next[i] - now; + if (ex->next[i] - now < soonest) soonest = ex->next[i] - now; } if (ticked && pure_app_save(&ex->app) == PURE_OK) after_save(ex); /* a failed save undoes the ticks */ - return soonest < 0 ? -1 : (long)ceil(soonest); + return soonest == UINT64_MAX ? -1 : (long)soonest; } /* On the request's connection thread: complete its ticket and answer it if |
