/* test_tt.c : exercises the tt.h tiny-thread macros * SPDX-License-Identifier: 0BSD or CC0-1.0 * 2026 Jon Mayo */ #include "tt.h" #include #include struct worker { tt_state_t tts; int pending; // work handed in by the outside world int busy; // a downstream stays busy until it clears this int processed; // running total, must persist across waits }; /** A worker thread that waits for work, processes it, then waits for a * downstream to go idle before reporting done. * * The wait macros re-test their condition on every resume, so they block on * state the outside world owns (pending, busy), not on locals of this * function. Returns TT_STATUS_WAITING until it finishes, then * TT_STATUS_EXITED. */ static unsigned worker_th(struct worker *w) { TT_BEGIN(&w->tts); // This block runs once each time the thread starts or restarts. w->processed = 0; printf("worker: ready\n"); // Block until the outside world hands in some work. TT_WAIT_UNTIL(&w->tts, w->pending > 0); // Drain the work one item per call, yielding between items. while (w->pending > 0) { w->pending--; w->processed++; TT_YIELD(&w->tts); } // Block while a downstream is still busy. TT_WAIT_WHILE(&w->tts, w->busy); printf("worker: processed %d item(s)\n", w->processed); TT_END(&w->tts); } struct pinger { tt_state_t tts; int remaining; }; /** A thread that restarts a fixed number of times before exiting. * * Demonstrates TT_RESTART and that TT_BEGIN re-runs its prologue each restart. */ static unsigned pinger_th(struct pinger *p) { TT_BEGIN(&p->tts); printf("ping (%d left)\n", p->remaining); if (p->remaining > 0) { p->remaining--; TT_RESTART(&p->tts); } TT_END(&p->tts); } int main(void) { struct worker w = { TT_INITIALIZER, 0, 1, 0 }; int tick, ran = 0; unsigned status = TT_STATUS_WAITING; // Drive the worker from a scheduler loop. The outside world hands in // work at tick 3 and lets the downstream go idle at tick 6. for (tick = 0; tick < 100; tick++) { if (tick == 3) w.pending = 4; if (tick == 6) w.busy = 0; status = worker_th(&w); if (status == TT_STATUS_EXITED) break; printf("tick %d: waiting at line %u\n", tick, TT_LINE(&w.tts)); ran++; } printf("worker: exited\n"); assert(status == TT_STATUS_EXITED); assert(w.processed == 4); assert(w.pending == 0); assert(ran > 0); // Drive the pinger until it exits. struct pinger p = { TT_INITIALIZER, 3 }; int spins = 0; while (pinger_th(&p) != TT_STATUS_EXITED) { spins++; assert(spins < 100); // guard against a runaway loop } assert(p.remaining == 0); assert(spins == 3); // A finished thread resets itself, so it can be reused immediately. assert(TT_LINE(&p.tts) == 0); printf("all tests passed\n"); return 0; }