Expand description
Custom SPSC shared-memory face (Unix only, spsc-shm feature).
A named POSIX SHM region holds two lock-free single-producer/single-consumer
ring buffers — one for each direction. Wakeup uses a named FIFO (pipe)
pair on all platforms: the engine creates two FIFOs
(/tmp/.ndn-{name}.a2e.pipe and .e2a.pipe); both sides open them
O_RDWR | O_NONBLOCK (avoids the blocking-open problem). The consumer
awaits readability via tokio::io::unix::AsyncFd; the producer writes 1
non-blocking byte. The parked flag in SHM is still used to avoid
unnecessary pipe writes when the consumer is active.
This design integrates directly into Tokio’s epoll/kqueue loop with zero
thread transitions, unlike the previous Linux futex + spawn_blocking
approach which routed every park through Tokio’s blocking thread pool.
§SHM layout
Cache line 0 (off 0–63): magic u64 | capacity u32 | slot_size u32 | pad
Cache line 1 (off 64–127): a2e_tail AtomicU32 — app writes, engine reads
Cache line 2 (off 128–191): a2e_head AtomicU32 — engine writes, app reads
Cache line 3 (off 192–255): e2a_tail AtomicU32 — engine writes, app reads
Cache line 4 (off 256–319): e2a_head AtomicU32 — app writes, engine reads
Cache line 5 (off 320–383): a2e_parked AtomicU32 — set by engine before sleeping on a2e ring
Cache line 6 (off 384–447): e2a_parked AtomicU32 — set by app before sleeping on e2a ring
Data block (off 448–N): a2e ring (capacity × slot_stride bytes)
Data block (off N–end): e2a ring (capacity × slot_stride bytes)
slot_stride = 4 (length prefix) + slot_size (payload area)§Conditional wakeup protocol
The producer checks the parked flag with SeqCst after writing to the
ring; the consumer stores the parked flag with SeqCst before its second
ring check. This total-order guarantee prevents the producer from missing
a sleeping consumer.
Structs§
- Spsc
Face - Engine-side SPSC SHM face.
- Spsc
Handle - Application-side SPSC SHM handle.
Constants§
- DEFAULT_
CAPACITY - Default number of slots per ring.
- DEFAULT_
SLOT_ SIZE - Default slot payload size in bytes (~8.75 KiB). Covers a standard NDN Data packet (≤8800 bytes on most link types) with a small headroom margin. This is the right default for the majority of NDN traffic: Interests (~60–200 bytes), single-packet Data (~4–8 KiB content), iperf probes, ping, management, etc.
- SHM_
SLOT_ OVERHEAD - NDN Data packet wire overhead above the raw content bytes:
Data TLV + Name + MetaInfo + SignatureInfo + SignatureValue. 16 KiB
is generous enough to cover a Data whose content is
mtubytes of payload plus a long name and a large signature (Ed25519, ECDSA with key locator, or a Merkle proof up to a few hundred hashes).
Functions§
- capacity_
for_ slot - Compute ring capacity for a given slot_size, keeping total ring
memory within [
SHM_BUDGET]. Returns at least 16 (to keep some pipeline buffering even for very large slots). - slot_
size_ for_ mtu - Pick a slot size for a face that needs to carry NDN Data whose
content can be up to
mtubytes. Rounds up to the next multiple of 64 bytes so the per-slot stride stays cache-line aligned. Does not clamp to a floor — an explicitmtu = 8800gets a ~25 KiB slot, not the 272 KiB default. If nomtuis specified, the caller should useDEFAULT_SLOT_SIZEdirectly.