Skip to content

Commit 535021e

Browse files
authored
Merge pull request #128 from habibtalib/codex/review-github-issues
fix: gate Linux-only runtime paths
2 parents 25040fd + 66fda96 commit 535021e

21 files changed

Lines changed: 552 additions & 150 deletions

File tree

Cargo.lock

Lines changed: 18 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

nodedb-bridge/src/eventfd.rs

Lines changed: 117 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,12 @@ use std::os::unix::io::{AsRawFd, FromRawFd, OwnedFd, RawFd};
2626
/// Write to signal, read to consume. Multiple signals coalesce into one.
2727
/// The fd can be registered with any event loop (epoll, io_uring, kqueue fallback).
2828
pub struct EventFd {
29+
#[cfg(target_os = "linux")]
2930
fd: OwnedFd,
31+
#[cfg(not(target_os = "linux"))]
32+
read_fd: OwnedFd,
33+
#[cfg(not(target_os = "linux"))]
34+
write_fd: OwnedFd,
3035
}
3136

3237
impl EventFd {
@@ -35,14 +40,36 @@ impl EventFd {
3540
/// `EFD_NONBLOCK` ensures reads/writes never block the calling thread.
3641
/// `EFD_CLOEXEC` prevents fd leaks across fork/exec.
3742
pub fn new() -> io::Result<Self> {
38-
// SAFETY: eventfd2 is a standard Linux syscall. Flags are valid.
39-
let fd = unsafe { libc::eventfd(0, libc::EFD_NONBLOCK | libc::EFD_CLOEXEC) };
40-
if fd < 0 {
41-
return Err(io::Error::last_os_error());
43+
#[cfg(target_os = "linux")]
44+
{
45+
// SAFETY: eventfd2 is a standard Linux syscall. Flags are valid.
46+
let fd = unsafe { libc::eventfd(0, libc::EFD_NONBLOCK | libc::EFD_CLOEXEC) };
47+
if fd < 0 {
48+
return Err(io::Error::last_os_error());
49+
}
50+
// SAFETY: fd is a valid file descriptor returned by eventfd().
51+
let fd = unsafe { OwnedFd::from_raw_fd(fd) };
52+
Ok(Self { fd })
53+
}
54+
#[cfg(not(target_os = "linux"))]
55+
{
56+
let mut fds = [0; 2];
57+
// SAFETY: `fds` points to two valid c_int slots for pipe to fill.
58+
if unsafe { libc::pipe(fds.as_mut_ptr()) } < 0 {
59+
return Err(io::Error::last_os_error());
60+
}
61+
if let Err(err) = set_pipe_flags(fds[0]).and_then(|()| set_pipe_flags(fds[1])) {
62+
unsafe {
63+
libc::close(fds[0]);
64+
libc::close(fds[1]);
65+
}
66+
return Err(err);
67+
}
68+
// SAFETY: fds were returned by pipe() and ownership moves into OwnedFd.
69+
let read_fd = unsafe { OwnedFd::from_raw_fd(fds[0]) };
70+
let write_fd = unsafe { OwnedFd::from_raw_fd(fds[1]) };
71+
Ok(Self { read_fd, write_fd })
4272
}
43-
// SAFETY: fd is a valid file descriptor returned by eventfd().
44-
let fd = unsafe { OwnedFd::from_raw_fd(fd) };
45-
Ok(Self { fd })
4673
}
4774

4875
/// Signal the other side to wake up.
@@ -51,14 +78,12 @@ impl EventFd {
5178
/// a single read clears all pending signals.
5279
pub fn notify(&self) -> io::Result<()> {
5380
let val: u64 = 1;
81+
#[cfg(target_os = "linux")]
82+
let fd = self.fd.as_raw_fd();
83+
#[cfg(not(target_os = "linux"))]
84+
let fd = self.write_fd.as_raw_fd();
5485
// SAFETY: writing 8 bytes to a valid eventfd.
55-
let ret = unsafe {
56-
libc::write(
57-
self.fd.as_raw_fd(),
58-
&val as *const u64 as *const libc::c_void,
59-
8,
60-
)
61-
};
86+
let ret = unsafe { libc::write(fd, &val as *const u64 as *const libc::c_void, 8) };
6287
if ret < 0 {
6388
Err(io::Error::last_os_error())
6489
} else {
@@ -71,24 +96,54 @@ impl EventFd {
7196
/// Returns `Ok(0)` if no signal was pending (EAGAIN on non-blocking fd).
7297
/// Returns `Ok(n)` where n is the accumulated signal count.
7398
pub fn try_read(&self) -> io::Result<u64> {
74-
let mut val: u64 = 0;
75-
// SAFETY: reading 8 bytes from a valid eventfd.
76-
let ret = unsafe {
77-
libc::read(
78-
self.fd.as_raw_fd(),
79-
&mut val as *mut u64 as *mut libc::c_void,
80-
8,
81-
)
82-
};
83-
if ret < 0 {
84-
let err = io::Error::last_os_error();
85-
if err.kind() == io::ErrorKind::WouldBlock {
86-
Ok(0)
99+
#[cfg(target_os = "linux")]
100+
{
101+
let mut val: u64 = 0;
102+
// SAFETY: reading 8 bytes from a valid eventfd.
103+
let ret = unsafe {
104+
libc::read(
105+
self.fd.as_raw_fd(),
106+
&mut val as *mut u64 as *mut libc::c_void,
107+
8,
108+
)
109+
};
110+
if ret < 0 {
111+
let err = io::Error::last_os_error();
112+
if err.kind() == io::ErrorKind::WouldBlock {
113+
Ok(0)
114+
} else {
115+
Err(err)
116+
}
87117
} else {
88-
Err(err)
118+
Ok(val)
119+
}
120+
}
121+
#[cfg(not(target_os = "linux"))]
122+
{
123+
let mut count = 0u64;
124+
loop {
125+
let mut val: u64 = 0;
126+
// SAFETY: reading 8 bytes from the nonblocking pipe read end.
127+
let ret = unsafe {
128+
libc::read(
129+
self.read_fd.as_raw_fd(),
130+
&mut val as *mut u64 as *mut libc::c_void,
131+
8,
132+
)
133+
};
134+
if ret == 8 {
135+
count = count.saturating_add(val.max(1));
136+
continue;
137+
}
138+
if ret < 0 {
139+
let err = io::Error::last_os_error();
140+
if err.kind() == io::ErrorKind::WouldBlock {
141+
return Ok(count);
142+
}
143+
return Err(err);
144+
}
145+
return Ok(count);
89146
}
90-
} else {
91-
Ok(val)
92147
}
93148
}
94149

@@ -99,8 +154,39 @@ impl EventFd {
99154
/// - Glommio: `GlommioDma::from_raw_fd()` or similar
100155
/// - io_uring: `IORING_OP_READ` on the fd
101156
pub fn as_fd(&self) -> RawFd {
102-
self.fd.as_raw_fd()
157+
#[cfg(target_os = "linux")]
158+
{
159+
self.fd.as_raw_fd()
160+
}
161+
#[cfg(not(target_os = "linux"))]
162+
{
163+
self.read_fd.as_raw_fd()
164+
}
165+
}
166+
}
167+
168+
#[cfg(not(target_os = "linux"))]
169+
fn set_pipe_flags(fd: RawFd) -> io::Result<()> {
170+
// SAFETY: fcntl is called with a valid fd returned by pipe().
171+
let status_flags = unsafe { libc::fcntl(fd, libc::F_GETFL) };
172+
if status_flags < 0 {
173+
return Err(io::Error::last_os_error());
174+
}
175+
// SAFETY: fcntl F_SETFL updates status flags for this fd.
176+
if unsafe { libc::fcntl(fd, libc::F_SETFL, status_flags | libc::O_NONBLOCK) } < 0 {
177+
return Err(io::Error::last_os_error());
178+
}
179+
180+
// SAFETY: fcntl is called with a valid fd returned by pipe().
181+
let fd_flags = unsafe { libc::fcntl(fd, libc::F_GETFD) };
182+
if fd_flags < 0 {
183+
return Err(io::Error::last_os_error());
184+
}
185+
// SAFETY: fcntl F_SETFD updates close-on-exec flags for this fd.
186+
if unsafe { libc::fcntl(fd, libc::F_SETFD, fd_flags | libc::FD_CLOEXEC) } < 0 {
187+
return Err(io::Error::last_os_error());
103188
}
189+
Ok(())
104190
}
105191

106192
// SAFETY: eventfd is a kernel object. The fd can be shared across threads.

nodedb-bridge/tests/cross_runtime.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
//!
1313
//! Requires the `tokio` feature: `cargo test -p nodedb-bridge --features tokio`
1414
15-
#![cfg(feature = "tokio")]
15+
#![cfg(all(feature = "tokio", target_os = "linux"))]
1616

1717
use std::sync::Arc;
1818
use std::sync::atomic::{AtomicBool, Ordering};

nodedb-cluster/src/readiness.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@
1313
//! no-op so the server builds and runs unchanged; `sd-notify` itself
1414
//! is only compiled in on Linux via a target-gated Cargo dependency.
1515
16-
use tracing::{debug, warn};
16+
use tracing::debug;
17+
#[cfg(target_os = "linux")]
18+
use tracing::warn;
1719

1820
/// Signal that the server is fully initialised.
1921
///

nodedb-wal/Cargo.toml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,13 @@ thiserror = { workspace = true }
2121
tracing = { workspace = true }
2222
crc32c = { workspace = true }
2323
libc = { workspace = true }
24-
io-uring = { workspace = true, optional = true }
2524
aes-gcm = { workspace = true }
2625
getrandom = { workspace = true }
2726
zeroize = { workspace = true }
2827

28+
[target.'cfg(target_os = "linux")'.dependencies]
29+
io-uring = { workspace = true, optional = true }
30+
2931
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
3032
memmap2 = { workspace = true }
3133

@@ -34,9 +36,11 @@ tokio = { workspace = true }
3436
fluxbench = { workspace = true }
3537
tracing-subscriber = { workspace = true }
3638
tempfile = "3"
37-
io-uring = { workspace = true }
3839
rand = { workspace = true }
3940

41+
[target.'cfg(target_os = "linux")'.dev-dependencies]
42+
io-uring = { workspace = true }
43+
4044
[[bench]]
4145
name = "wal_throughput"
4246
harness = false

nodedb-wal/src/double_write.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ use std::io::{Read, Seek, SeekFrom, Write};
4242
use std::path::{Path, PathBuf};
4343
use std::sync::atomic::{AtomicU64, Ordering};
4444

45-
#[cfg(not(target_arch = "wasm32"))]
45+
#[cfg(target_os = "linux")]
4646
use std::os::unix::fs::OpenOptionsExt as _;
4747

4848
use crate::align::{AlignedBuf, DEFAULT_ALIGNMENT, is_aligned};
@@ -169,7 +169,7 @@ impl DoubleWriteBuffer {
169169

170170
let mut opts = OpenOptions::new();
171171
opts.read(true).write(true).create(true).truncate(false);
172-
#[cfg(not(target_arch = "wasm32"))]
172+
#[cfg(target_os = "linux")]
173173
if mode == DwbMode::Direct {
174174
opts.custom_flags(libc::O_DIRECT);
175175
}

nodedb-wal/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ pub mod segment;
4141
pub mod segmented;
4242
pub mod temporal_purge;
4343
pub mod tombstone;
44-
#[cfg(feature = "io-uring")]
44+
#[cfg(all(feature = "io-uring", target_os = "linux"))]
4545
pub mod uring_writer;
4646
pub mod writer;
4747

0 commit comments

Comments
 (0)