π Nylon Ring
Ultra-Fast ABI-Stable HostβPlugin Interface for Rust
Build fast native plugins with an explicit C-compatible ABI.
Features β’ Quick Start β’ Performance β’ System Overview
π Features
- Stable ABI v1 β C-compatible types, fixed status values, version and structure-size validation.
- Flexible calls β fire-and-forget, async unary, synchronous fast path, and bounded streaming with backpressure.
- Safe lifecycle β in-flight call guards, graceful unload/reload, timeouts, and automatic request cleanup.
- Cross-language validation β Rust examples plus a buildable C plugin.
The workspace provides:
nylon-ringβ ABI types, vtables, and thedefine_plugin!macro.nylon-ring-hostβ dynamic loading, routing, streaming, lifecycle controls, and metrics.
MSRV: Rust 1.88.
π Quick start
Install
# Plugin
[]
= "0.1.3"
# Host
[]
= "0.1.3"
Plugin crates must also build as a dynamic library:
[]
= ["cdylib"]
Host
use ;
# async
Other call patterns:
plugin.call.await?;
plugin.call_response_fast.await?;
plugin.call_response_timeout.await?;
let = plugin.call_stream.await?;
See the complete Rust plugin example and Rust host example.
Run the workspace demo:
π Performance
Reference snapshot on an Apple M1 Pro (8 performance + 2 efficiency cores),
release build. All host numbers come from the worker-loop harness in
ex-nyring-host: workers await each call sequentially (in-flight depth 1 per
worker) and every counted call is asserted Ok. Single-stream is the same
harness at one worker, so the two columns are directly comparable. Each value
is the median of three runs.
Throughput
| Host operation | 1 worker | 10 workers | Scaling |
|---|---|---|---|
| Fire-and-forget | 86.09M calls/s (11.6 ns) | 537.35M calls/s | 6.2Γ |
| Synchronous fast path | 42.59M calls/s (23.5 ns) | 301.46M calls/s | 7.1Γ |
| Standard unary | 23.34M calls/s (42.8 ns) | 163.93M calls/s | 7.0Γ |
| Streaming | 14.97M frames/s (66.8 ns) | 61.27M frames/s | 4.1Γ |
Streaming times full 9-frame round trips (8 data frames + StreamEnd); its
scaling is currently bounded by per-stream channel setup.
Unary payload curve (1 worker)
| Payload | Time per call | Throughput |
|---|---|---|
| empty | 42.8 ns | 23.34M calls/s |
| 128 B | 82.0 ns | 12.19M calls/s |
| 1 KiB | 141.8 ns | 7.05M calls/s |
| 4 KiB | 217.4 ns | 4.60M calls/s |
Non-empty payloads pay two alloc/free pairs and two copies under ABI v1: the response crosses the boundary as a foreign allocation and is copied into host-owned memory because allocator provenance cannot be proven across images.
These are reference measurements, not cross-platform guarantees; absolute
numbers shift a few percent with thermal and scheduling state. Numbers
published before 0.1.3 used per-iteration block_on (single-stream) and a
join_all batch loop (multi-core) and are not comparable with this table.
Reproduce with:
NYRING_BENCH_OPERATION=all NYRING_BENCH_WORKERS=10
# NYRING_BENCH_OPERATION: fire | fast | unary | stream | all
# Also: NYRING_BENCH_WORKERS, NYRING_BENCH_SECONDS, NYRING_BENCH_BATCH_SIZE,
# NYRING_BENCH_PAYLOAD_BYTES, NYRING_BENCH_CPU_SAMPLES
π System overview
+------------------------------------------------------+
| Host (nylon-ring-host) |
| NylonRingHost |
| ββ LoadedPlugin + in-flight call gate |
| ββ HostContext |
| ββ thread-local synchronous slot |
| ββ sharded unary router + inline completion |
| ββ bounded stream queues |
+-------------------------+----------------------------+
| C ABI v1
| NrPluginVTable / NrHostVTable
+-------------------------+----------------------------+
| Plugin |
| define_plugin! β named handlers β send_result |
+------------------------------------------------------+
The host validates and initializes a library, assigns each call a session ID, then routes plugin callbacks to the matching unary request or stream. Call guards keep the library loaded until active work finishes; graceful unload/reload stops new calls and waits for existing calls to drain.
π‘ Safety
- Host and plugin must use the same target and ABI version.
NrStrandNrBytesare borrowed; copy them before retaining their data.- Owned
NrVec<T>values carry the producer's drop callback, avoiding cross-allocator frees. - Plugins must stop worker threads and callbacks during shutdown.
- Only load trusted native libraries; this is not a security sandbox.
Development
Publishing is handled by GitHub Actions in dependency order using the
organization-level RUST_TOKEN secret.