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

Reply via email to