]> git.lizzy.rs Git - rust.git/blob - src/libstd/sys/unix/tcp.rs
Merge pull request #20510 from tshepang/patch-6
[rust.git] / src / libstd / sys / unix / tcp.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 prelude::v1::*;
12
13 use io::net::ip;
14 use io::IoResult;
15 use libc;
16 use mem;
17 use ptr;
18 use super::{last_error, last_net_error, retry, sock_t};
19 use sync::Arc;
20 use sync::atomic::{AtomicBool, Ordering};
21 use sys::fs::FileDesc;
22 use sys::{set_nonblocking, wouldblock};
23 use sys;
24 use sys_common;
25 use sys_common::net;
26 use sys_common::net::SocketStatus::Readable;
27
28 pub use sys_common::net::TcpStream;
29
30 ////////////////////////////////////////////////////////////////////////////////
31 // TCP listeners
32 ////////////////////////////////////////////////////////////////////////////////
33
34 pub struct TcpListener {
35     pub inner: FileDesc,
36 }
37
38 unsafe impl Sync for TcpListener {}
39
40 impl TcpListener {
41     pub fn bind(addr: ip::SocketAddr) -> IoResult<TcpListener> {
42         let fd = try!(net::socket(addr, libc::SOCK_STREAM));
43         let ret = TcpListener { inner: FileDesc::new(fd, true) };
44
45         let mut storage = unsafe { mem::zeroed() };
46         let len = net::addr_to_sockaddr(addr, &mut storage);
47         let addrp = &storage as *const _ as *const libc::sockaddr;
48
49         // On platforms with Berkeley-derived sockets, this allows
50         // to quickly rebind a socket, without needing to wait for
51         // the OS to clean up the previous one.
52         try!(net::setsockopt(fd, libc::SOL_SOCKET,
53                              libc::SO_REUSEADDR,
54                              1 as libc::c_int));
55
56
57         match unsafe { libc::bind(fd, addrp, len) } {
58             -1 => Err(last_error()),
59             _ => Ok(ret),
60         }
61     }
62
63     pub fn fd(&self) -> sock_t { self.inner.fd() }
64
65     pub fn listen(self, backlog: int) -> IoResult<TcpAcceptor> {
66         match unsafe { libc::listen(self.fd(), backlog as libc::c_int) } {
67             -1 => Err(last_net_error()),
68             _ => {
69                 let (reader, writer) = try!(unsafe { sys::os::pipe() });
70                 try!(set_nonblocking(reader.fd(), true));
71                 try!(set_nonblocking(writer.fd(), true));
72                 try!(set_nonblocking(self.fd(), true));
73                 Ok(TcpAcceptor {
74                     inner: Arc::new(AcceptorInner {
75                         listener: self,
76                         reader: reader,
77                         writer: writer,
78                         closed: AtomicBool::new(false),
79                     }),
80                     deadline: 0,
81                 })
82             }
83         }
84     }
85
86     pub fn socket_name(&mut self) -> IoResult<ip::SocketAddr> {
87         net::sockname(self.fd(), libc::getsockname)
88     }
89 }
90
91 pub struct TcpAcceptor {
92     inner: Arc<AcceptorInner>,
93     deadline: u64,
94 }
95
96 struct AcceptorInner {
97     listener: TcpListener,
98     reader: FileDesc,
99     writer: FileDesc,
100     closed: AtomicBool,
101 }
102
103 unsafe impl Sync for AcceptorInner {}
104
105 impl TcpAcceptor {
106     pub fn fd(&self) -> sock_t { self.inner.listener.fd() }
107
108     pub fn accept(&mut self) -> IoResult<TcpStream> {
109         // In implementing accept, the two main concerns are dealing with
110         // close_accept() and timeouts. The unix implementation is based on a
111         // nonblocking accept plus a call to select(). Windows ends up having
112         // an entirely separate implementation than unix, which is explained
113         // below.
114         //
115         // To implement timeouts, all blocking is done via select() instead of
116         // accept() by putting the socket in non-blocking mode. Because
117         // select() takes a timeout argument, we just pass through the timeout
118         // to select().
119         //
120         // To implement close_accept(), we have a self-pipe to ourselves which
121         // is passed to select() along with the socket being accepted on. The
122         // self-pipe is never written to unless close_accept() is called.
123         let deadline = if self.deadline == 0 {None} else {Some(self.deadline)};
124
125         while !self.inner.closed.load(Ordering::SeqCst) {
126             match retry(|| unsafe {
127                 libc::accept(self.fd(), ptr::null_mut(), ptr::null_mut())
128             }) {
129                 -1 if wouldblock() => {}
130                 -1 => return Err(last_net_error()),
131                 fd => return Ok(TcpStream::new(fd as sock_t)),
132             }
133             try!(net::await(&[self.fd(), self.inner.reader.fd()],
134                        deadline, Readable));
135         }
136
137         Err(sys_common::eof())
138     }
139
140     pub fn socket_name(&mut self) -> IoResult<ip::SocketAddr> {
141         net::sockname(self.fd(), libc::getsockname)
142     }
143
144     pub fn set_timeout(&mut self, timeout: Option<u64>) {
145         self.deadline = timeout.map(|a| sys::timer::now() + a).unwrap_or(0);
146     }
147
148     pub fn close_accept(&mut self) -> IoResult<()> {
149         self.inner.closed.store(true, Ordering::SeqCst);
150         let fd = FileDesc::new(self.inner.writer.fd(), false);
151         match fd.write(&[0]) {
152             Ok(..) => Ok(()),
153             Err(..) if wouldblock() => Ok(()),
154             Err(e) => Err(e),
155         }
156     }
157 }
158
159 impl Clone for TcpAcceptor {
160     fn clone(&self) -> TcpAcceptor {
161         TcpAcceptor {
162             inner: self.inner.clone(),
163             deadline: 0,
164         }
165     }
166 }