1 // Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
16 use std::rt::rtio::{IoResult, IoError};
20 use super::{retry, last_error};
23 pub enum SocketStatus {
28 pub fn timeout(desc: &'static str) -> IoError {
29 #[cfg(unix)] use libc::ETIMEDOUT as ERROR;
30 #[cfg(windows)] use libc::ERROR_OPERATION_ABORTED as ERROR;
34 detail: Some(desc.to_string()),
38 pub fn short_write(n: uint, desc: &'static str) -> IoError {
39 #[cfg(unix)] use libc::EAGAIN as ERROR;
40 #[cfg(windows)] use libc::ERROR_OPERATION_ABORTED as ERROR;
44 detail: Some(desc.to_string()),
48 pub fn eof() -> IoError {
50 code: libc::EOF as uint,
57 pub fn ms_to_timeval(ms: u64) -> libc::timeval {
59 tv_sec: (ms / 1000) as libc::c_long,
60 tv_usec: ((ms % 1000) * 1000) as libc::c_long,
64 pub fn ms_to_timeval(ms: u64) -> libc::timeval {
66 tv_sec: (ms / 1000) as libc::time_t,
67 tv_usec: ((ms % 1000) * 1000) as libc::suseconds_t,
72 pub fn wouldblock() -> bool {
73 let err = os::errno();
74 err == libc::EWOULDBLOCK as int || err == libc::EAGAIN as int
78 pub fn wouldblock() -> bool {
79 let err = os::errno();
80 err == libc::WSAEWOULDBLOCK as uint
84 pub fn set_nonblocking(fd: net::sock_t, nb: bool) -> IoResult<()> {
85 let set = nb as libc::c_int;
86 super::mkerr_libc(retry(|| unsafe { c::ioctl(fd, c::FIONBIO, &set) }))
90 pub fn set_nonblocking(fd: net::sock_t, nb: bool) -> IoResult<()> {
91 let mut set = nb as libc::c_ulong;
92 if unsafe { c::ioctlsocket(fd, c::FIONBIO, &mut set) != 0 } {
99 // See http://developerweb.net/viewtopic.php?id=3196 for where this is
101 pub fn connect_timeout(fd: net::sock_t,
102 addrp: *const libc::sockaddr,
103 len: libc::socklen_t,
104 timeout_ms: u64) -> IoResult<()> {
106 #[cfg(unix)] use libc::EINPROGRESS as INPROGRESS;
107 #[cfg(windows)] use libc::WSAEINPROGRESS as INPROGRESS;
108 #[cfg(unix)] use libc::EWOULDBLOCK as WOULDBLOCK;
109 #[cfg(windows)] use libc::WSAEWOULDBLOCK as WOULDBLOCK;
111 // Make sure the call to connect() doesn't block
112 try!(set_nonblocking(fd, true));
114 let ret = match unsafe { libc::connect(fd, addrp, len) } {
115 // If the connection is in progress, then we need to wait for it to
116 // finish (with a timeout). The current strategy for doing this is
117 // to use select() with a timeout.
118 -1 if os::errno() as int == INPROGRESS as int ||
119 os::errno() as int == WOULDBLOCK as int => {
120 let mut set: c::fd_set = unsafe { mem::zeroed() };
121 c::fd_set(&mut set, fd);
122 match await(fd, &mut set, timeout_ms) {
123 0 => Err(timeout("connection timed out")),
124 -1 => Err(last_error()),
126 let err: libc::c_int = try!(
127 net::getsockopt(fd, libc::SOL_SOCKET, libc::SO_ERROR));
134 detail: Some(os::error_string(err as uint)),
141 -1 => Err(last_error()),
145 // be sure to turn blocking I/O back on
146 try!(set_nonblocking(fd, false));
150 fn await(fd: net::sock_t, set: &mut c::fd_set,
151 timeout: u64) -> libc::c_int {
152 let start = ::io::timer::now();
154 // Recalculate the timeout each iteration (it is generally
155 // undefined what the value of the 'tv' is after select
157 let mut tv = ms_to_timeval(timeout - (::io::timer::now() - start));
158 c::select(fd + 1, ptr::mut_null(), set as *mut _,
159 ptr::mut_null(), &mut tv)
163 fn await(_fd: net::sock_t, set: &mut c::fd_set,
164 timeout: u64) -> libc::c_int {
165 let mut tv = ms_to_timeval(timeout);
166 unsafe { c::select(1, ptr::mut_null(), set, ptr::mut_null(), &mut tv) }
170 pub fn await(fds: &[net::sock_t], deadline: Option<u64>,
171 status: SocketStatus) -> IoResult<()> {
172 let mut set: c::fd_set = unsafe { mem::zeroed() };
174 for &fd in fds.iter() {
175 c::fd_set(&mut set, fd);
176 max = cmp::max(max, fd + 1);
179 let (read, write) = match status {
180 Readable => (&mut set as *mut _, ptr::mut_null()),
181 Writable => (ptr::mut_null(), &mut set as *mut _),
183 let mut tv: libc::timeval = unsafe { mem::zeroed() };
186 let now = ::io::timer::now();
187 let tvp = match deadline {
188 None => ptr::mut_null(),
190 // If we're past the deadline, then pass a 0 timeout to
191 // select() so we can poll the status
192 let ms = if deadline < now {0} else {deadline - now};
193 tv = ms_to_timeval(ms);
198 c::select(max as libc::c_int, read, write, ptr::mut_null(), tvp)
202 -1 => Err(last_error()),
203 0 => Err(timeout("timed out")),