Skip to content

feat: invoke native sendfile(2) on macOS#4043

Merged
oferchen merged 1 commit into
masterfrom
feat/macos-native-sendfile-2152
May 14, 2026
Merged

feat: invoke native sendfile(2) on macOS#4043
oferchen merged 1 commit into
masterfrom
feat/macos-native-sendfile-2152

Conversation

@oferchen
Copy link
Copy Markdown
Owner

Summary

  • Adds a target_os = \"macos\" arm to send_file_to_fd and send_file_to_fd_with_policy in crates/fast_io/src/sendfile.rs so file-to-socket transfers use Darwin's native sendfile(2) instead of falling through to a buffered read/write loop.
  • The Darwin signature differs from Linux (int sendfile(int fd, int s, off_t offset, off_t *len, struct sf_hdtr *hdtr, int flags)). The new try_sendfile_macos handles the in/out len parameter, treats a return of 0 as success, retries / records forward progress on EAGAIN/EINTR, and preserves the "transfer from the current file position" contract by querying the source offset with lseek(SEEK_CUR) and advancing it with lseek(SEEK_SET) after the transfer.
  • Non-socket destinations (pipes, regular files) return ENOTSOCK and fall back to the existing buffered loop, matching the behaviour Linux already had on non-sendfile-capable targets.

This is the macOS sendfile gap called out in docs/audits/macos-fastio-fallback.md (section 3.5 + section 5 item 2), produced as part of PR #4008. The unsafe FFI stays inside fast_io, the only crate permitted to wrap platform syscalls.

Refs #2152.

Test plan

  • cargo fmt --all -- --check
  • cargo clippy --workspace --all-targets --all-features --no-deps -- -D warnings
  • cargo nextest run -p fast_io --all-features on macOS exercises the three new tests:
    • test_send_file_to_fd_socketpair_macos - calls try_sendfile_macos directly against a SOCK_STREAM socketpair and asserts the bytes match.
    • test_send_file_to_fd_dispatch_uses_native_macos - drives the public send_file_to_fd against a SOCK_STREAM socketpair end-to-end.
    • test_send_file_to_fd_macos_non_socket_falls_back - sends to a pipe; Darwin's sendfile rejects it with ENOTSOCK and the dispatch must fall back to copy_via_fd_write while still delivering the bytes.
  • Linux / Windows / musl matrices remain on their existing paths (the new arm is #[cfg(target_os = \"macos\")] only).

Darwin ships a BSD-style sendfile(2) syscall, but the dispatch in
crates/fast_io/src/sendfile.rs only branched on Linux and fell
through to a read/write loop on every other unix target. The macOS
fast_io audit at docs/audits/macos-fastio-fallback.md flagged this
as one of two unshipped wins identified during the post-merge
review of PR #4008.

This change adds a target_os = "macos" arm in send_file_to_fd and
send_file_to_fd_with_policy that calls libc::sendfile with the
Darwin signature:

    int sendfile(int fd, int s, off_t offset, off_t *len,
                 struct sf_hdtr *hdtr, int flags);

The implementation handles the in/out `len` parameter (bytes to
send -> bytes actually sent), treats a return of 0 as success, and
preserves the "transfer from the current file position" contract
by querying the source offset with lseek(SEEK_CUR) and advancing
the position with lseek(SEEK_SET) after the transfer. Partial
sends on EAGAIN/EINTR are reported as forward progress so the
socket peer always sees a consistent prefix.

Non-socket destinations (pipes, regular files) fail with
ENOTSOCK; the dispatch transparently falls back to the buffered
read/write loop in that case, matching the behaviour Linux
already had for non-sendfile-capable targets.

Tests gated to target_os = "macos" exercise the new path through
a SOCK_STREAM socketpair (native sendfile fast path), the public
dispatch entry point, and a pipe destination that must fall back
to read/write.

Refs #2152.
@github-actions github-actions Bot added the enhancement New feature or request label May 14, 2026
@oferchen oferchen merged commit 516bf16 into master May 14, 2026
36 of 39 checks passed
@oferchen oferchen deleted the feat/macos-native-sendfile-2152 branch May 14, 2026 14:57
oferchen added a commit that referenced this pull request May 18, 2026
Darwin ships a BSD-style sendfile(2) syscall, but the dispatch in
crates/fast_io/src/sendfile.rs only branched on Linux and fell
through to a read/write loop on every other unix target. The macOS
fast_io audit at docs/audits/macos-fastio-fallback.md flagged this
as one of two unshipped wins identified during the post-merge
review of PR #4008.

This change adds a target_os = "macos" arm in send_file_to_fd and
send_file_to_fd_with_policy that calls libc::sendfile with the
Darwin signature:

    int sendfile(int fd, int s, off_t offset, off_t *len,
                 struct sf_hdtr *hdtr, int flags);

The implementation handles the in/out `len` parameter (bytes to
send -> bytes actually sent), treats a return of 0 as success, and
preserves the "transfer from the current file position" contract
by querying the source offset with lseek(SEEK_CUR) and advancing
the position with lseek(SEEK_SET) after the transfer. Partial
sends on EAGAIN/EINTR are reported as forward progress so the
socket peer always sees a consistent prefix.

Non-socket destinations (pipes, regular files) fail with
ENOTSOCK; the dispatch transparently falls back to the buffered
read/write loop in that case, matching the behaviour Linux
already had for non-sendfile-capable targets.

Tests gated to target_os = "macos" exercise the new path through
a SOCK_STREAM socketpair (native sendfile fast path), the public
dispatch entry point, and a pipe destination that must fall back
to read/write.

Refs #2152.
oferchen added a commit that referenced this pull request May 18, 2026
Darwin ships a BSD-style sendfile(2) syscall, but the dispatch in
crates/fast_io/src/sendfile.rs only branched on Linux and fell
through to a read/write loop on every other unix target. The macOS
fast_io audit at docs/audits/macos-fastio-fallback.md flagged this
as one of two unshipped wins identified during the post-merge
review of PR #4008.

This change adds a target_os = "macos" arm in send_file_to_fd and
send_file_to_fd_with_policy that calls libc::sendfile with the
Darwin signature:

    int sendfile(int fd, int s, off_t offset, off_t *len,
                 struct sf_hdtr *hdtr, int flags);

The implementation handles the in/out `len` parameter (bytes to
send -> bytes actually sent), treats a return of 0 as success, and
preserves the "transfer from the current file position" contract
by querying the source offset with lseek(SEEK_CUR) and advancing
the position with lseek(SEEK_SET) after the transfer. Partial
sends on EAGAIN/EINTR are reported as forward progress so the
socket peer always sees a consistent prefix.

Non-socket destinations (pipes, regular files) fail with
ENOTSOCK; the dispatch transparently falls back to the buffered
read/write loop in that case, matching the behaviour Linux
already had for non-sendfile-capable targets.

Tests gated to target_os = "macos" exercise the new path through
a SOCK_STREAM socketpair (native sendfile fast path), the public
dispatch entry point, and a pipe destination that must fall back
to read/write.

Refs #2152.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant