Prerequisites. You can read C, understand event-loop callbacks, and have a C11 compiler, OpenSSL, the rl-c-client source tree, and libuv development files available for the target compiler.
This example integrates one resource rate limit and one latency guard with a libuv loop. Allowed work runs once and reports one measured latency sample; denied work does neither.
This self-contained program uses uv_poll_t handles to observe the runtime’s
nonblocking UDP sockets and a one-shot uv_timer_t for the request deadline.
Socket callbacks drain datagrams, timer callbacks advance retries, and the
admission callback copies the result before stopping the loop.
The application owns the loop handles and request storage. The runtime owns the client and sockets. Stop and close every libuv handle, run the loop until close callbacks drain, and only then destroy the runtime.
flowchart TD
Start["Submit resource limit + latency guard"] --> Watch["Arm uv_poll_t sockets and uv_timer_t"]
Watch --> Run["Run uv_run"]
Run --> Event{"Which callback ran?"}
Event -->|Socket readable| Read["Drain runtime datagrams"]
Event -->|Timer expired| Timeout["Advance timeout and retry state"]
Read --> Decision{"Admission complete?"}
Timeout --> Decision
Decision -->|No| Rearm["Recompute and re-arm deadline"]
Rearm --> Run
Decision -->|Denied| Reject["Skip work and latency report"]
Decision -->|Allowed| Work["Run work, measure it, report one sample"]
Reject --> Close["Close handles before runtime teardown"]
Work --> Close
Install libuv, then build the client library and this folder:
sudo apt-get install libuv1-dev # Debian or Ubuntu
brew install libuv # macOS
make -C ../..
make
export RATELIMITLY_AUTH_KEY=rl-aes1...
./libuv-example
The CMake path builds rl-c-client with the selected compiler, which avoids mixing Visual Studio objects with a Unix or MinGW archive:
cmake -S . -B build
cmake --build build
RATELIMITLY_AUTH_KEY=rl-aes1... ./build/libuv-example
An admitted run exits 0. A policy denial exits 2; setup or transport failure exits 1.
RATELIMITLY_AUTH_KEY is required. Its encoded key ID defaults production P0
discovery to:
_ratelimitly._udp.c-<key-id>.p0.ratelimitly.com
RATELIMITLY_TENANT optionally overrides that key-derived tenant name. For local
testing, bypass DNS with a complete fixed-endpoint pair:
export RATELIMITLY_EXAMPLE_SERVER_HOST=127.0.0.1
export RATELIMITLY_EXAMPLE_SERVER_PORT=39082
Set RATELIMITLY_EXAMPLE_SERVER_HOST and RATELIMITLY_EXAMPLE_SERVER_PORT
together, or set neither. One without the other is a configuration error.
Leave both unset in production, and never commit authentication keys.
The latency guard checks existing samples for libuv-protected-service before
work begins. The latency printed after admission is a different value:
perform_protected_work() measures its small snprintf response construction
with a monotonic clock and reports one new duration to the same tracker.
Resource-denied, latency-denied, cancelled, failed, and unsuccessful-work paths do not report a sample.
perform_protected_work() is intentionally tiny and synchronous. For real nonblocking
work, start the operation only after admission, retain request and connection
state, and measure from asynchronous start to successful completion with a
monotonic clock. Call r_client_admission_report_latency() once on the libuv loop
thread when that operation finishes.
Keep client calls on the loop thread unless explicit serialization is added.
Re-arm the one-shot deadline after each timeout transition because a retry may
publish a different deadline. Use uv_async_t if a worker thread must notify
the loop.
libuv can deliver spurious poll readiness, so a readable callback must tolerate
the runtime reaching EAGAIN without a datagram. Keep exactly one active
uv_poll_t per socket, stop every poll handle before uv_close(), and let close
callbacks drain before destroying runtime-owned sockets.
The source uses uv_poll_init_socket, preserving a native WinSock SOCKET, and the
build files link the required Windows socket and DNS libraries. It therefore
targets Linux, macOS, and Windows. Current repository integration CI executes
this binary only on Ubuntu; macOS and Windows are source/build support claims
for this example, not CI execution claims.
Ubuntu CI verifies allowed, resource-denied, and latency-denied behavior against a synthetic responder. Trusted main runs additionally cover key-derived production P0 discovery and admission. The production latency report is fire-and-forget, so the per-example run proves local send success rather than server acknowledgement.
| Term | Meaning here |
|---|---|
uv_poll_t |
A libuv handle that watches an existing socket for readiness. |
uv_timer_t |
A libuv timer handle used here as a one-shot admission deadline. |
SOCKET |
WinSock’s native socket-handle type, preserved by uv_poll_init_socket. |
| loop thread | The thread calling uv_run() and all client callbacks in this example. |
| latency guard | The pre-work check against previously collected service latency. |
| latency sample | The duration recorded after admitted work completes successfully. |
| CMake | Cross-platform build-system generator provided as an alternative to Make. |