Prerequisites. You can read C and know what a UDP socket is. Building requires Linux 5.11 or newer, io_uring UAPI headers, a C11 compiler, OpenSSL development files, and a kernel policy that permits
io_uring_setup. Everything else is explained here.
Raw io_uring syscalls drive a request containing a resource rate limit and a
pre-work latency guard without using liburing. Allowed work is measured
afterward and reported as one latency sample; denied, cancelled, or failed work
produces no sample.
This Linux-only example uses the io_uring userspace API and syscalls directly,
without liburing. It maps the submission queue, completion queue, and submission
entry array from offsets returned by io_uring_setup, publishes poll requests
with release ordering, and consumes completions with acquire ordering.
The application submits both policies in one admission request. It performs protected work only after the resource limit and latency guard allow it, then measures and reports successful work once.
Linux UAPI headers are the only io_uring-specific build dependency:
make -C ../..
make
./io-uring-example
cmake -S . -B build
cmake --build build
./build/io-uring-example
If io_uring_setup returns EPERM or ENOSYS, check the kernel version,
container profile, seccomp policy, and host setting before debugging admission.
RATELIMITLY_AUTH_KEY is required. With no overrides, the runtime decodes the
key ID, derives c-<key-id>.p0.ratelimitly.com, and discovers
_ratelimitly._udp.c-<key-id>.p0.ratelimitly.com.
RATELIMITLY_TENANT optionally replaces the key-derived tenant DNS name. For
a fixed development responder, set RATELIMITLY_EXAMPLE_SERVER_HOST and
RATELIMITLY_EXAMPLE_SERVER_PORT together; setting only one is invalid. Leave
all three overrides unset for key-derived P0 discovery.
export RATELIMITLY_AUTH_KEY='rl-aes1...'
# Optional fixed development endpoint; set both or neither.
export RATELIMITLY_EXAMPLE_SERVER_HOST=127.0.0.1
export RATELIMITLY_EXAMPLE_SERVER_PORT=39082
./io-uring-example
flowchart TD
Setup["Map SQ, CQ, and SQE regions"] --> Start["Start resource + latency admission"]
Start --> Publish["Publish POLL_ADD SQEs with release ordering"]
Publish --> Enter["io_uring_enter with deadline"]
Enter --> Result{"CQE or timeout?"}
Result -->|Poll CQE| Consume["Acquire CQE, drain socket, retire slot"]
Result -->|Timeout| Timeout["Advance admission timeout"]
Consume --> Decision{"Admission complete?"}
Timeout --> Decision
Decision -->|No| Publish
Decision -->|Denied| Reject["No protected work or sample"]
Decision -->|Allowed| Work["Run work, measure, report latency"]
The latency guard checks server-side history before work starts; it is separate
from timing the current operation. Once the rate limit and guard both allow the
request, r_runtime_admission_run_and_report() measures the synchronous
prepare_response() callback with a monotonic clock and sends one post-work
sample. It sends none for denied, cancelled, or failed work.
This callback is synchronous only to keep the ring mechanics readable. A production loop should start asynchronous work after admission, retain the request identity and monotonic start time, and report once from successful completion rather than blocking the ring thread.
This source is Linux-only and uses IORING_ENTER_EXT_ARG, whose extended
argument form was added in Linux 5.11. liburing wraps the same kernel interface
and adds compatibility helpers; use it when supporting older kernel/API
combinations matters more than demonstrating the raw mappings.
Ring offsets supplied by the kernel define the mappings. Queue counters are
monotonic and wrap through their masks; a release store publishes a complete
submission, and an acquire load observes a complete completion. POLL_ADD is
one-shot, so the example re-arms it after draining the socket and destroys the
ring before closing runtime-owned sockets.
This folder is Linux-only. Ubuntu CI runs allow, resource-denial, and
latency-denial scenarios against the synthetic responder and verifies exact
request/report pairing. Trusted main runs also test key-derived production P0
discovery and admission; the unacknowledged UDP report proves only its local
send path in that smoke test.
| Term | Meaning |
|---|---|
| UAPI | User-space API exposed by Linux kernel headers. |
| SQE | Submission queue entry describing one operation for the kernel. |
| CQE | Completion queue entry containing an operation’s result and user data. |
POLL_ADD |
One-shot io_uring operation that completes when a descriptor becomes ready. |
| acquire/release ordering | Atomic ordering that makes shared queue writes visible before publication and reads complete before reuse. |
| latency guard | Pre-work check against existing service-latency samples. |