19dc3b50bd
Excluding the functions inherited from the cast module last week (with marked stability levels), these functions received the following treatment. * size_of - this method has become #[stable] * nonzero_size_of/nonzero_size_of_val - these methods have been removed * min_align_of - this method is now #[stable] * pref_align_of - this method has been renamed without the `pref_` prefix, and it is the "default alignment" now. This decision is in line with what clang does (see url linked in comment on function). This function is now #[stable]. * init - renamed to zeroed and marked #[stable] * uninit - marked #[stable] * move_val_init - renamed to overwrite and marked #[stable] * {from,to}_{be,le}{16,32,64} - all functions marked #[stable] * swap/replace/drop - marked #[stable] * size_of_val/min_align_of_val/align_of_val - these functions are marked #[unstable], but will continue to exist in some form. Concerns have been raised about their `_val` prefix. [breaking-change]
169 lines
5.3 KiB
Rust
169 lines
5.3 KiB
Rust
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
|
|
// file at the top-level directory of this distribution and at
|
|
// http://rust-lang.org/COPYRIGHT.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
// option. This file may not be copied, modified, or distributed
|
|
// except according to those terms.
|
|
|
|
use libc;
|
|
use std::io::IoResult;
|
|
use std::io;
|
|
use std::mem;
|
|
use std::os;
|
|
use std::ptr;
|
|
|
|
use super::c;
|
|
use super::net;
|
|
use super::{retry, last_error};
|
|
|
|
#[deriving(Show)]
|
|
pub enum SocketStatus {
|
|
Readable,
|
|
Writable,
|
|
}
|
|
|
|
pub fn timeout(desc: &'static str) -> io::IoError {
|
|
io::IoError {
|
|
kind: io::TimedOut,
|
|
desc: desc,
|
|
detail: None,
|
|
}
|
|
}
|
|
|
|
pub fn ms_to_timeval(ms: u64) -> libc::timeval {
|
|
libc::timeval {
|
|
tv_sec: (ms / 1000) as libc::time_t,
|
|
tv_usec: ((ms % 1000) * 1000) as libc::suseconds_t,
|
|
}
|
|
}
|
|
|
|
#[cfg(unix)]
|
|
pub fn wouldblock() -> bool {
|
|
let err = os::errno();
|
|
err == libc::EWOULDBLOCK as int || err == libc::EAGAIN as int
|
|
}
|
|
|
|
#[cfg(windows)]
|
|
pub fn wouldblock() -> bool {
|
|
let err = os::errno();
|
|
err == libc::WSAEWOULDBLOCK as uint
|
|
}
|
|
|
|
#[cfg(unix)]
|
|
pub fn set_nonblocking(fd: net::sock_t, nb: bool) -> IoResult<()> {
|
|
let set = nb as libc::c_int;
|
|
super::mkerr_libc(retry(|| unsafe { c::ioctl(fd, c::FIONBIO, &set) }))
|
|
}
|
|
|
|
#[cfg(windows)]
|
|
pub fn set_nonblocking(fd: net::sock_t, nb: bool) -> IoResult<()> {
|
|
let mut set = nb as libc::c_ulong;
|
|
if unsafe { c::ioctlsocket(fd, c::FIONBIO, &mut set) != 0 } {
|
|
Err(last_error())
|
|
} else {
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
// See http://developerweb.net/viewtopic.php?id=3196 for where this is
|
|
// derived from.
|
|
pub fn connect_timeout(fd: net::sock_t,
|
|
addrp: *libc::sockaddr,
|
|
len: libc::socklen_t,
|
|
timeout_ms: u64) -> IoResult<()> {
|
|
use std::os;
|
|
#[cfg(unix)] use INPROGRESS = libc::EINPROGRESS;
|
|
#[cfg(windows)] use INPROGRESS = libc::WSAEINPROGRESS;
|
|
#[cfg(unix)] use WOULDBLOCK = libc::EWOULDBLOCK;
|
|
#[cfg(windows)] use WOULDBLOCK = libc::WSAEWOULDBLOCK;
|
|
|
|
// Make sure the call to connect() doesn't block
|
|
try!(set_nonblocking(fd, true));
|
|
|
|
let ret = match unsafe { libc::connect(fd, addrp, len) } {
|
|
// If the connection is in progress, then we need to wait for it to
|
|
// finish (with a timeout). The current strategy for doing this is
|
|
// to use select() with a timeout.
|
|
-1 if os::errno() as int == INPROGRESS as int ||
|
|
os::errno() as int == WOULDBLOCK as int => {
|
|
let mut set: c::fd_set = unsafe { mem::zeroed() };
|
|
c::fd_set(&mut set, fd);
|
|
match await(fd, &mut set, timeout_ms) {
|
|
0 => Err(timeout("connection timed out")),
|
|
-1 => Err(last_error()),
|
|
_ => {
|
|
let err: libc::c_int = try!(
|
|
net::getsockopt(fd, libc::SOL_SOCKET, libc::SO_ERROR));
|
|
if err == 0 {
|
|
Ok(())
|
|
} else {
|
|
Err(io::IoError::from_errno(err as uint, true))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
-1 => Err(last_error()),
|
|
_ => Ok(()),
|
|
};
|
|
|
|
// be sure to turn blocking I/O back on
|
|
try!(set_nonblocking(fd, false));
|
|
return ret;
|
|
|
|
#[cfg(unix)]
|
|
fn await(fd: net::sock_t, set: &mut c::fd_set,
|
|
timeout: u64) -> libc::c_int {
|
|
let start = ::io::timer::now();
|
|
retry(|| unsafe {
|
|
// Recalculate the timeout each iteration (it is generally
|
|
// undefined what the value of the 'tv' is after select
|
|
// returns EINTR).
|
|
let tv = ms_to_timeval(timeout - (::io::timer::now() - start));
|
|
c::select(fd + 1, ptr::null(), set as *mut _ as *_,
|
|
ptr::null(), &tv)
|
|
})
|
|
}
|
|
#[cfg(windows)]
|
|
fn await(_fd: net::sock_t, set: &mut c::fd_set,
|
|
timeout: u64) -> libc::c_int {
|
|
let tv = ms_to_timeval(timeout);
|
|
unsafe { c::select(1, ptr::null(), &*set, ptr::null(), &tv) }
|
|
}
|
|
}
|
|
|
|
pub fn await(fd: net::sock_t, deadline: Option<u64>,
|
|
status: SocketStatus) -> IoResult<()> {
|
|
let mut set: c::fd_set = unsafe { mem::zeroed() };
|
|
c::fd_set(&mut set, fd);
|
|
let (read, write) = match status {
|
|
Readable => (&set as *_, ptr::null()),
|
|
Writable => (ptr::null(), &set as *_),
|
|
};
|
|
let mut tv: libc::timeval = unsafe { mem::zeroed() };
|
|
|
|
match retry(|| {
|
|
let now = ::io::timer::now();
|
|
let tvp = match deadline {
|
|
None => ptr::null(),
|
|
Some(deadline) => {
|
|
// If we're past the deadline, then pass a 0 timeout to
|
|
// select() so we can poll the status
|
|
let ms = if deadline < now {0} else {deadline - now};
|
|
tv = ms_to_timeval(ms);
|
|
&tv as *_
|
|
}
|
|
};
|
|
let n = if cfg!(windows) {1} else {fd as libc::c_int + 1};
|
|
let r = unsafe { c::select(n, read, write, ptr::null(), tvp) };
|
|
r
|
|
}) {
|
|
-1 => Err(last_error()),
|
|
0 => Err(timeout("timed out")),
|
|
_ => Ok(()),
|
|
}
|
|
}
|