Hey!
We now have a long-desired mechanism for calling from C back into Rust
functions. This should make writing bindings for certain libraries much easier.
The short version is that we have a new type of function declaration, a 'crust'
function ("C-to-Rust" - cute, or obnoxious?) that follows the C ABI and can be
used as a callback from C functions. It looks like this
crust fn cb(data: ctypes::uintptr_t) -> ctypes::uintptr_t {
... do whatever rusty stuff you like ...
}
These are special in several ways
* They cannot be called from Rust code
* Their value can be taken, but they always have type *u8
* A task in a crust callback that fails results in the runtime aborting
abrubtly
* Tasks cannot be killed while in a crust callback
Their use should generally be encapsulated into libraries that can deal with
their special nature. My general suggestion for using them successfully is:
* Use them primarily to dispatch messages to other tasks to avoid
catastrophic failure
* If you are using them from an event loop then first put yourself into a
new, single-threaded scheduler using spawn_sched to avoid blocking other tasks
Additionally, the inability to kill tasks running in callbacks places an
additional large burden on those using callbacks to handle long-running event
loops (like libuv) - if the runtime is failing you have to ensure that your
callbacks can detect this situation and exit cleanly. Otherwise your task will
keep the runtime alive after all other tasks have died.
My current suggestion is to have a monitor task (on a different scheduler) that
detects failure and notifies the event loop callbacks via a message that it
needs to terminate (using the non-blocking comm::peek function to look into the
message queue). With libuv we could also inject a cleanup function into the
event loop with the async_t handle.
Anyway, here's an example of running the uv event loop using no C code:
use std;
// A selection from the uv API, reexported from the runtime with a rust_ prefix.
#[link_name = "rustrt"]
native mod uv {
fn rust_uv_loop_new() -> *loop_t;
fn rust_uv_loop_delete(loop: *loop_t);
fn rust_uv_default_loop() -> *loop_t;
fn rust_uv_run(loop: *loop_t) -> ctypes::c_int;
fn rust_uv_unref(loop: *loop_t);
fn rust_uv_idle_init(loop: *loop_t, idle: *idle_t) -> ctypes::c_int;
fn rust_uv_idle_start(idle: *idle_t, cb: idle_cb) -> ctypes::c_int;
}
type opaque_cb = *u8;
type handle_type = ctypes::enum;
type close_cb = opaque_cb;
type idle_cb = opaque_cb;
// Redefine various uv structs. Most fields we don't care about
type handle_private_fields = {
a00: ctypes::c_int, a01: ctypes::c_int,
a02: ctypes::c_int, a03: ctypes::c_int,
a04: ctypes::c_int, a05: ctypes::c_int,
a06: int, a07: int,
a08: int, a09: int,
a10: int, a11: int,
a12: int
};
type handle_fields = {
loop: *loop_t,
type_: handle_type,
close_cb: close_cb,
mutable data: *ctypes::void,
private: handle_private_fields
};
type handle_t = {
fields: handle_fields
};
type loop_t = int;
type idle_t = {
fields: handle_fields
/* private: idle_private_fields */
};
fn handle_fields_new() -> handle_fields {
{
loop: ptr::null(),
type_: 0u32,
close_cb: ptr::null(),
mutable data: ptr::null(),
private: {
a00: 0i32, a01: 0i32, a02: 0i32, a03: 0i32,
a04: 0i32, a05: 0i32,
a06: 0, a07: 0, a08: 0, a09: 0,
a10: 0, a11: 0, a12: 0
}
}
}
fn idle_new() -> idle_t {
{
fields: handle_fields_new()
}
}
// The C ABI callback function. When this is called it will
// automatically switch to the Rust stack, and we can run arbitrary
// Rust code (but we must not fail!).
crust fn idle_cb(handle: *ctypes::void,
_status: ctypes::c_int) unsafe {
std::io::println("I'm a-callin' back");
let idle: *idle_t = unsafe::reinterpret_cast(handle);
// Drop the reference count of the event loop so
// that it exits
uv::rust_uv_unref((*idle).fields.loop);
}
fn main() unsafe {
// Put ourselves into a new scheduler so that we
// run the native event loop without blocking other tasks
task::spawn_sched(1u) {||
let loop = uv::rust_uv_loop_new();
let h = idle_new();
uv::rust_uv_idle_init(loop, ptr::addr_of(h));
// Take the value of our callback function
let callback = idle_cb;
uv::rust_uv_idle_start(ptr::addr_of(h), callback);
// Begin the event loop
uv::rust_uv_run(loop);
uv::rust_uv_loop_delete(loop);
};
}
_______________________________________________
Rust-dev mailing list
[email protected]
https://mail.mozilla.org/listinfo/rust-dev