Skip to content

Commit b5e602a

Browse files
committed
auto merge of #10321 : alexcrichton/rust/uv-rewrite, r=brson
The major impetus for this pull request was to remove all usage of `~fn()` in `librustuv`. This construct is going away as a language feature, and additionally it imposes the requirement that all I/O operations have at least one allocation. This allocation has been seen to have a fairly high performance impact in profiles of I/O benchmarks. I've migrated `librustuv` away from all usage of `~fn()`, and at the same time it no longer allocates on every I/O operation anywhere. The scheduler is now much more tightly integrated with all of the libuv bindings and most of the uv callbacks are specialized functions for a certain procedure. This is a step backwards in terms of making `librustuv` usable anywhere else, but I think that the performance gains are a big win here. In just a simple benchmark of reading/writing 4k of 0s at a time between a tcp client/server in separate processes on the same system, I have witnessed the throughput increase from ~750MB/s to ~1200MB/s with this change applied. I'm still in the process of testing this change, although all the major bugs (to my knowledge) have been fleshed out and removed. There are still a few spurious segfaults, and that's what I'm currently investigating. In the meantime, I wanted to put this up for review to get some eyes on it other than mine. I'll update this once I've got all the tests passing reliably again.
2 parents 3851f90 + e38a89d commit b5e602a

35 files changed

+3630
-5672
lines changed

mk/rt.mk

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ LIBUV_MAKEFILE_$(1) := $$(CFG_BUILD_DIR)$$(RT_OUTPUT_DIR_$(1))/libuv/Makefile
207207

208208
$$(LIBUV_MAKEFILE_$(1)): $$(LIBUV_DEPS)
209209
(cd $(S)src/libuv/ && \
210-
$$(CFG_PYTHON) ./gyp_uv -f make -Dtarget_arch=$$(LIBUV_ARCH_$(1)) \
210+
$$(CFG_PYTHON) ./gyp_uv.py -f make -Dtarget_arch=$$(LIBUV_ARCH_$(1)) \
211211
-D ninja \
212212
-DOS=$$(LIBUV_OSTYPE_$(1)) \
213213
-Goutput_dir=$$(@D) --generator-output $$(@D))
@@ -218,7 +218,7 @@ $$(LIBUV_MAKEFILE_$(1)): $$(LIBUV_DEPS)
218218
ifdef CFG_WINDOWSY_$(1)
219219
$$(LIBUV_LIB_$(1)): $$(LIBUV_DEPS)
220220
$$(Q)$$(MAKE) -C $$(S)src/libuv -f Makefile.mingw \
221-
CFLAGS="$$(CFG_GCCISH_CFLAGS) $$(LIBUV_FLAGS_$$(HOST_$(1))) $$(SNAP_DEFINES)" \
221+
CC="$$(CC) $$(CFG_GCCISH_CFLAGS) $$(LIBUV_FLAGS_$$(HOST_$(1))) $$(SNAP_DEFINES)" \
222222
AR="$$(AR_$(1))" \
223223
V=$$(VERBOSE)
224224
$$(Q)cp $$(S)src/libuv/libuv.a $$@

src/librustuv/addrinfo.rs

Lines changed: 70 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -8,41 +8,34 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
use std::cast::transmute;
12-
use std::cell::Cell;
13-
use std::libc::{c_int, c_void};
14-
use std::ptr::null;
1511
use ai = std::rt::io::net::addrinfo;
12+
use std::libc::c_int;
13+
use std::ptr::null;
14+
use std::rt::BlockedTask;
15+
use std::rt::local::Local;
16+
use std::rt::sched::Scheduler;
1617

17-
use uvll;
18-
use uvll::UV_GETADDRINFO;
19-
use super::{Loop, UvError, NativeHandle, status_to_maybe_uv_error};
2018
use net;
19+
use super::{Loop, UvError, Request, wait_until_woken_after};
20+
use uvll;
2121

22-
type GetAddrInfoCallback = ~fn(GetAddrInfoRequest, &net::UvAddrInfo, Option<UvError>);
23-
24-
pub struct GetAddrInfoRequest(*uvll::uv_getaddrinfo_t);
25-
26-
pub struct RequestData {
27-
priv getaddrinfo_cb: Option<GetAddrInfoCallback>,
22+
struct Addrinfo {
23+
handle: *uvll::addrinfo,
2824
}
2925

30-
impl GetAddrInfoRequest {
31-
pub fn new() -> GetAddrInfoRequest {
32-
let req = unsafe { uvll::malloc_req(UV_GETADDRINFO) };
33-
assert!(req.is_not_null());
34-
let mut req: GetAddrInfoRequest = NativeHandle::from_native_handle(req);
35-
req.install_req_data();
36-
return req;
37-
}
26+
struct Ctx {
27+
slot: Option<BlockedTask>,
28+
status: c_int,
29+
addrinfo: Option<Addrinfo>,
30+
}
3831

39-
pub fn getaddrinfo(&mut self, loop_: &Loop, node: Option<&str>,
40-
service: Option<&str>, hints: Option<ai::Hint>,
41-
cb: GetAddrInfoCallback) {
32+
pub struct GetAddrInfoRequest;
4233

34+
impl GetAddrInfoRequest {
35+
pub fn run(loop_: &Loop, node: Option<&str>, service: Option<&str>,
36+
hints: Option<ai::Hint>) -> Result<~[ai::Info], UvError> {
4337
assert!(node.is_some() || service.is_some());
44-
45-
let (c_node, c_node_ptr) = match node {
38+
let (_c_node, c_node_ptr) = match node {
4639
Some(n) => {
4740
let c_node = n.to_c_str();
4841
let c_node_ptr = c_node.with_ref(|r| r);
@@ -51,7 +44,7 @@ impl GetAddrInfoRequest {
5144
None => (None, null())
5245
};
5346

54-
let (c_service, c_service_ptr) = match service {
47+
let (_c_service, c_service_ptr) = match service {
5548
Some(s) => {
5649
let c_service = s.to_c_str();
5750
let c_service_ptr = c_service.with_ref(|r| r);
@@ -60,37 +53,13 @@ impl GetAddrInfoRequest {
6053
None => (None, null())
6154
};
6255

63-
let cb = Cell::new(cb);
64-
let wrapper_cb: GetAddrInfoCallback = |req, addrinfo, err| {
65-
// Capture some heap values that need to stay alive for the
66-
// getaddrinfo call
67-
let _ = &c_node;
68-
let _ = &c_service;
69-
70-
let cb = cb.take();
71-
cb(req, addrinfo, err)
72-
};
73-
7456
let hint = hints.map(|hint| {
7557
let mut flags = 0;
7658
do each_ai_flag |cval, aival| {
7759
if hint.flags & (aival as uint) != 0 {
7860
flags |= cval as i32;
7961
}
8062
}
81-
/* XXX: do we really want to support these?
82-
let socktype = match hint.socktype {
83-
Some(ai::Stream) => uvll::rust_SOCK_STREAM(),
84-
Some(ai::Datagram) => uvll::rust_SOCK_DGRAM(),
85-
Some(ai::Raw) => uvll::rust_SOCK_RAW(),
86-
None => 0,
87-
};
88-
let protocol = match hint.protocol {
89-
Some(ai::UDP) => uvll::rust_IPPROTO_UDP(),
90-
Some(ai::TCP) => uvll::rust_IPPROTO_TCP(),
91-
_ => 0,
92-
};
93-
*/
9463
let socktype = 0;
9564
let protocol = 0;
9665

@@ -106,66 +75,48 @@ impl GetAddrInfoRequest {
10675
}
10776
});
10877
let hint_ptr = hint.as_ref().map_default(null(), |x| x as *uvll::addrinfo);
78+
let mut req = Request::new(uvll::UV_GETADDRINFO);
79+
80+
return match unsafe {
81+
uvll::uv_getaddrinfo(loop_.handle, req.handle,
82+
getaddrinfo_cb, c_node_ptr, c_service_ptr,
83+
hint_ptr)
84+
} {
85+
0 => {
86+
req.defuse(); // uv callback now owns this request
87+
let mut cx = Ctx { slot: None, status: 0, addrinfo: None };
88+
89+
do wait_until_woken_after(&mut cx.slot) {
90+
req.set_data(&cx);
91+
}
10992

110-
self.get_req_data().getaddrinfo_cb = Some(wrapper_cb);
111-
112-
unsafe {
113-
assert!(0 == uvll::getaddrinfo(loop_.native_handle(),
114-
self.native_handle(),
115-
getaddrinfo_cb,
116-
c_node_ptr,
117-
c_service_ptr,
118-
hint_ptr));
119-
}
120-
121-
extern "C" fn getaddrinfo_cb(req: *uvll::uv_getaddrinfo_t,
122-
status: c_int,
123-
res: *uvll::addrinfo) {
124-
let mut req: GetAddrInfoRequest = NativeHandle::from_native_handle(req);
125-
let err = status_to_maybe_uv_error(status);
126-
let addrinfo = net::UvAddrInfo(res);
127-
let data = req.get_req_data();
128-
(*data.getaddrinfo_cb.get_ref())(req, &addrinfo, err);
129-
unsafe {
130-
uvll::freeaddrinfo(res);
93+
match cx.status {
94+
0 => Ok(accum_addrinfo(cx.addrinfo.get_ref())),
95+
n => Err(UvError(n))
96+
}
13197
}
132-
}
133-
}
98+
n => Err(UvError(n))
99+
};
134100

135-
fn get_loop(&self) -> Loop {
136-
unsafe {
137-
Loop {
138-
handle: uvll::get_loop_from_fs_req(self.native_handle())
139-
}
140-
}
141-
}
142101

143-
fn install_req_data(&mut self) {
144-
let req = self.native_handle() as *uvll::uv_getaddrinfo_t;
145-
let data = ~RequestData {
146-
getaddrinfo_cb: None
147-
};
148-
unsafe {
149-
let data = transmute::<~RequestData, *c_void>(data);
150-
uvll::set_data_for_req(req, data);
151-
}
152-
}
102+
extern fn getaddrinfo_cb(req: *uvll::uv_getaddrinfo_t,
103+
status: c_int,
104+
res: *uvll::addrinfo) {
105+
let req = Request::wrap(req);
106+
assert!(status != uvll::ECANCELED);
107+
let cx: &mut Ctx = unsafe { req.get_data() };
108+
cx.status = status;
109+
cx.addrinfo = Some(Addrinfo { handle: res });
153110

154-
fn get_req_data<'r>(&'r mut self) -> &'r mut RequestData {
155-
unsafe {
156-
let data = uvll::get_data_for_req(self.native_handle());
157-
let data = transmute::<&*c_void, &mut ~RequestData>(&data);
158-
return &mut **data;
111+
let sched: ~Scheduler = Local::take();
112+
sched.resume_blocked_task_immediately(cx.slot.take_unwrap());
159113
}
160114
}
115+
}
161116

162-
fn delete(self) {
163-
unsafe {
164-
let data = uvll::get_data_for_req(self.native_handle());
165-
let _data = transmute::<*c_void, ~RequestData>(data);
166-
uvll::set_data_for_req(self.native_handle(), null::<()>());
167-
uvll::free_req(self.native_handle());
168-
}
117+
impl Drop for Addrinfo {
118+
fn drop(&mut self) {
119+
unsafe { uvll::uv_freeaddrinfo(self.handle) }
169120
}
170121
}
171122

@@ -184,15 +135,13 @@ fn each_ai_flag(_f: &fn(c_int, ai::Flag)) {
184135
}
185136

186137
// Traverse the addrinfo linked list, producing a vector of Rust socket addresses
187-
pub fn accum_addrinfo(addr: &net::UvAddrInfo) -> ~[ai::Info] {
138+
pub fn accum_addrinfo(addr: &Addrinfo) -> ~[ai::Info] {
188139
unsafe {
189-
let &net::UvAddrInfo(addr) = addr;
190-
let mut addr = addr;
140+
let mut addr = addr.handle;
191141

192142
let mut addrs = ~[];
193143
loop {
194-
let uvaddr = net::sockaddr_to_UvSocketAddr((*addr).ai_addr);
195-
let rustaddr = net::uv_socket_addr_to_socket_addr(uvaddr);
144+
let rustaddr = net::sockaddr_to_socket_addr((*addr).ai_addr);
196145

197146
let mut flags = 0;
198147
do each_ai_flag |cval, aival| {
@@ -235,39 +184,27 @@ pub fn accum_addrinfo(addr: &net::UvAddrInfo) -> ~[ai::Info] {
235184
}
236185
}
237186

238-
impl NativeHandle<*uvll::uv_getaddrinfo_t> for GetAddrInfoRequest {
239-
fn from_native_handle(handle: *uvll::uv_getaddrinfo_t) -> GetAddrInfoRequest {
240-
GetAddrInfoRequest(handle)
241-
}
242-
fn native_handle(&self) -> *uvll::uv_getaddrinfo_t {
243-
match self { &GetAddrInfoRequest(ptr) => ptr }
244-
}
245-
}
246-
247187
#[cfg(test)]
248188
mod test {
249-
use Loop;
250189
use std::rt::io::net::ip::{SocketAddr, Ipv4Addr};
251190
use super::*;
191+
use super::super::local_loop;
252192

253193
#[test]
254194
fn getaddrinfo_test() {
255-
let mut loop_ = Loop::new();
256-
let mut req = GetAddrInfoRequest::new();
257-
do req.getaddrinfo(&loop_, Some("localhost"), None, None) |_, addrinfo, _| {
258-
let sockaddrs = accum_addrinfo(addrinfo);
259-
let mut found_local = false;
260-
let local_addr = &SocketAddr {
261-
ip: Ipv4Addr(127, 0, 0, 1),
262-
port: 0
263-
};
264-
for addr in sockaddrs.iter() {
265-
found_local = found_local || addr.address == *local_addr;
195+
match GetAddrInfoRequest::run(local_loop(), Some("localhost"), None, None) {
196+
Ok(infos) => {
197+
let mut found_local = false;
198+
let local_addr = &SocketAddr {
199+
ip: Ipv4Addr(127, 0, 0, 1),
200+
port: 0
201+
};
202+
for addr in infos.iter() {
203+
found_local = found_local || addr.address == *local_addr;
204+
}
205+
assert!(found_local);
266206
}
267-
assert!(found_local);
207+
Err(e) => fail!("{:?}", e),
268208
}
269-
loop_.run();
270-
loop_.close();
271-
req.delete();
272209
}
273210
}

0 commit comments

Comments
 (0)