std: Add support for accept4 on Linux

This is necessary to atomically accept a socket and set the CLOEXEC flag at the
same time. Support only appeared in Linux 2.6.28 so we have to dynamically
determine which syscall we're supposed to call in this case.
This commit is contained in:
Alex Crichton 2016-02-04 15:22:41 -08:00
parent 1a31e1c09f
commit 46315184cb
2 changed files with 28 additions and 4 deletions

View File

@ -12,7 +12,7 @@ use prelude::v1::*;
use ffi::CStr;
use io;
use libc::{self, c_int, size_t};
use libc::{self, c_int, size_t, sockaddr, socklen_t};
use net::{SocketAddr, Shutdown};
use str;
use sys::fd::FileDesc;
@ -78,8 +78,28 @@ impl Socket {
}
}
pub fn accept(&self, storage: *mut libc::sockaddr,
len: *mut libc::socklen_t) -> io::Result<Socket> {
pub fn accept(&self, storage: *mut sockaddr, len: *mut socklen_t)
-> io::Result<Socket> {
// Unfortunately the only known way right now to accept a socket and
// atomically set the CLOEXEC flag is to use the `accept4` syscall on
// Linux. This was added in 2.6.28, however, and because we support
// 2.6.18 we must detect this support dynamically.
if cfg!(target_os = "linux") {
weak! {
fn accept4(c_int, *mut sockaddr, *mut socklen_t, c_int) -> c_int
}
if let Some(accept) = accept4.get() {
let res = cvt_r(|| unsafe {
accept(self.0.raw(), storage, len, SOCK_CLOEXEC)
});
match res {
Ok(fd) => return Ok(Socket(FileDesc::new(fd))),
Err(ref e) if e.raw_os_error() == Some(libc::ENOSYS) => {}
Err(e) => return Err(e),
}
}
}
let fd = try!(cvt_r(|| unsafe {
libc::accept(self.0.raw(), storage, len)
}));

View File

@ -61,7 +61,11 @@ impl<F> Weak<F> {
if self.addr.load(Ordering::SeqCst) == 1 {
self.addr.store(fetch(self.name), Ordering::SeqCst);
}
mem::transmute::<&AtomicUsize, Option<&F>>(&self.addr)
if self.addr.load(Ordering::SeqCst) == 0 {
None
} else {
mem::transmute::<&AtomicUsize, Option<&F>>(&self.addr)
}
}
}
}