]> git.lizzy.rs Git - rust.git/blob - src/libnative/io/util.rs
Rename all raw pointers as necessary
[rust.git] / src / libnative / io / util.rs
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.
4 //
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.
10
11 use libc;
12 use std::mem;
13 use std::os;
14 use std::ptr;
15 use std::rt::rtio::{IoResult, IoError};
16
17 use super::c;
18 use super::net;
19 use super::{retry, last_error};
20
21 #[deriving(Show)]
22 pub enum SocketStatus {
23     Readable,
24     Writable,
25 }
26
27 pub fn timeout(desc: &'static str) -> IoError {
28     #[cfg(unix)] use ERROR = libc::ETIMEDOUT;
29     #[cfg(windows)] use ERROR = libc::ERROR_OPERATION_ABORTED;
30     IoError {
31         code: ERROR as uint,
32         extra: 0,
33         detail: Some(desc.to_str()),
34     }
35 }
36
37 pub fn short_write(n: uint, desc: &'static str) -> IoError {
38     #[cfg(unix)] use ERROR = libc::EAGAIN;
39     #[cfg(windows)] use ERROR = libc::ERROR_OPERATION_ABORTED;
40     IoError {
41         code: ERROR as uint,
42         extra: n,
43         detail: Some(desc.to_str()),
44     }
45 }
46
47 pub fn eof() -> IoError {
48     IoError {
49         code: libc::EOF as uint,
50         extra: 0,
51         detail: None,
52     }
53 }
54
55 pub fn ms_to_timeval(ms: u64) -> libc::timeval {
56     libc::timeval {
57         tv_sec: (ms / 1000) as libc::time_t,
58         tv_usec: ((ms % 1000) * 1000) as libc::suseconds_t,
59     }
60 }
61
62 #[cfg(unix)]
63 pub fn wouldblock() -> bool {
64     let err = os::errno();
65     err == libc::EWOULDBLOCK as int || err == libc::EAGAIN as int
66 }
67
68 #[cfg(windows)]
69 pub fn wouldblock() -> bool {
70     let err = os::errno();
71     err == libc::WSAEWOULDBLOCK as uint
72 }
73
74 #[cfg(unix)]
75 pub fn set_nonblocking(fd: net::sock_t, nb: bool) -> IoResult<()> {
76     let set = nb as libc::c_int;
77     super::mkerr_libc(retry(|| unsafe { c::ioctl(fd, c::FIONBIO, &set) }))
78 }
79
80 #[cfg(windows)]
81 pub fn set_nonblocking(fd: net::sock_t, nb: bool) -> IoResult<()> {
82     let mut set = nb as libc::c_ulong;
83     if unsafe { c::ioctlsocket(fd, c::FIONBIO, &mut set) != 0 } {
84         Err(last_error())
85     } else {
86         Ok(())
87     }
88 }
89
90 // See http://developerweb.net/viewtopic.php?id=3196 for where this is
91 // derived from.
92 pub fn connect_timeout(fd: net::sock_t,
93                        addrp: *const libc::sockaddr,
94                        len: libc::socklen_t,
95                        timeout_ms: u64) -> IoResult<()> {
96     use std::os;
97     #[cfg(unix)]    use INPROGRESS = libc::EINPROGRESS;
98     #[cfg(windows)] use INPROGRESS = libc::WSAEINPROGRESS;
99     #[cfg(unix)]    use WOULDBLOCK = libc::EWOULDBLOCK;
100     #[cfg(windows)] use WOULDBLOCK = libc::WSAEWOULDBLOCK;
101
102     // Make sure the call to connect() doesn't block
103     try!(set_nonblocking(fd, true));
104
105     let ret = match unsafe { libc::connect(fd, addrp, len) } {
106         // If the connection is in progress, then we need to wait for it to
107         // finish (with a timeout). The current strategy for doing this is
108         // to use select() with a timeout.
109         -1 if os::errno() as int == INPROGRESS as int ||
110               os::errno() as int == WOULDBLOCK as int => {
111             let mut set: c::fd_set = unsafe { mem::zeroed() };
112             c::fd_set(&mut set, fd);
113             match await(fd, &mut set, timeout_ms) {
114                 0 => Err(timeout("connection timed out")),
115                 -1 => Err(last_error()),
116                 _ => {
117                     let err: libc::c_int = try!(
118                         net::getsockopt(fd, libc::SOL_SOCKET, libc::SO_ERROR));
119                     if err == 0 {
120                         Ok(())
121                     } else {
122                         Err(IoError {
123                             code: err as uint,
124                             extra: 0,
125                             detail: Some(os::error_string(err as uint)),
126                         })
127                     }
128                 }
129             }
130         }
131
132         -1 => Err(last_error()),
133         _ => Ok(()),
134     };
135
136     // be sure to turn blocking I/O back on
137     try!(set_nonblocking(fd, false));
138     return ret;
139
140     #[cfg(unix)]
141     fn await(fd: net::sock_t, set: &mut c::fd_set,
142              timeout: u64) -> libc::c_int {
143         let start = ::io::timer::now();
144         retry(|| unsafe {
145             // Recalculate the timeout each iteration (it is generally
146             // undefined what the value of the 'tv' is after select
147             // returns EINTR).
148             let mut tv = ms_to_timeval(timeout - (::io::timer::now() - start));
149             c::select(fd + 1, ptr::mut_null(), set as *mut _,
150                       ptr::mut_null(), &mut tv)
151         })
152     }
153     #[cfg(windows)]
154     fn await(_fd: net::sock_t, set: &mut c::fd_set,
155              timeout: u64) -> libc::c_int {
156         let mut tv = ms_to_timeval(timeout);
157         unsafe { c::select(1, ptr::mut_null(), set, ptr::mut_null(), &mut tv) }
158     }
159 }
160
161 pub fn await(fd: net::sock_t, deadline: Option<u64>,
162              status: SocketStatus) -> IoResult<()> {
163     let mut set: c::fd_set = unsafe { mem::zeroed() };
164     c::fd_set(&mut set, fd);
165     let (read, write) = match status {
166         Readable => (&mut set as *mut _, ptr::mut_null()),
167         Writable => (ptr::mut_null(), &mut set as *mut _),
168     };
169     let mut tv: libc::timeval = unsafe { mem::zeroed() };
170
171     match retry(|| {
172         let now = ::io::timer::now();
173         let tvp = match deadline {
174             None => ptr::mut_null(),
175             Some(deadline) => {
176                 // If we're past the deadline, then pass a 0 timeout to
177                 // select() so we can poll the status
178                 let ms = if deadline < now {0} else {deadline - now};
179                 tv = ms_to_timeval(ms);
180                 &mut tv as *mut _
181             }
182         };
183         let n = if cfg!(windows) {1} else {fd as libc::c_int + 1};
184         let r = unsafe { c::select(n, read, write, ptr::mut_null(), tvp) };
185         r
186     }) {
187         -1 => Err(last_error()),
188         0 => Err(timeout("timed out")),
189         _ => Ok(()),
190     }
191 }