The multishot recvmsg path handed buffer slots back to the ring by
convention: a `defer { Task { reprovideAndSubmit(id:) } }` inside the
buffer-pointer closure. Nothing prevented touching a slot after it was
reprovided, reproviding it twice, or forgetting to reprovide it.
Introduce ProvidedBuffer<U>, a ~Copyable handle that makes the contract
compiler-checked:
- ~Copyable: exactly one owner, so a slot cannot be reprovided twice.
- borrowing withUnsafeRawBufferPointer: payload access is scoped, so the
slot cannot be used after ownership ends.
- deinit: the slot is reprovided (RAII) when the handle leaves scope, so a
forgotten reprovide cannot leak it. This is the same fire-and-forget hop
onto the ring actor the previous defer performed.
MessageHolder.receive now borrows a slot, copies the payload out, and lets
scope exit return it — no defer bookkeeping.
Adds ProvidedBufferTests, which sends more datagrams than the slot pool
holds so slots must be recycled, proving copy-out-then-reprovide preserves
every payload across reuse.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pw4S9tnBX48BK8UppcfmFH