# tt - Tiny Threads

A single-header library for protothread-style cooperative "tiny threads" in
C. A tiny thread is an ordinary function that can wait partway through and
resume later from the same point, without its own stack or any static state. It
is useful for state machines, event loops, and cooperative multitasking on
small systems.

The whole implementation is in `tt.h`. There is nothing to build or link.

## How it works

A tiny thread is a function that returns a status value and keeps its progress
in a caller-owned `tt_state_t`. The body sits between `TT_BEGIN` and `TT_END`.
Each call resumes at the point where the previous call chose to wait. Under the
hood this is a `switch` on the saved line number, so a few rules apply:

- Everything before the first wait runs once per start or restart.
- Local variables do not survive across a wait, because the function returns
  and is called again. Keep anything that must persist in the struct passed by
  the caller.
- A wait macro must not sit inside a `switch` of your own, since the
  `TT_BEGIN`/`TT_END` pair is itself a `switch`.

## Example

```c
#include "tt.h"
#include <stdio.h>

struct worker {
    tt_state_t tts;
    int pending;    // work handed in by the outside world
    int processed;  // running total, must persist across waits
};

unsigned worker_th(struct worker *w)
{
    TT_BEGIN(&w->tts);
    // This block runs once each time the thread starts or restarts.
    w->processed = 0;

    // 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);
    }

    printf("worker: processed %d item(s)\n", w->processed);
    TT_END(&w->tts);
}
```

Drive it from a scheduler loop, calling it repeatedly until it exits:

```c
struct worker w = { TT_INITIALIZER, 0, 0 };
w.pending = 4;
while (worker_th(&w) != TT_STATUS_EXITED)
    ; // do other work, then call again
```

See the comment at the bottom of `tt.h` for a fuller walkthrough.

## Macros

| Macro | Purpose |
| --- | --- |
| `TT_BEGIN(tts)` | Start of the thread body. Code before the first wait runs once per start. |
| `TT_END(tts)` | End of the thread body. Falls through to `TT_EXIT`. |
| `TT_WAIT_UNTIL(tts, cond)` | Wait until `cond` is true. Re-tests `cond` on each call. |
| `TT_WAIT_WHILE(tts, cond)` | Wait while `cond` is true. Re-tests `cond` on each call. |
| `TT_YIELD(tts)` | Yield once, then continue on the next call. Does not test a condition. |
| `TT_RESTART(tts)` | Restart the thread from the beginning on the next call. |
| `TT_EXIT(tts)` | Finish the thread and reset its state. |
| `TT_INITIALIZE(tts)` | Reset a thread's state so it starts from the beginning. |
| `TT_INITIALIZER` | Static initializer for a `tt_state_t` field. |
| `TT_LINE(tts)` | Source line the thread is waiting on, for debugging. |

A thread function returns `TT_STATUS_WAITING` while it still needs to be called
again, and `TT_STATUS_EXITED` once it has finished.

`TT_YIELD` differs from the wait macros in an important way. The wait macros
re-test their condition at the resume point, so any statement placed just before
them is skipped when the thread resumes. `TT_YIELD` resumes after its return, so
it runs code before it exactly once and is the right tool for stepping through
work one item per call.

## Building the test

```sh
make test
```

This compiles and runs `test_tt`, which exercises every macro.

## License

`tt.h` is released under 0BSD or CC0-1.0, at your option. See the header for
the SPDX identifier.
