Skip to content

Commit c65cd8e

Browse files
committed
Add back preadv2 optimization for try_acquire on Linux
Signed-off-by: Jiahao XU <[email protected]>
1 parent 66782f5 commit c65cd8e

File tree

1 file changed

+103
-0
lines changed

1 file changed

+103
-0
lines changed

src/unix.rs

+103
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,33 @@ impl Client {
285285
pub fn try_acquire(&self) -> io::Result<Option<Acquired>> {
286286
let mut buf = [0];
287287

288+
// On Linux, we can use preadv2 to do non-blocking read,
289+
// even if `O_NONBLOCK` is not set.
290+
//
291+
// TODO: musl libc supports preadv2 since 1.2.5, but `libc` crate
292+
// hasn't yet added it.
293+
#[cfg(target_os = "linux")]
294+
{
295+
let read = self.read().as_raw_fd();
296+
loop {
297+
match linux::non_blocking_read(read, &mut buf) {
298+
Ok(1) => return Ok(Some(Acquired { byte: buf[0] })),
299+
Ok(_) => {
300+
return Err(io::Error::new(
301+
io::ErrorKind::UnexpectedEof,
302+
"early EOF on jobserver pipe",
303+
))
304+
}
305+
306+
Err(e) if e.kind() == io::ErrorKind::WouldBlock => return Ok(None),
307+
Err(e) if e.kind() == io::ErrorKind::Interrupted => continue,
308+
Err(e) if e.kind() == io::ErrorKind::Unsupported => break,
309+
310+
Err(err) => return Err(err),
311+
}
312+
}
313+
}
314+
288315
let (mut fifo, is_non_blocking) = match self {
289316
Self::Fifo {
290317
file,
@@ -368,6 +395,82 @@ impl Client {
368395
}
369396
}
370397

398+
// This should be available for all linux targets,
399+
// though only [`non_blocking_read`] currently uses it so adding gnu cfg.
400+
#[cfg(target_os = "linux")]
401+
mod linux {
402+
use super::*;
403+
404+
use libc::{iovec, off_t, ssize_t, syscall, SYS_preadv2};
405+
406+
fn cvt_ssize(t: ssize_t) -> io::Result<ssize_t> {
407+
if t == -1 {
408+
Err(io::Error::last_os_error())
409+
} else {
410+
Ok(t)
411+
}
412+
}
413+
414+
unsafe fn preadv2(
415+
fd: c_int,
416+
iov: *const iovec,
417+
iovcnt: c_int,
418+
offset: off_t,
419+
flags: c_int,
420+
) -> ssize_t {
421+
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
422+
let res = syscall(SYS_preadv2, fd, iov, iovcnt, offset, 0, flags);
423+
424+
#[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
425+
let res = syscall(SYS_preadv2, fd, iov, iovcnt, offset, 0, flags);
426+
427+
#[cfg(not(target_arch = "x86_64"))]
428+
let res = syscall(
429+
SYS_preadv2,
430+
fd,
431+
iov,
432+
iovcnt,
433+
offset as libc::c_long,
434+
((offset as u64) >> 32) as libc::c_long,
435+
flags,
436+
);
437+
438+
res.try_into().unwrap()
439+
}
440+
441+
pub fn non_blocking_read(fd: c_int, buf: &mut [u8]) -> io::Result<usize> {
442+
static IS_NONBLOCKING_READ_UNSUPPORTED: AtomicBool = AtomicBool::new(false);
443+
444+
if IS_NONBLOCKING_READ_UNSUPPORTED.load(Ordering::Relaxed) {
445+
return Err(io::ErrorKind::Unsupported.into());
446+
}
447+
448+
match cvt_ssize(unsafe {
449+
preadv2(
450+
fd,
451+
&iovec {
452+
iov_base: buf.as_ptr() as *mut _,
453+
iov_len: buf.len(),
454+
},
455+
1,
456+
-1,
457+
libc::RWF_NOWAIT,
458+
)
459+
}) {
460+
Ok(cnt) => Ok(cnt.try_into().unwrap()),
461+
Err(err) if err.raw_os_error() == Some(libc::EOPNOTSUPP) => {
462+
IS_NONBLOCKING_READ_UNSUPPORTED.store(true, Ordering::Relaxed);
463+
Err(io::ErrorKind::Unsupported.into())
464+
}
465+
Err(err) if err.kind() == io::ErrorKind::Unsupported => {
466+
IS_NONBLOCKING_READ_UNSUPPORTED.store(true, Ordering::Relaxed);
467+
Err(err)
468+
}
469+
Err(err) => Err(err),
470+
}
471+
}
472+
}
473+
371474
#[derive(Debug)]
372475
pub struct Helper {
373476
thread: JoinHandle<()>,

0 commit comments

Comments
 (0)