Skip to content

Commit 791ea34

Browse files
olsonjefferybrson
authored andcommitted
std::uv : cleanup and an isolated test for hand-rolled high_level_loops
1 parent e02057c commit 791ea34

File tree

3 files changed

+204
-29
lines changed

3 files changed

+204
-29
lines changed

src/libstd/uv_global_loop.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,9 @@ native mod rustrt {
2121
Race-free helper to get access to a global task where a libuv
2222
loop is running.
2323
24-
Use `uv::hl::interact`, `uv::hl::ref_handle` and `uv::hl::unref_handle` to
25-
do operations against the global loop that this function returns.
24+
Use `uv::hl::interact`, `uv::hl::ref`, `uv::hl::unref` and
25+
uv `uv::hl::unref_and_close` to do operations against the global
26+
loop that this function returns.
2627
2728
# Return
2829
@@ -33,6 +34,7 @@ fn get() -> hl::high_level_loop {
3334
ret get_monitor_task_gl();
3435
}
3536

37+
// WARNING: USE ONLY ONE get_*_task_gl fn in the scope of a process lifetime.
3638
#[doc(hidden)]
3739
fn get_monitor_task_gl() -> hl::high_level_loop {
3840
let monitor_loop_chan =
@@ -51,6 +53,7 @@ fn get_monitor_task_gl() -> hl::high_level_loop {
5153
});
5254
}
5355

56+
// WARNING: USE ONLY ONE get_*_task_gl fn in the scope of a process lifetime.
5457
#[doc(hidden)]
5558
fn get_single_task_gl() -> hl::high_level_loop {
5659
let global_loop_chan_ptr = rustrt::rust_uv_get_kernel_global_chan_ptr();
@@ -323,7 +326,7 @@ mod test {
323326
hl::interact(hl_loop) {|loop_ptr|
324327
log(debug, "closing timer");
325328
//ll::close(timer_ptr as *libc::c_void, simple_timer_close_cb);
326-
hl::unref_handle(hl_loop, timer_ptr, simple_timer_close_cb);
329+
hl::unref_and_close(hl_loop, timer_ptr, simple_timer_close_cb);
327330
log(debug, "about to deref exit_ch_ptr");
328331
log(debug, "after msg sent on deref'd exit_ch");
329332
};
@@ -342,7 +345,7 @@ mod test {
342345
log(debug, "user code inside interact loop!!!");
343346
let init_status = ll::timer_init(loop_ptr, timer_ptr);
344347
if(init_status == 0i32) {
345-
hl::ref_handle(hl_loop, timer_ptr);
348+
hl::ref(hl_loop, timer_ptr);
346349
ll::set_data_for_uv_handle(
347350
timer_ptr as *libc::c_void,
348351
exit_ch_ptr as *libc::c_void);

src/libstd/uv_hl.rs

Lines changed: 195 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ libuv functionality.
77
"];
88

99
export high_level_loop, hl_loop_ext, high_level_msg;
10-
export run_high_level_loop, interact, ref_handle, unref_handle;
10+
export run_high_level_loop, interact, ref, unref, unref_and_close;
1111

1212
import ll = uv_ll;
1313

@@ -23,6 +23,10 @@ the C uv loop to process any pending callbacks
2323
by the C uv loop
2424
"]
2525
enum high_level_loop {
26+
simple_task_loop({
27+
async_handle: *ll::uv_async_t,
28+
op_chan: comm::chan<high_level_msg>
29+
}),
2630
single_task_loop({
2731
async_handle: **ll::uv_async_t,
2832
op_chan: comm::chan<high_level_msg>
@@ -52,6 +56,9 @@ impl hl_loop_ext for high_level_loop {
5256
monitor_task_loop({op_chan}) {
5357
ret op_chan;
5458
}
59+
simple_task_loop({async_handle, op_chan}) {
60+
ret op_chan;
61+
}
5562
}
5663
}
5764
}
@@ -61,8 +68,8 @@ Represents the range of interactions with a `high_level_loop`
6168
"]
6269
enum high_level_msg {
6370
interaction (fn~(*libc::c_void)),
64-
auto_ref_handle (*libc::c_void),
65-
auto_unref_handle (*libc::c_void, *u8),
71+
ref_handle (*libc::c_void),
72+
manual_unref_handle (*libc::c_void, option<*u8>),
6673
tear_down
6774
}
6875

@@ -96,7 +103,7 @@ unsafe fn run_high_level_loop(loop_ptr: *libc::c_void,
96103
ll::async_init(loop_ptr, async_handle, high_level_wake_up_cb);
97104

98105
// initialize our loop data and store it in the loop
99-
let data: global_loop_data = default_gl_data({
106+
let data: hl_loop_data = default_gl_data({
100107
async_handle: async_handle,
101108
mut active: true,
102109
before_msg_drain: before_msg_drain,
@@ -159,22 +166,25 @@ resource safe_handle_container<T>(handle_fields: safe_handle_fields<T>) {
159166
#[doc="
160167
Needs to be encapsulated within `safe_handle`
161168
"]
162-
fn ref_handle<T>(hl_loop: high_level_loop, handle: *T) unsafe {
163-
send_high_level_msg(hl_loop, auto_ref_handle(handle as *libc::c_void));
169+
fn ref<T>(hl_loop: high_level_loop, handle: *T) unsafe {
170+
send_high_level_msg(hl_loop, ref_handle(handle as *libc::c_void));
164171
}
165172
#[doc="
166173
Needs to be encapsulated within `safe_handle`
167174
"]
168-
fn unref_handle<T>(hl_loop: high_level_loop, handle: *T,
169-
user_close_cb: *u8) unsafe {
170-
send_high_level_msg(hl_loop, auto_unref_handle(handle as *libc::c_void,
171-
user_close_cb));
175+
fn unref<T>(hl_loop: high_level_loop, handle: *T) unsafe {
176+
send_high_level_msg(hl_loop, manual_unref_handle(handle as *libc::c_void,
177+
none));
178+
}
179+
fn unref_and_close<T>(hl_loop: high_level_loop, handle: *T, cb: *u8) unsafe {
180+
send_high_level_msg(hl_loop, manual_unref_handle(handle as *libc::c_void,
181+
some(cb)));
172182
}
173183

174184
// INTERNAL API
175185

176186
// data that lives for the lifetime of the high-evel oo
177-
enum global_loop_data {
187+
enum hl_loop_data {
178188
default_gl_data({
179189
async_handle: *ll::uv_async_t,
180190
mut active: bool,
@@ -203,6 +213,10 @@ unsafe fn send_high_level_msg(hl_loop: high_level_loop,
203213
log(debug,"GLOBAL ASYNC handle == 0");
204214
}
205215
}
216+
simple_task_loop({async_handle, op_chan}) {
217+
log(debug,"simple async handle != 0, waking up loop..");
218+
ll::async_send((async_handle));
219+
}
206220
_ {}
207221
}
208222
}
@@ -218,7 +232,7 @@ crust fn high_level_wake_up_cb(async_handle: *ll::uv_async_t,
218232
log(debug, #fmt("high_level_wake_up_cb crust.. handle: %? status: %?",
219233
async_handle, status));
220234
let loop_ptr = ll::get_loop_for_uv_handle(async_handle);
221-
let data = ll::get_data_for_uv_handle(async_handle) as *global_loop_data;
235+
let data = ll::get_data_for_uv_handle(async_handle) as *hl_loop_data;
222236
// we check to see if the loop is "active" (the loop is set to
223237
// active = false the first time we realize we need to 'tear down',
224238
// set subsequent calls to the global async handle may be triggered
@@ -245,11 +259,11 @@ crust fn high_level_wake_up_cb(async_handle: *ll::uv_async_t,
245259
cb(loop_ptr);
246260
log(debug,"after calling cb");
247261
}
248-
auto_ref_handle(handle) {
262+
ref_handle(handle) {
249263
high_level_ref(data, handle);
250264
}
251-
auto_unref_handle(handle, user_close_cb) {
252-
high_level_unref(data, handle, false, user_close_cb);
265+
manual_unref_handle(handle, user_close_cb) {
266+
high_level_unref(data, handle, true, user_close_cb);
253267
}
254268
tear_down {
255269
log(debug,"incoming hl_msg: got tear_down");
@@ -258,6 +272,9 @@ crust fn high_level_wake_up_cb(async_handle: *ll::uv_async_t,
258272
continue = comm::peek(msg_po);
259273
}
260274
}
275+
else {
276+
log(debug, "in hl wake_cb, no pending messages");
277+
}
261278
}
262279
log(debug, #fmt("after on_wake, continue? %?", continue));
263280
if !do_msg_drain {
@@ -269,50 +286,80 @@ crust fn high_level_wake_up_cb(async_handle: *ll::uv_async_t,
269286
crust fn tear_down_close_cb(handle: *ll::uv_async_t) unsafe {
270287
log(debug, #fmt("tear_down_close_cb called, closing handle at %?",
271288
handle));
272-
let data = ll::get_data_for_uv_handle(handle) as *global_loop_data;
273-
if vec::len((*data).refd_handles) > 0 {
289+
let data = ll::get_data_for_uv_handle(handle) as *hl_loop_data;
290+
if vec::len((*data).refd_handles) > 0u {
274291
fail "Didn't unref all high-level handles";
275292
}
276293
}
277294

278-
fn high_level_tear_down(data: *global_loop_data) unsafe {
295+
fn high_level_tear_down(data: *hl_loop_data) unsafe {
279296
log(debug, "high_level_tear_down() called, close async_handle");
280297
// call user-suppled before_tear_down cb
281298
let async_handle = (*data).async_handle;
282299
(*data).before_tear_down(async_handle);
283300
ll::close(async_handle as *libc::c_void, tear_down_close_cb);
284301
}
285302

286-
unsafe fn high_level_ref(data: *global_loop_data, handle: *libc::c_void) {
287-
log(debug,"incoming hl_msg: got auto_ref_handle");
303+
unsafe fn high_level_ref(data: *hl_loop_data, handle: *libc::c_void) {
304+
log(debug,"incoming hl_msg: got ..ref_handle");
288305
let mut refd_handles = (*data).refd_handles;
306+
let mut unrefd_handles = (*data).unrefd_handles;
289307
let handle_already_refd = refd_handles.contains(handle);
290308
if handle_already_refd {
291309
fail "attempt to do a high-level ref an already ref'd handle";
292310
}
311+
let handle_already_unrefd = unrefd_handles.contains(handle);
312+
// if we are ref'ing a handle (by ptr) that was already unref'd,
313+
// probably
314+
if handle_already_unrefd {
315+
let last_idx = vec::len(unrefd_handles) - 1u;
316+
let handle_idx = vec::position_elem(unrefd_handles, handle);
317+
alt handle_idx {
318+
none {
319+
fail "trying to remove handle that isn't in unrefd_handles";
320+
}
321+
some(idx) {
322+
unrefd_handles[idx] <-> unrefd_handles[last_idx];
323+
vec::pop(unrefd_handles);
324+
}
325+
}
326+
(*data).unrefd_handles = unrefd_handles;
327+
}
293328
refd_handles += [handle];
294329
(*data).refd_handles = refd_handles;
295330
}
296331

297-
unsafe fn high_level_unref(data: *global_loop_data, handle: *libc::c_void,
298-
manual_unref: bool, user_close_cb: *u8) {
332+
unsafe fn high_level_unref(data: *hl_loop_data, handle: *libc::c_void,
333+
manual_unref: bool, user_close_cb: option<*u8>) {
299334
log(debug,"incoming hl_msg: got auto_unref_handle");
300335
let mut refd_handles = (*data).refd_handles;
301336
let mut unrefd_handles = (*data).unrefd_handles;
337+
log(debug, #fmt("refs: %?, unrefs %? handle %?", vec::len(refd_handles),
338+
vec::len(unrefd_handles), handle));
302339
let handle_already_refd = refd_handles.contains(handle);
303340
if !handle_already_refd {
304341
fail "attempting to high-level unref an untracked handle";
305342
}
306343
let double_unref = unrefd_handles.contains(handle);
307344
if double_unref {
345+
log(debug, "double unref encountered");
308346
if manual_unref {
309347
// will allow a user to manual unref, but only signal
310348
// a fail when a double-unref is caused by a user
311349
fail "attempting to high-level unref an unrefd handle";
312350
}
351+
else {
352+
log(debug, "not failing...");
353+
}
313354
}
314355
else {
315-
ll::close(handle, user_close_cb);
356+
log(debug, "attempting to unref handle");
357+
alt user_close_cb {
358+
some(cb) {
359+
ll::close(handle, cb);
360+
}
361+
none { }
362+
}
316363
let last_idx = vec::len(refd_handles) - 1u;
317364
let handle_idx = vec::position_elem(refd_handles, handle);
318365
alt handle_idx {
@@ -337,3 +384,128 @@ unsafe fn high_level_unref(data: *global_loop_data, handle: *libc::c_void,
337384
}
338385

339386
}
387+
#[cfg(test)]
388+
mod test {
389+
crust fn async_close_cb(handle: *ll::uv_async_t) unsafe {
390+
log(debug, #fmt("async_close_cb handle %?", handle));
391+
let exit_ch = (*(ll::get_data_for_uv_handle(handle)
392+
as *ah_data)).exit_ch;
393+
comm::send(exit_ch, ());
394+
}
395+
crust fn async_handle_cb(handle: *ll::uv_async_t, status: libc::c_int)
396+
unsafe {
397+
log(debug, #fmt("async_handle_cb handle %? status %?",handle,status));
398+
let hl_loop = (*(ll::get_data_for_uv_handle(handle)
399+
as *ah_data)).hl_loop;
400+
unref_and_close(hl_loop, handle, async_close_cb);
401+
}
402+
type ah_data = {
403+
hl_loop: high_level_loop,
404+
exit_ch: comm::chan<()>
405+
};
406+
fn impl_uv_hl_async(hl_loop: high_level_loop) unsafe {
407+
let async_handle = ll::async_t();
408+
let ah_ptr = ptr::addr_of(async_handle);
409+
let exit_po = comm::port::<()>();
410+
let exit_ch = comm::chan(exit_po);
411+
let ah_data = {
412+
hl_loop: hl_loop,
413+
exit_ch: exit_ch
414+
};
415+
let ah_data_ptr = ptr::addr_of(ah_data);
416+
interact(hl_loop) {|loop_ptr|
417+
ref(hl_loop, ah_ptr);
418+
ll::async_init(loop_ptr, ah_ptr, async_handle_cb);
419+
ll::set_data_for_uv_handle(ah_ptr, ah_data_ptr as *libc::c_void);
420+
ll::async_send(ah_ptr);
421+
};
422+
comm::recv(exit_po);
423+
}
424+
425+
// this fn documents the bear minimum neccesary to roll your own
426+
// high_level_loop
427+
unsafe fn spawn_test_loop(exit_ch: comm::chan<()>) -> high_level_loop {
428+
let hl_loop_port = comm::port::<high_level_loop>();
429+
let hl_loop_ch = comm::chan(hl_loop_port);
430+
task::spawn_sched(task::manual_threads(1u)) {||
431+
let loop_ptr = ll::loop_new();
432+
let msg_po = comm::port::<high_level_msg>();
433+
let msg_ch = comm::chan(msg_po);
434+
run_high_level_loop(
435+
loop_ptr,
436+
msg_po,
437+
// before_run
438+
{|async_handle|
439+
log(debug,#fmt("hltest before_run: async_handle %?",
440+
async_handle));
441+
// do an async_send with it
442+
ll::async_send(async_handle);
443+
comm::send(hl_loop_ch, simple_task_loop({
444+
async_handle: async_handle,
445+
op_chan: msg_ch
446+
}));
447+
},
448+
// before_msg_drain
449+
{|async_handle|
450+
log(debug,#fmt("hltest before_msg_drain: async_handle %?",
451+
async_handle));
452+
true
453+
},
454+
// before_tear_down
455+
{|async_handle|
456+
log(debug,#fmt("hl test_loop b4_tear_down: async %?",
457+
async_handle));
458+
});
459+
ll::loop_delete(loop_ptr);
460+
comm::send(exit_ch, ());
461+
};
462+
ret comm::recv(hl_loop_port);
463+
}
464+
465+
crust fn lifetime_handle_close(handle: *libc::c_void) unsafe {
466+
log(debug, #fmt("lifetime_handle_close ptr %?", handle));
467+
}
468+
469+
crust fn lifetime_async_callback(handle: *libc::c_void,
470+
status: libc::c_int) {
471+
log(debug, #fmt("lifetime_handle_close ptr %? status %?",
472+
handle, status));
473+
}
474+
475+
#[test]
476+
#[ignore(cfg(target_os = "freebsd"))]
477+
fn test_uv_hl_async() unsafe {
478+
let exit_po = comm::port::<()>();
479+
let exit_ch = comm::chan(exit_po);
480+
let hl_loop = spawn_test_loop(exit_ch);
481+
482+
// using this handle to manage the lifetime of the high_level_loop,
483+
// as it will exit the first time one of the impl_uv_hl_async() is
484+
// cleaned up with no one ref'd handles on the loop (Which can happen
485+
// under race-condition type situations.. this ensures that the loop
486+
// lives until, at least, all of the impl_uv_hl_async() runs have been
487+
// called, at least.
488+
let lifetime_handle = ll::async_t();
489+
let lifetime_handle_ptr = ptr::addr_of(lifetime_handle);
490+
interact(hl_loop) {|loop_ptr|
491+
ref(hl_loop, lifetime_handle_ptr);
492+
ll::async_init(loop_ptr, lifetime_handle_ptr,
493+
lifetime_async_callback);
494+
};
495+
496+
iter::repeat(7u) {||
497+
task::spawn_sched(task::manual_threads(1u), {||
498+
impl_uv_hl_async(hl_loop);
499+
});
500+
};
501+
impl_uv_hl_async(hl_loop);
502+
impl_uv_hl_async(hl_loop);
503+
impl_uv_hl_async(hl_loop);
504+
interact(hl_loop) {|loop_ptr|
505+
ll::close(lifetime_handle_ptr, lifetime_handle_close);
506+
unref(hl_loop, lifetime_handle_ptr);
507+
log(debug, "close and unref lifetime handle");
508+
};
509+
comm::recv(exit_po);
510+
}
511+
}

0 commit comments

Comments
 (0)