From 7ac8c3081c5d4d7b1c79942e426770c1c3e1c0b3 Mon Sep 17 00:00:00 2001 From: Jeff Olson Date: Fri, 20 Apr 2012 05:00:14 -0700 Subject: [PATCH] std: add std::timer and timer::delayed_send and timer::sleep .. leveraging std::uv, we have: timer::delayed_send - send a value over a provided channel after the timeout has passed timer::sleep - block the current task for the specified period both of these fns (and everything that goes in timer.rs) leverage the uv_timer_* API --- src/libstd/std.rc | 3 +- src/libstd/timer.rs | 113 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 src/libstd/timer.rs diff --git a/src/libstd/std.rc b/src/libstd/std.rc index cd9dfdc7fd7..d1b578f3d87 100644 --- a/src/libstd/std.rc +++ b/src/libstd/std.rc @@ -14,7 +14,7 @@ use core(vers = "0.2"); import core::*; export net, uv; -export c_vec, util; +export c_vec, util, timer; export bitv, deque, fun_treemap, list, map, smallintmap, sort, treemap, ufind; export rope, arena; export ebml, dbg, getopts, json, rand, sha1, term, time, prettyprint; @@ -35,6 +35,7 @@ mod uv_global_loop; mod c_vec; mod util; +mod timer; // Collections diff --git a/src/libstd/timer.rs b/src/libstd/timer.rs new file mode 100644 index 00000000000..c8243ae8cf1 --- /dev/null +++ b/src/libstd/timer.rs @@ -0,0 +1,113 @@ +#[doc =" +Utilities that leverage libuv's `uv_timer_*` API +"]; + +import uv = uv; +export delayed_send, sleep; + +#[doc = " +Wait for timeout period then send provided value over a channel + +This call returns immediately. Useful as the building block for a number +of higher-level timer functions. + +Is not guaranteed to wait for exactly the specified time, but will wait +for *at least* that period of time. + +# Arguments + +msecs - a timeout period, in milliseconds, to wait +ch - a channel of type T to send a `val` on +val - a value of type T to send over the provided `ch` +"] +fn delayed_send(msecs: uint, ch: comm::chan, val: T) { + task::spawn() {|| + unsafe { + let timer_done_po = comm::port::<()>(); + let timer_done_ch = comm::chan(timer_done_po); + let timer_done_ch_ptr = ptr::addr_of(timer_done_ch); + let timer = uv::ll::timer_t(); + let timer_ptr = ptr::addr_of(timer); + let hl_loop = uv::global_loop::get(); + uv::hl::interact(hl_loop) {|loop_ptr| + uv::hl::ref(hl_loop, timer_ptr); + let init_result = uv::ll::timer_init(loop_ptr, timer_ptr); + if (init_result == 0i32) { + let start_result = uv::ll::timer_start( + timer_ptr, delayed_send_cb, msecs, 0u); + if (start_result == 0i32) { + uv::ll::set_data_for_uv_handle( + timer_ptr, + timer_done_ch_ptr as *libc::c_void); + } + else { + let error_msg = uv::ll::get_last_err_info(loop_ptr); + fail "timer::delayed_send() start failed: "+error_msg; + } + } + else { + let error_msg = uv::ll::get_last_err_info(loop_ptr); + fail "timer::delayed_send() init failed: "+error_msg; + } + }; + // delayed_send_cb has been processed by libuv + comm::recv(timer_done_po); + // notify the caller immediately + comm::send(ch, copy(val)); + // then clean up our handle + uv::hl::unref_and_close(hl_loop, timer_ptr, + delayed_send_close_cb); + // uv_close for this timer has been processed + comm::recv(timer_done_po); + } + }; +} + +#[doc = " +Blocks the current task for (at least) the specified time period. + +Is not guaranteed to sleep for exactly the specified time, but will sleep +for *at least* that period of time. + +# Arguments + +* msecs - an amount of time, in milliseconds, for the current task to block +"] +fn sleep(msecs: uint) { + let exit_po = comm::port::<()>(); + let exit_ch = comm::chan(exit_po); + delayed_send(msecs, exit_ch, ()); + comm::recv(exit_po); +} + +// INTERNAL API +crust fn delayed_send_cb(handle: *uv::ll::uv_timer_t, + status: libc::c_int) unsafe { + log(debug, #fmt("delayed_send_cb handle %? status %?", handle, status)); + let timer_done_ch = + *(uv::ll::get_data_for_uv_handle(handle) as *comm::chan<()>); + let stop_result = uv::ll::timer_stop(handle); + if (stop_result == 0i32) { + comm::send(timer_done_ch, ()); + } + else { + let loop_ptr = uv::ll::get_loop_for_uv_handle(handle); + let error_msg = uv::ll::get_last_err_info(loop_ptr); + fail "timer::sleep() init failed: "+error_msg; + } +} + +crust fn delayed_send_close_cb(handle: *uv::ll::uv_timer_t) unsafe { + log(debug, #fmt("delayed_send_close_cb handle %?", handle)); + let timer_done_ch = + *(uv::ll::get_data_for_uv_handle(handle) as *comm::chan<()>); + comm::send(timer_done_ch, ()); +} + +#[cfg(test)] +mod test { + #[test] + fn test_timer_simple_sleep_test() { + sleep(2000u); + } +}