Skip to content

v0.12.0

Choose a tag to compare

@Segfaultd Segfaultd released this 29 Jul 13:05
· 4 commits to master since this release

Batched datagram I/O (recvmmsg / sendmmsg)

On Linux the reliability layer now coalesces a tick's outgoing datagrams into a single sendmmsg, and drains the receive socket with a single recvmmsg per burst, instead of one sendto/recvfrom per packet.

There is nothing to configure. Batching is a platform capability, guarded by a plain #if defined(__linux__), always on where the syscalls exist. macOS, Windows and the BSDs compile the portable per-datagram paths. Delivery semantics are identical either way — the same datagrams arrive, in the same order, with the same reliability.

Impact

Measured on the 2560-message reliable-ordered burst in Tests/Integration/MmsgBatchLiveTests.cpp (Linux, Release, strace -c, median of 3 runs):

syscall per-datagram batched
sendto 2907 35
sendmmsg 0 58
recvfrom 2618 0
recvmmsg 0 85
total 5525 178

~31x fewer system calls. Up to 64 datagrams (MMSG_BATCH_MAX) coalesce per call. Counts vary a percent or two between runs, so the ratio is the result rather than the exact figures. At low packet rates the change is not measurable.

Runtime fallback

If recvmmsg/sendmmsg report ENOSYS — a seccomp profile, gVisor, user-mode emulation, or a kernel older than the syscall — the process latches the condition once and both paths revert to the portable per-datagram code for the rest of its life. Only ENOSYS latches; EPERM is excluded because a firewall rejecting a single destination reports it too. Verified end to end under a seccomp profile forcing errno 38: recvmmsg is attempted exactly once, sendmmsg never, and all traffic falls back cleanly with the full suite passing.

New API

RakNetSocket2::SendBatch — a virtual on the socket interface with a portable Send()-loop default and a sendmmsg override on Linux. Returns a datagram count (not a byte total), or a negative error only when nothing at all went out, mirroring sendmmsg(2). A datagram that fails on its own is dropped and the rest of the batch is still sent.

Testing

  • Tests/Unit/MmsgBatchTests.cpp — 58 cases over the partial-send resume state machine, the transient-vs-permanent errno split, missing-syscall detection, sockaddr decoding, and the recv-slot carry-over including a 500-pass fixed-seed stress proving no slot is leaked or double-freed.
  • Tests/Integration/MmsgBatchLiveTests.cpp — drives bursts far past the batch boundary; the rest of the suite sends about one datagram per tick and never fills a batch. Each message is its own checksum, so truncation, payload aliasing and reordering each fail a distinct assertion.
  • New linux-native CI job covering Debug and Release. The hermetic unit suite now runs exactly once — --repeat until-pass:3 is reserved for the integration suite, where it absorbs loopback timing misses instead of masking nondeterminism.

Verified on Linux Debug and Release (unit 94/94, integration 32/32), macOS and Windows CI green.

Known limitations

All testing is loopback inside a container — not a real NIC, MTU, loss or ICMP. ENOBUFS and partial-sendmmsg are exercised against fakes rather than provoked. No long-running soak. A staged rollout is recommended for the batched paths.

Full changelog: v0.11.0...v0.12.0