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-19 00:43:11 -05:00
|
|
|
export high_level_loop, hl_loop_ext, high_level_msg;
|
2012-04-19 17:36:07 -05:00
|
|
|
export run_high_level_loop, interact, ref, unref, unref_and_close;
|
2012-04-16 15:32:38 -05:00
|
|
|
|
2012-04-02 13:03:45 -05:00
|
|
|
import ll = uv_ll;
|
|
|
|
|
|
|
|
#[doc = "
|
|
|
|
Used to abstract-away direct interaction with a libuv loop.
|
|
|
|
|
2012-04-08 00:53:34 -05:00
|
|
|
# Arguments
|
2012-04-02 13:03:45 -05:00
|
|
|
|
2012-04-16 15:32:38 -05:00
|
|
|
* async_handle - a pointer to a pointer to a uv_async_t struct used to 'poke'
|
2012-04-02 13:03:45 -05:00
|
|
|
the C uv loop to process any pending callbacks
|
|
|
|
|
|
|
|
* op_chan - a channel used to send function callbacks to be processed
|
|
|
|
by the C uv loop
|
|
|
|
"]
|
2012-04-19 00:43:11 -05:00
|
|
|
enum high_level_loop {
|
2012-04-19 17:36:07 -05:00
|
|
|
simple_task_loop({
|
|
|
|
async_handle: *ll::uv_async_t,
|
|
|
|
op_chan: comm::chan<high_level_msg>
|
|
|
|
}),
|
2012-04-19 00:43:11 -05:00
|
|
|
single_task_loop({
|
|
|
|
async_handle: **ll::uv_async_t,
|
|
|
|
op_chan: comm::chan<high_level_msg>
|
|
|
|
}),
|
|
|
|
monitor_task_loop({
|
|
|
|
op_chan: comm::chan<high_level_msg>
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
impl hl_loop_ext for high_level_loop {
|
|
|
|
fn async_handle() -> **ll::uv_async_t {
|
|
|
|
alt self {
|
|
|
|
single_task_loop({async_handle, op_chan}) {
|
|
|
|
ret async_handle;
|
|
|
|
}
|
|
|
|
_ {
|
|
|
|
fail "variant of hl::high_level_loop that doesn't include" +
|
|
|
|
"an async_handle field";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fn op_chan() -> comm::chan<high_level_msg> {
|
|
|
|
alt self {
|
|
|
|
single_task_loop({async_handle, op_chan}) {
|
|
|
|
ret op_chan;
|
|
|
|
}
|
|
|
|
monitor_task_loop({op_chan}) {
|
|
|
|
ret op_chan;
|
|
|
|
}
|
2012-04-19 17:36:07 -05:00
|
|
|
simple_task_loop({async_handle, op_chan}) {
|
|
|
|
ret op_chan;
|
|
|
|
}
|
2012-04-19 00:43:11 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-04-02 13:03:45 -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-19 17:36:07 -05:00
|
|
|
ref_handle (*libc::c_void),
|
|
|
|
manual_unref_handle (*libc::c_void, option<*u8>),
|
2012-04-17 14:05:04 -05:00
|
|
|
tear_down
|
2012-04-16 15:32:38 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[doc = "
|
2012-04-17 14:05:04 -05:00
|
|
|
Given a vanilla `uv_loop_t*`
|
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
|
|
|
|
must be a pointer to a clean rust `uv_async_t` record
|
2012-04-17 14:05:04 -05:00
|
|
|
* msg_po - an active port that receives `high_level_msg`s
|
|
|
|
* before_run - a unique closure that is invoked after `uv_async_init` is
|
|
|
|
called on the `async_handle` passed into this callback, just before `uv_run`
|
|
|
|
is called on the provided `loop_ptr`
|
2012-04-16 15:32:38 -05:00
|
|
|
* before_msg_drain - a unique closure that is invoked every time the loop is
|
2012-04-17 14:05:04 -05:00
|
|
|
awoken, but before the port pointed to in the `msg_po` argument is drained
|
|
|
|
* 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
|
|
|
"]
|
|
|
|
unsafe fn run_high_level_loop(loop_ptr: *libc::c_void,
|
|
|
|
msg_po: comm::port<high_level_msg>,
|
2012-04-17 14:05:04 -05:00
|
|
|
before_run: fn~(*ll::uv_async_t),
|
|
|
|
before_msg_drain: fn~(*ll::uv_async_t) -> bool,
|
|
|
|
before_tear_down: fn~(*ll::uv_async_t)) {
|
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();
|
|
|
|
let async_handle = ptr::addr_of(async);
|
|
|
|
// 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-04-19 17:36:07 -05:00
|
|
|
let data: hl_loop_data = default_gl_data({
|
2012-04-16 15:32:38 -05:00
|
|
|
async_handle: async_handle,
|
|
|
|
mut active: true,
|
|
|
|
before_msg_drain: before_msg_drain,
|
2012-04-17 14:05:04 -05:00
|
|
|
before_tear_down: before_tear_down,
|
2012-04-16 15:32:38 -05:00
|
|
|
msg_po_ptr: ptr::addr_of(msg_po),
|
|
|
|
mut refd_handles: [mut],
|
|
|
|
mut unrefd_handles: [mut]
|
2012-04-17 14:05:04 -05:00
|
|
|
});
|
2012-04-16 15:32:38 -05:00
|
|
|
let data_ptr = ptr::addr_of(data);
|
|
|
|
ll::set_data_for_uv_handle(async_handle, data_ptr);
|
|
|
|
|
|
|
|
// 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-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-08 00:53:34 -05:00
|
|
|
# Arguments
|
2012-04-02 13:03:45 -05:00
|
|
|
|
2012-04-17 14:05:04 -05:00
|
|
|
* a_loop - a `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
|
|
|
|
thread. The only parameter is an opaque pointer to the running
|
2012-04-17 14:05:04 -05:00
|
|
|
uv_loop_t. In the context of this callback, it is safe to use this pointer
|
|
|
|
to do various uv_* API calls. _DO NOT_ send this pointer out via ports/chans
|
2012-04-02 13:03:45 -05:00
|
|
|
"]
|
|
|
|
unsafe fn interact(a_loop: high_level_loop,
|
|
|
|
-cb: fn~(*libc::c_void)) {
|
2012-04-16 15:32:38 -05:00
|
|
|
send_high_level_msg(a_loop, interaction(cb));
|
2012-04-02 13:03:45 -05:00
|
|
|
}
|
|
|
|
|
2012-04-16 15:32:38 -05:00
|
|
|
iface uv_handle_manager<T> {
|
|
|
|
fn init() -> T;
|
|
|
|
}
|
2012-04-02 13:03:45 -05:00
|
|
|
|
2012-04-17 14:05:04 -05:00
|
|
|
type safe_handle_fields<T> = {
|
|
|
|
hl_loop: high_level_loop,
|
|
|
|
handle: T,
|
|
|
|
close_cb: *u8
|
|
|
|
};
|
|
|
|
|
|
|
|
/*fn safe_handle<T>(a_loop: high_level_loop,
|
|
|
|
handle_val: T,
|
|
|
|
handle_init_cb: fn~(*libc::c_void, *T),
|
|
|
|
close_cb: *u8) {
|
|
|
|
|
|
|
|
resource safe_handle_container<T>(handle_fields: safe_handle_fields<T>) {
|
2012-04-16 15:32:38 -05:00
|
|
|
}
|
2012-04-17 14:05:04 -05:00
|
|
|
}*/
|
|
|
|
|
2012-04-02 13:03:45 -05:00
|
|
|
|
2012-04-16 15:32:38 -05:00
|
|
|
#[doc="
|
2012-04-17 14:05:04 -05:00
|
|
|
Needs to be encapsulated within `safe_handle`
|
2012-04-16 15:32:38 -05:00
|
|
|
"]
|
2012-04-19 17:36:07 -05:00
|
|
|
fn ref<T>(hl_loop: high_level_loop, handle: *T) unsafe {
|
|
|
|
send_high_level_msg(hl_loop, ref_handle(handle as *libc::c_void));
|
2012-04-16 15:32:38 -05:00
|
|
|
}
|
|
|
|
#[doc="
|
2012-04-17 14:05:04 -05:00
|
|
|
Needs to be encapsulated within `safe_handle`
|
2012-04-16 15:32:38 -05:00
|
|
|
"]
|
2012-04-19 17:36:07 -05:00
|
|
|
fn unref<T>(hl_loop: high_level_loop, handle: *T) unsafe {
|
|
|
|
send_high_level_msg(hl_loop, manual_unref_handle(handle as *libc::c_void,
|
|
|
|
none));
|
|
|
|
}
|
|
|
|
fn unref_and_close<T>(hl_loop: high_level_loop, handle: *T, cb: *u8) unsafe {
|
|
|
|
send_high_level_msg(hl_loop, manual_unref_handle(handle as *libc::c_void,
|
|
|
|
some(cb)));
|
2012-04-16 15:32:38 -05:00
|
|
|
}
|
2012-04-02 13:03:45 -05:00
|
|
|
|
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-04-19 17:36:07 -05:00
|
|
|
enum hl_loop_data {
|
2012-04-17 14:05:04 -05:00
|
|
|
default_gl_data({
|
|
|
|
async_handle: *ll::uv_async_t,
|
|
|
|
mut active: bool,
|
|
|
|
before_msg_drain: fn~(*ll::uv_async_t) -> bool,
|
|
|
|
before_tear_down: fn~(*ll::uv_async_t),
|
|
|
|
msg_po_ptr: *comm::port<high_level_msg>,
|
|
|
|
mut refd_handles: [mut *libc::c_void],
|
|
|
|
mut unrefd_handles: [mut *libc::c_void]})
|
|
|
|
}
|
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,
|
|
|
|
-msg: high_level_msg) unsafe {
|
2012-04-19 00:43:11 -05:00
|
|
|
comm::send(hl_loop.op_chan(), msg);
|
2012-04-03 11:25:13 -05:00
|
|
|
|
2012-04-16 15:32:38 -05:00
|
|
|
// if the global async handle == 0, then that means
|
|
|
|
// the loop isn't active, so we don't need to wake it up,
|
|
|
|
// (the loop's enclosing task should be blocking on a message
|
|
|
|
// receive on this port)
|
2012-04-19 01:49:20 -05:00
|
|
|
alt hl_loop {
|
|
|
|
single_task_loop({async_handle, op_chan}) {
|
|
|
|
if ((*async_handle) != 0 as *ll::uv_async_t) {
|
|
|
|
log(debug,"global async handle != 0, waking up loop..");
|
|
|
|
ll::async_send((*async_handle));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
log(debug,"GLOBAL ASYNC handle == 0");
|
|
|
|
}
|
|
|
|
}
|
2012-04-19 17:36:07 -05:00
|
|
|
simple_task_loop({async_handle, op_chan}) {
|
|
|
|
log(debug,"simple async handle != 0, waking up loop..");
|
|
|
|
ll::async_send((async_handle));
|
|
|
|
}
|
2012-04-19 01:49:20 -05:00
|
|
|
_ {}
|
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-02 13:03:45 -05:00
|
|
|
// nothing here, yet.
|
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-04-16 15:32:38 -05:00
|
|
|
// we check to see if the loop is "active" (the loop is set to
|
|
|
|
// active = false the first time we realize we need to 'tear down',
|
|
|
|
// set subsequent calls to the global async handle may be triggered
|
|
|
|
// before all of the uv_close() calls are processed and loop exits
|
|
|
|
// on its own. So if the loop isn't active, we won't run the user's
|
|
|
|
// on_wake callback (and, consequently, let messages pile up, probably
|
|
|
|
// in the loops msg_po)
|
|
|
|
if (*data).active {
|
|
|
|
log(debug, "before on_wake");
|
2012-04-17 14:05:04 -05:00
|
|
|
let mut do_msg_drain = (*data).before_msg_drain(async_handle);
|
2012-04-16 15:32:38 -05:00
|
|
|
let mut continue = true;
|
|
|
|
if do_msg_drain {
|
|
|
|
let msg_po = *((*data).msg_po_ptr);
|
|
|
|
if comm::peek(msg_po) {
|
|
|
|
// if this is true, we'll iterate over the
|
|
|
|
// msgs waiting in msg_po until there's no more
|
|
|
|
log(debug,"got msg_po");
|
|
|
|
while(continue) {
|
|
|
|
log(debug,"before alt'ing on high_level_msg");
|
|
|
|
alt comm::recv(msg_po) {
|
|
|
|
interaction(cb) {
|
|
|
|
log(debug,"got interaction, before cb..");
|
|
|
|
// call it..
|
|
|
|
cb(loop_ptr);
|
|
|
|
log(debug,"after calling cb");
|
|
|
|
}
|
2012-04-19 17:36:07 -05:00
|
|
|
ref_handle(handle) {
|
2012-04-16 15:32:38 -05:00
|
|
|
high_level_ref(data, handle);
|
|
|
|
}
|
2012-04-19 17:36:07 -05:00
|
|
|
manual_unref_handle(handle, user_close_cb) {
|
|
|
|
high_level_unref(data, handle, true, user_close_cb);
|
2012-04-16 15:32:38 -05:00
|
|
|
}
|
|
|
|
tear_down {
|
|
|
|
log(debug,"incoming hl_msg: got tear_down");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
continue = comm::peek(msg_po);
|
|
|
|
}
|
|
|
|
}
|
2012-04-19 17:36:07 -05:00
|
|
|
else {
|
|
|
|
log(debug, "in hl wake_cb, no pending messages");
|
|
|
|
}
|
2012-04-16 15:32:38 -05:00
|
|
|
}
|
|
|
|
log(debug, #fmt("after on_wake, continue? %?", continue));
|
|
|
|
if !do_msg_drain {
|
|
|
|
high_level_tear_down(data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
crust fn tear_down_close_cb(handle: *ll::uv_async_t) unsafe {
|
|
|
|
log(debug, #fmt("tear_down_close_cb called, closing handle at %?",
|
|
|
|
handle));
|
2012-04-19 17:36:07 -05:00
|
|
|
let data = ll::get_data_for_uv_handle(handle) as *hl_loop_data;
|
|
|
|
if vec::len((*data).refd_handles) > 0u {
|
2012-04-19 07:59:37 -05:00
|
|
|
fail "Didn't unref all high-level handles";
|
|
|
|
}
|
2012-04-16 15:32:38 -05:00
|
|
|
}
|
|
|
|
|
2012-04-19 17:36:07 -05:00
|
|
|
fn high_level_tear_down(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-04-17 14:05:04 -05:00
|
|
|
(*data).before_tear_down(async_handle);
|
2012-04-16 15:32:38 -05:00
|
|
|
ll::close(async_handle as *libc::c_void, tear_down_close_cb);
|
|
|
|
}
|
|
|
|
|
2012-04-19 17:36:07 -05:00
|
|
|
unsafe fn high_level_ref(data: *hl_loop_data, handle: *libc::c_void) {
|
|
|
|
log(debug,"incoming hl_msg: got ..ref_handle");
|
2012-04-16 15:32:38 -05:00
|
|
|
let mut refd_handles = (*data).refd_handles;
|
2012-04-19 17:36:07 -05:00
|
|
|
let mut unrefd_handles = (*data).unrefd_handles;
|
2012-04-16 15:32:38 -05:00
|
|
|
let handle_already_refd = refd_handles.contains(handle);
|
|
|
|
if handle_already_refd {
|
|
|
|
fail "attempt to do a high-level ref an already ref'd handle";
|
|
|
|
}
|
2012-04-19 17:36:07 -05:00
|
|
|
let handle_already_unrefd = unrefd_handles.contains(handle);
|
|
|
|
// if we are ref'ing a handle (by ptr) that was already unref'd,
|
|
|
|
// probably
|
|
|
|
if handle_already_unrefd {
|
|
|
|
let last_idx = vec::len(unrefd_handles) - 1u;
|
|
|
|
let handle_idx = vec::position_elem(unrefd_handles, handle);
|
|
|
|
alt handle_idx {
|
|
|
|
none {
|
|
|
|
fail "trying to remove handle that isn't in unrefd_handles";
|
|
|
|
}
|
|
|
|
some(idx) {
|
|
|
|
unrefd_handles[idx] <-> unrefd_handles[last_idx];
|
|
|
|
vec::pop(unrefd_handles);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
(*data).unrefd_handles = unrefd_handles;
|
|
|
|
}
|
2012-04-16 15:32:38 -05:00
|
|
|
refd_handles += [handle];
|
|
|
|
(*data).refd_handles = refd_handles;
|
|
|
|
}
|
|
|
|
|
2012-04-19 17:36:07 -05:00
|
|
|
unsafe fn high_level_unref(data: *hl_loop_data, handle: *libc::c_void,
|
|
|
|
manual_unref: bool, user_close_cb: option<*u8>) {
|
2012-04-16 15:32:38 -05:00
|
|
|
log(debug,"incoming hl_msg: got auto_unref_handle");
|
|
|
|
let mut refd_handles = (*data).refd_handles;
|
|
|
|
let mut unrefd_handles = (*data).unrefd_handles;
|
2012-04-19 17:36:07 -05:00
|
|
|
log(debug, #fmt("refs: %?, unrefs %? handle %?", vec::len(refd_handles),
|
|
|
|
vec::len(unrefd_handles), handle));
|
2012-04-16 15:32:38 -05:00
|
|
|
let handle_already_refd = refd_handles.contains(handle);
|
|
|
|
if !handle_already_refd {
|
|
|
|
fail "attempting to high-level unref an untracked handle";
|
|
|
|
}
|
|
|
|
let double_unref = unrefd_handles.contains(handle);
|
|
|
|
if double_unref {
|
2012-04-19 17:36:07 -05:00
|
|
|
log(debug, "double unref encountered");
|
2012-04-16 15:32:38 -05:00
|
|
|
if manual_unref {
|
|
|
|
// will allow a user to manual unref, but only signal
|
|
|
|
// a fail when a double-unref is caused by a user
|
|
|
|
fail "attempting to high-level unref an unrefd handle";
|
|
|
|
}
|
2012-04-19 17:36:07 -05:00
|
|
|
else {
|
|
|
|
log(debug, "not failing...");
|
|
|
|
}
|
2012-04-16 15:32:38 -05:00
|
|
|
}
|
|
|
|
else {
|
2012-04-19 17:36:07 -05:00
|
|
|
log(debug, "attempting to unref handle");
|
|
|
|
alt user_close_cb {
|
|
|
|
some(cb) {
|
|
|
|
ll::close(handle, cb);
|
|
|
|
}
|
|
|
|
none { }
|
|
|
|
}
|
2012-04-16 15:32:38 -05:00
|
|
|
let last_idx = vec::len(refd_handles) - 1u;
|
|
|
|
let handle_idx = vec::position_elem(refd_handles, handle);
|
|
|
|
alt handle_idx {
|
|
|
|
none {
|
|
|
|
fail "trying to remove handle that isn't in refd_handles";
|
|
|
|
}
|
|
|
|
some(idx) {
|
|
|
|
refd_handles[idx] <-> refd_handles[last_idx];
|
|
|
|
vec::pop(refd_handles);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
(*data).refd_handles = refd_handles;
|
|
|
|
unrefd_handles += [handle];
|
|
|
|
(*data).unrefd_handles = unrefd_handles;
|
|
|
|
if vec::len(refd_handles) == 0u {
|
|
|
|
log(debug, "0 referenced handles, start loop teardown");
|
|
|
|
high_level_tear_down(data);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
log(debug, "more than 0 referenced handles");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
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));
|
|
|
|
let hl_loop = (*(ll::get_data_for_uv_handle(handle)
|
|
|
|
as *ah_data)).hl_loop;
|
|
|
|
unref_and_close(hl_loop, handle, async_close_cb);
|
|
|
|
}
|
|
|
|
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|
|
|
|
|
ref(hl_loop, ah_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 loop_ptr = ll::loop_new();
|
|
|
|
let msg_po = comm::port::<high_level_msg>();
|
|
|
|
let msg_ch = comm::chan(msg_po);
|
|
|
|
run_high_level_loop(
|
|
|
|
loop_ptr,
|
|
|
|
msg_po,
|
|
|
|
// before_run
|
|
|
|
{|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, simple_task_loop({
|
|
|
|
async_handle: async_handle,
|
|
|
|
op_chan: msg_ch
|
|
|
|
}));
|
|
|
|
},
|
|
|
|
// before_msg_drain
|
|
|
|
{|async_handle|
|
|
|
|
log(debug,#fmt("hltest before_msg_drain: async_handle %?",
|
|
|
|
async_handle));
|
|
|
|
true
|
|
|
|
},
|
|
|
|
// before_tear_down
|
|
|
|
{|async_handle|
|
|
|
|
log(debug,#fmt("hl test_loop b4_tear_down: async %?",
|
|
|
|
async_handle));
|
|
|
|
});
|
|
|
|
ll::loop_delete(loop_ptr);
|
|
|
|
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]
|
2012-04-20 18:46:50 -05:00
|
|
|
#[ignore]
|
2012-04-19 17:36:07 -05:00
|
|
|
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.
|
|
|
|
let lifetime_handle = ll::async_t();
|
|
|
|
let lifetime_handle_ptr = ptr::addr_of(lifetime_handle);
|
|
|
|
interact(hl_loop) {|loop_ptr|
|
|
|
|
ref(hl_loop, lifetime_handle_ptr);
|
|
|
|
ll::async_init(loop_ptr, lifetime_handle_ptr,
|
|
|
|
lifetime_async_callback);
|
|
|
|
};
|
|
|
|
|
|
|
|
iter::repeat(7u) {||
|
|
|
|
task::spawn_sched(task::manual_threads(1u), {||
|
|
|
|
impl_uv_hl_async(hl_loop);
|
|
|
|
});
|
|
|
|
};
|
|
|
|
impl_uv_hl_async(hl_loop);
|
|
|
|
impl_uv_hl_async(hl_loop);
|
|
|
|
impl_uv_hl_async(hl_loop);
|
|
|
|
interact(hl_loop) {|loop_ptr|
|
|
|
|
ll::close(lifetime_handle_ptr, lifetime_handle_close);
|
|
|
|
unref(hl_loop, lifetime_handle_ptr);
|
|
|
|
log(debug, "close and unref lifetime handle");
|
|
|
|
};
|
|
|
|
comm::recv(exit_po);
|
|
|
|
}
|
|
|
|
}
|