2012-04-02 13:03:45 -05:00
|
|
|
#[doc = "
|
|
|
|
High-level bindings to work with the libuv library.
|
|
|
|
|
|
|
|
This module is geared towards library developers who want to
|
|
|
|
provide a high-level, abstracted interface to some set of
|
|
|
|
libuv functionality.
|
|
|
|
"];
|
|
|
|
|
2012-04-27 23:42:04 -05:00
|
|
|
export high_level_loop, high_level_msg;
|
2012-05-24 23:38:48 -05:00
|
|
|
export run_high_level_loop, interact, exit;
|
2012-04-16 15:32:38 -05:00
|
|
|
|
2012-05-24 21:53:08 -05:00
|
|
|
import libc::c_void;
|
|
|
|
import ptr::addr_of;
|
|
|
|
import comm::{port, chan, methods};
|
2012-04-02 13:03:45 -05:00
|
|
|
import ll = uv_ll;
|
|
|
|
|
|
|
|
#[doc = "
|
|
|
|
Used to abstract-away direct interaction with a libuv loop.
|
|
|
|
"]
|
2012-05-24 21:53:08 -05:00
|
|
|
enum high_level_loop = {
|
|
|
|
async_handle: *ll::uv_async_t,
|
|
|
|
op_chan: chan<high_level_msg>
|
|
|
|
};
|
2012-04-19 00:43:11 -05:00
|
|
|
|
2012-04-17 14:05:04 -05:00
|
|
|
#[doc="
|
|
|
|
Represents the range of interactions with a `high_level_loop`
|
2012-04-16 15:32:38 -05:00
|
|
|
"]
|
2012-04-17 14:05:04 -05:00
|
|
|
enum high_level_msg {
|
|
|
|
interaction (fn~(*libc::c_void)),
|
2012-04-28 00:19:20 -05:00
|
|
|
#[doc="
|
|
|
|
For use in libraries that roll their own `high_level_loop` (like
|
|
|
|
`std::uv::global_loop`)
|
|
|
|
|
|
|
|
Is used to signal to the loop that it should close the internally-held
|
|
|
|
async handle and do a sanity check to make sure that all other handles are
|
|
|
|
closed, causing a failure otherwise. This should not be sent/used from
|
|
|
|
'normal' user code.
|
|
|
|
"]
|
2012-04-27 23:42:04 -05:00
|
|
|
teardown_loop
|
2012-04-16 15:32:38 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[doc = "
|
2012-04-28 00:19:20 -05:00
|
|
|
Useful for anyone who wants to roll their own `high_level_loop`.
|
2012-04-16 15:32:38 -05:00
|
|
|
|
|
|
|
# Arguments
|
|
|
|
|
|
|
|
* loop_ptr - a pointer to a currently unused libuv loop. Its `data` field
|
|
|
|
will be overwritten before the loop begins
|
2012-04-28 00:19:20 -05:00
|
|
|
* msg_po - an active port that receives `high_level_msg`s. You can distribute
|
|
|
|
a paired channel to users, along with the `async_handle` returned in the
|
|
|
|
following callback (combine them to make a `hl::simpler_task_loop` varient
|
|
|
|
of `hl::high_level_loop`)
|
|
|
|
* before_run - a unique closure that is invoked before `uv_run()` is called
|
|
|
|
on the provided `loop_ptr`. An `async_handle` is passed in which will be
|
|
|
|
live for the duration of the loop. You can distribute this to users so that
|
|
|
|
they can interact with the loop safely.
|
|
|
|
* before_msg_process - a unique closure that is invoked at least once when
|
|
|
|
the loop is woken up, and once more for every message that is drained from
|
|
|
|
the loop's msg port
|
2012-04-17 14:05:04 -05:00
|
|
|
* before_tear_down - called just before the loop invokes `uv_close()` on the
|
|
|
|
provided `async_handle`. `uv_run` should return shortly after
|
2012-04-16 15:32:38 -05:00
|
|
|
"]
|
2012-05-24 23:44:10 -05:00
|
|
|
unsafe fn run_high_level_loop(msg_po: port<high_level_msg>,
|
2012-05-24 23:48:40 -05:00
|
|
|
before_run: fn~(*ll::uv_async_t)) {
|
2012-05-24 23:44:10 -05:00
|
|
|
let loop_ptr = ll::loop_new();
|
2012-04-16 15:32:38 -05:00
|
|
|
// set up the special async handle we'll use to allow multi-task
|
|
|
|
// communication with this loop
|
|
|
|
let async = ll::async_t();
|
2012-05-24 21:53:08 -05:00
|
|
|
let async_handle = addr_of(async);
|
2012-04-16 15:32:38 -05:00
|
|
|
// associate the async handle with the loop
|
|
|
|
ll::async_init(loop_ptr, async_handle, high_level_wake_up_cb);
|
2012-04-16 17:29:07 -05:00
|
|
|
|
2012-04-16 15:32:38 -05:00
|
|
|
// initialize our loop data and store it in the loop
|
2012-05-24 21:53:08 -05:00
|
|
|
let data: hl_loop_data = {
|
2012-04-16 15:32:38 -05:00
|
|
|
async_handle: async_handle,
|
|
|
|
mut active: true,
|
2012-05-24 21:53:08 -05:00
|
|
|
msg_po_ptr: addr_of(msg_po)
|
|
|
|
};
|
|
|
|
ll::set_data_for_uv_handle(async_handle, addr_of(data));
|
2012-04-16 15:32:38 -05:00
|
|
|
|
|
|
|
// call before_run
|
2012-04-17 14:05:04 -05:00
|
|
|
before_run(async_handle);
|
2012-04-16 15:32:38 -05:00
|
|
|
|
|
|
|
log(debug, "about to run high level loop");
|
|
|
|
// enter the loop... this blocks until the loop is done..
|
|
|
|
ll::run(loop_ptr);
|
|
|
|
log(debug, "high-level loop ended");
|
2012-05-24 23:44:10 -05:00
|
|
|
ll::loop_delete(loop_ptr);
|
2012-04-16 15:32:38 -05:00
|
|
|
}
|
|
|
|
|
2012-04-02 13:03:45 -05:00
|
|
|
#[doc = "
|
2012-04-17 14:05:04 -05:00
|
|
|
Provide a callback to be processed by `a_loop`
|
|
|
|
|
|
|
|
The primary way to do operations again a running `high_level_loop` that
|
|
|
|
doesn't involve creating a uv handle via `safe_handle`
|
2012-04-02 13:03:45 -05:00
|
|
|
|
2012-04-28 00:19:20 -05:00
|
|
|
# Warning
|
|
|
|
|
|
|
|
This function is the only safe way to interact with _any_ `high_level_loop`.
|
|
|
|
Using functions in the `uv::ll` module outside of the `cb` passed into
|
|
|
|
this function is _very dangerous_.
|
|
|
|
|
2012-04-08 00:53:34 -05:00
|
|
|
# Arguments
|
2012-04-02 13:03:45 -05:00
|
|
|
|
2012-04-28 00:19:20 -05:00
|
|
|
* hl_loop - a `uv::hl::high_level_loop` that you want to do operations against
|
2012-04-02 13:03:45 -05:00
|
|
|
* cb - a function callback to be processed on the running loop's
|
2012-04-28 00:19:20 -05:00
|
|
|
thread. The only parameter passed in is an opaque pointer representing the
|
|
|
|
running `uv_loop_t*`. In the context of this callback, it is safe to use
|
|
|
|
this pointer to do various uv_* API calls contained within the `uv::ll`
|
|
|
|
module. It is not safe to send the `loop_ptr` param to this callback out
|
|
|
|
via ports/chans.
|
2012-04-02 13:03:45 -05:00
|
|
|
"]
|
2012-04-28 00:19:20 -05:00
|
|
|
unsafe fn interact(hl_loop: high_level_loop,
|
2012-05-24 21:53:08 -05:00
|
|
|
-cb: fn~(*c_void)) {
|
2012-04-28 00:19:20 -05:00
|
|
|
send_high_level_msg(hl_loop, interaction(cb));
|
2012-04-02 13:03:45 -05:00
|
|
|
}
|
|
|
|
|
2012-05-24 23:38:48 -05:00
|
|
|
fn exit(hl_loop: high_level_loop) unsafe {
|
|
|
|
send_high_level_msg(hl_loop, teardown_loop);
|
|
|
|
}
|
|
|
|
|
2012-04-16 15:32:38 -05:00
|
|
|
// INTERNAL API
|
2012-04-17 14:05:04 -05:00
|
|
|
|
|
|
|
// data that lives for the lifetime of the high-evel oo
|
2012-05-24 21:53:08 -05:00
|
|
|
type hl_loop_data = {
|
|
|
|
async_handle: *ll::uv_async_t,
|
|
|
|
mut active: bool,
|
|
|
|
msg_po_ptr: *port<high_level_msg>
|
|
|
|
};
|
2012-04-02 13:03:45 -05:00
|
|
|
|
2012-04-16 15:32:38 -05:00
|
|
|
unsafe fn send_high_level_msg(hl_loop: high_level_loop,
|
2012-04-27 23:42:04 -05:00
|
|
|
-msg: high_level_msg) {
|
2012-05-24 21:53:08 -05:00
|
|
|
comm::send(hl_loop.op_chan, msg);
|
|
|
|
ll::async_send(hl_loop.async_handle);
|
2012-04-02 13:03:45 -05:00
|
|
|
}
|
|
|
|
|
2012-04-16 15:32:38 -05:00
|
|
|
// this will be invoked by a call to uv::hl::interact() with
|
|
|
|
// the high_level_loop corresponding to this async_handle. We
|
|
|
|
// simply check if the loop is active and, if so, invoke the
|
|
|
|
// user-supplied on_wake callback that is stored in the loop's
|
|
|
|
// data member
|
2012-04-17 14:05:04 -05:00
|
|
|
crust fn high_level_wake_up_cb(async_handle: *ll::uv_async_t,
|
2012-04-16 15:32:38 -05:00
|
|
|
status: int) unsafe {
|
2012-04-17 14:05:04 -05:00
|
|
|
log(debug, #fmt("high_level_wake_up_cb crust.. handle: %? status: %?",
|
|
|
|
async_handle, status));
|
2012-04-16 15:32:38 -05:00
|
|
|
let loop_ptr = ll::get_loop_for_uv_handle(async_handle);
|
2012-04-19 17:36:07 -05:00
|
|
|
let data = ll::get_data_for_uv_handle(async_handle) as *hl_loop_data;
|
2012-05-24 18:42:05 -05:00
|
|
|
// FIXME: What is this checking?
|
2012-05-24 21:53:08 -05:00
|
|
|
if (*data).active {
|
2012-04-27 23:42:04 -05:00
|
|
|
let msg_po = *((*data).msg_po_ptr);
|
2012-05-24 21:53:08 -05:00
|
|
|
while msg_po.peek() {
|
|
|
|
let msg = msg_po.recv();
|
|
|
|
if (*data).active {
|
|
|
|
alt msg {
|
|
|
|
interaction(cb) {
|
|
|
|
cb(loop_ptr);
|
2012-04-27 23:42:04 -05:00
|
|
|
}
|
2012-05-24 21:53:08 -05:00
|
|
|
teardown_loop {
|
|
|
|
begin_teardown(data);
|
2012-04-27 23:42:04 -05:00
|
|
|
}
|
2012-04-16 15:32:38 -05:00
|
|
|
}
|
2012-05-24 21:53:08 -05:00
|
|
|
} else {
|
|
|
|
// FIXME: drop msg ?
|
2012-04-16 15:32:38 -05:00
|
|
|
}
|
|
|
|
}
|
2012-05-24 21:53:08 -05:00
|
|
|
} else {
|
2012-04-27 23:42:04 -05:00
|
|
|
// loop not active
|
2012-04-16 15:32:38 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
crust fn tear_down_close_cb(handle: *ll::uv_async_t) unsafe {
|
2012-04-27 23:42:04 -05:00
|
|
|
let loop_ptr = ll::get_loop_for_uv_handle(handle);
|
|
|
|
let loop_refs = ll::loop_refcount(loop_ptr);
|
|
|
|
log(debug, #fmt("tear_down_close_cb called, closing handle at %? refs %?",
|
|
|
|
handle, loop_refs));
|
|
|
|
assert loop_refs == 1i32;
|
2012-04-16 15:32:38 -05:00
|
|
|
}
|
|
|
|
|
2012-04-27 23:42:04 -05:00
|
|
|
fn begin_teardown(data: *hl_loop_data) unsafe {
|
2012-04-16 15:32:38 -05:00
|
|
|
log(debug, "high_level_tear_down() called, close async_handle");
|
|
|
|
// call user-suppled before_tear_down cb
|
|
|
|
let async_handle = (*data).async_handle;
|
2012-05-24 21:53:08 -05:00
|
|
|
ll::close(async_handle as *c_void, tear_down_close_cb);
|
2012-04-16 15:32:38 -05:00
|
|
|
}
|
|
|
|
|
2012-04-19 17:36:07 -05:00
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
|
|
|
crust fn async_close_cb(handle: *ll::uv_async_t) unsafe {
|
|
|
|
log(debug, #fmt("async_close_cb handle %?", handle));
|
|
|
|
let exit_ch = (*(ll::get_data_for_uv_handle(handle)
|
|
|
|
as *ah_data)).exit_ch;
|
|
|
|
comm::send(exit_ch, ());
|
|
|
|
}
|
|
|
|
crust fn async_handle_cb(handle: *ll::uv_async_t, status: libc::c_int)
|
|
|
|
unsafe {
|
|
|
|
log(debug, #fmt("async_handle_cb handle %? status %?",handle,status));
|
2012-04-27 23:42:04 -05:00
|
|
|
ll::close(handle, async_close_cb);
|
2012-04-19 17:36:07 -05:00
|
|
|
}
|
|
|
|
type ah_data = {
|
|
|
|
hl_loop: high_level_loop,
|
|
|
|
exit_ch: comm::chan<()>
|
|
|
|
};
|
|
|
|
fn impl_uv_hl_async(hl_loop: high_level_loop) unsafe {
|
|
|
|
let async_handle = ll::async_t();
|
|
|
|
let ah_ptr = ptr::addr_of(async_handle);
|
|
|
|
let exit_po = comm::port::<()>();
|
|
|
|
let exit_ch = comm::chan(exit_po);
|
|
|
|
let ah_data = {
|
|
|
|
hl_loop: hl_loop,
|
|
|
|
exit_ch: exit_ch
|
|
|
|
};
|
|
|
|
let ah_data_ptr = ptr::addr_of(ah_data);
|
|
|
|
interact(hl_loop) {|loop_ptr|
|
|
|
|
ll::async_init(loop_ptr, ah_ptr, async_handle_cb);
|
|
|
|
ll::set_data_for_uv_handle(ah_ptr, ah_data_ptr as *libc::c_void);
|
|
|
|
ll::async_send(ah_ptr);
|
|
|
|
};
|
|
|
|
comm::recv(exit_po);
|
|
|
|
}
|
|
|
|
|
|
|
|
// this fn documents the bear minimum neccesary to roll your own
|
|
|
|
// high_level_loop
|
|
|
|
unsafe fn spawn_test_loop(exit_ch: comm::chan<()>) -> high_level_loop {
|
|
|
|
let hl_loop_port = comm::port::<high_level_loop>();
|
|
|
|
let hl_loop_ch = comm::chan(hl_loop_port);
|
|
|
|
task::spawn_sched(task::manual_threads(1u)) {||
|
|
|
|
let msg_po = comm::port::<high_level_msg>();
|
|
|
|
let msg_ch = comm::chan(msg_po);
|
2012-05-24 23:48:40 -05:00
|
|
|
run_high_level_loop(msg_po) {|async_handle|
|
|
|
|
log(debug,#fmt("hltest before_run: async_handle %?",
|
|
|
|
async_handle));
|
|
|
|
// do an async_send with it
|
|
|
|
ll::async_send(async_handle);
|
|
|
|
comm::send(hl_loop_ch, high_level_loop({
|
|
|
|
async_handle: async_handle,
|
|
|
|
op_chan: msg_ch
|
|
|
|
}));
|
|
|
|
}
|
2012-04-19 17:36:07 -05:00
|
|
|
comm::send(exit_ch, ());
|
|
|
|
};
|
|
|
|
ret comm::recv(hl_loop_port);
|
|
|
|
}
|
|
|
|
|
|
|
|
crust fn lifetime_handle_close(handle: *libc::c_void) unsafe {
|
|
|
|
log(debug, #fmt("lifetime_handle_close ptr %?", handle));
|
|
|
|
}
|
|
|
|
|
|
|
|
crust fn lifetime_async_callback(handle: *libc::c_void,
|
|
|
|
status: libc::c_int) {
|
|
|
|
log(debug, #fmt("lifetime_handle_close ptr %? status %?",
|
|
|
|
handle, status));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_uv_hl_async() unsafe {
|
|
|
|
let exit_po = comm::port::<()>();
|
|
|
|
let exit_ch = comm::chan(exit_po);
|
|
|
|
let hl_loop = spawn_test_loop(exit_ch);
|
|
|
|
|
|
|
|
// using this handle to manage the lifetime of the high_level_loop,
|
|
|
|
// as it will exit the first time one of the impl_uv_hl_async() is
|
|
|
|
// cleaned up with no one ref'd handles on the loop (Which can happen
|
|
|
|
// under race-condition type situations.. this ensures that the loop
|
|
|
|
// lives until, at least, all of the impl_uv_hl_async() runs have been
|
|
|
|
// called, at least.
|
2012-04-27 23:42:04 -05:00
|
|
|
let work_exit_po = comm::port::<()>();
|
|
|
|
let work_exit_ch = comm::chan(work_exit_po);
|
2012-04-19 17:36:07 -05:00
|
|
|
iter::repeat(7u) {||
|
|
|
|
task::spawn_sched(task::manual_threads(1u), {||
|
|
|
|
impl_uv_hl_async(hl_loop);
|
2012-04-27 23:42:04 -05:00
|
|
|
comm::send(work_exit_ch, ());
|
2012-04-19 17:36:07 -05:00
|
|
|
});
|
|
|
|
};
|
2012-04-27 23:42:04 -05:00
|
|
|
iter::repeat(7u) {||
|
|
|
|
comm::recv(work_exit_po);
|
2012-04-19 17:36:07 -05:00
|
|
|
};
|
2012-04-27 23:42:04 -05:00
|
|
|
log(debug, "sending teardown_loop msg..");
|
|
|
|
// the teardown msg usually comes, in the case of the global loop,
|
|
|
|
// as a result of receiving a msg on the weaken_task port. but,
|
|
|
|
// anyone rolling their own high_level_loop can decide when to
|
|
|
|
// send the msg. it's assert and barf, though, if all of your
|
|
|
|
// handles aren't uv_close'd first
|
2012-05-24 21:53:08 -05:00
|
|
|
comm::send(hl_loop.op_chan, teardown_loop);
|
|
|
|
ll::async_send(hl_loop.async_handle);
|
2012-04-19 17:36:07 -05:00
|
|
|
comm::recv(exit_po);
|
2012-04-27 23:42:04 -05:00
|
|
|
log(debug, "after recv on exit_po.. exiting..");
|
2012-04-19 17:36:07 -05:00
|
|
|
}
|
|
|
|
}
|