Module spsc

Module spsc 

Source
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§

SpscFace
Engine-side SPSC SHM face.
SpscHandle
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 mtu bytes 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 mtu bytes. 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 explicit mtu = 8800 gets a ~25 KiB slot, not the 272 KiB default. If no mtu is specified, the caller should use DEFAULT_SLOT_SIZE directly.