Skip to content

The Green Runtime (--green)

--green builds the server on an experimental, from-scratch stackful-coroutine + epoll runtime (runtime/green/) instead of the default thread-per-connection model.

sh
./weld examples/calc.weld --green --emit bin -O ReleaseSafe
./weld examples/db.weld   --green --profile --emit bin -O ReleaseSafe

What it is

  • Each connection is a cheap green task (a coroutine), not an OS thread.
  • One green event loop runs per core (SO_REUSEPORT, thread-local schedulers), so the kernel load-balances connections across cores.
  • A proper HTTP/1.1 connection: keep-alive, fragmented / large request accumulation, per-socket idle timeouts (slow-loris guard), and graceful shutdown across all carriers.
  • The context switch is hand-written per target, so --green cross-compiles and runs on x86_64, aarch64, and riscv64 (all validated under qemu; other arches are a clean compile error).

Preemption

CPU-bound handlers can't monopolise a carrier:

  • Every loop back-edge is a cooperative yield point (__coop), keeping /ping at p90 ~3 ms while 24 compute-heavy requests saturate all carriers.
  • For code with no such points (a loop-free blob, or a big JSON parse inside std), a sysmon thread signals real async preemption (SIGURG; the handler swaps out of the coroutine and sigreturn restores it later — arch-agnostic), so even those can't monopolise a carrier (p90 ~50 ms with cooperative yielding fully disabled).

Non-blocking outbound I/O

Outbound Postgres, gRPC, and HTTP and HTTPS upstreams run on a green std.Io — their socket ops park the coroutine on epoll instead of blocking the carrier, so a slow call doesn't stall its neighbours. HTTP uses a pooled keep-alive green client; HTTPS runs std.crypto.tls over the same green socket. WebSocket hubs work too — an upgrade builds a live green socket, so idle readers yield instead of pinning a carrier. Pool and lock waits spin-yield rather than block the carrier.

Memory

Handlers borrow their large buffers from a per-thread pool, so coroutine stacks are 64 KB (256 KB with upstreams/TLS) and mmap'd with a guard page. An idle keep-alive connection holds ~64 KB + a ~16 KB grow-on-demand request buffer.

When to use it

--green targets high-concurrency, I/O-bound workloads: many connections, slow upstreams, lots of idle keep-alive sockets. It has been soak-tested (bench/) under 150 s of adversarial mixed load — keep-alive floods, malformed/partial requests, connection churn, slow-loris — with flat RSS (~11 MB), no fd leak, and zero crashes.

Add --profile for a per-phase, per-route timing breakdown printed on shutdown.

Experimental

The green runtime is experimental. The default thread-per-connection server remains the conservative choice; reach for --green when concurrency and outbound-I/O overlap are the bottleneck.