1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
/*! # External executor for async Rust This project aims to provide simple executor which helps to run asynchronous Rust code using external event loops. ## Usage On a Rust side you should add `extern_executor` as dependency to your `cdylib` crate and use `spawn()` function to run futures, like so: ```no_run use extern_executor::spawn; spawn(async { // your awaits }); ``` On a C side you should implement executor's driver using your preferred event loop API. For example, when [libuv](https://github.com/libuv/libuv) is used it may looks like so: ```c #include <uv.h> #include <rust_async_executor.h> static void task_wake(RustAsyncExecutorExternTask data) { uv_async_t* handle = data; // wakeup uv's async task uv_async_send(handle); } static void task_poll(uv_async_t* handle) { // poll internal task until task complete if (!rust_async_executor_poll(handle->data)) { // drop internal task when task complete rust_async_executor_drop(handle->data); // drop uv's async task handle uv_close((uv_handle_t*)handle, NULL); } } static RustAsyncExecutorExternTask task_new(RustAsyncExecutorUserData data) { uv_loop_t* loop = data; // crate and initialize uv's async task handle uv_async_t* handle = malloc(sizeof(uv_async_t)); uv_async_init(loop, handle, task_poll); return handle; } static void task_run(RustAsyncExecutorExternTask task, RustAsyncExecutorInternTask data) { uv_async_t* handle = task; // store internal task handle to be able to poll it later handle->data = data; uv_async_send(handle); // do initial polling (important) } void uv_rust_async_executor_init(uv_loop_t *loop) { // send out executor API to Rust side rust_async_executor_init(task_new, task_run, task_wake, loop); } ``` Now you can run your async code in __libuv__'s event loop like so: ```c int main(void) { uv_loop_t loop; uv_loop_init(&loop); uv_rust_async_executor_init(&loop); my_async_function(my_async_callback); uv_run(&loop, UV_RUN_DEFAULT); uv_loop_close(&loop); return 0; } ``` The C header _rust_async_executor.h_ generated using [cbindgen](https://github.com/eqrion/cbindgen/). There are two options how you can get it: * Copy from _include_ directory in this repo * Generate by youself by using _cbindgen_ feature In second case generated header will be available at `target/$PROFILE/include` directory. ## Built-in event-loop drivers To simplify setup for some widely used event loops the built-in drivers was introduced. To use driver you should enable corresponding feature. Currently supported next drivers: - __uv__ built-in _libuv_ event loop integration (see [example_uv](http://github.com/katyo/extern_executor/tree/master/example_uv)) - __dart__ built-in _dart-lang_ event loop integration (see [example_dart](http://github.com/katyo/extern_executor/tree/master/example_uv)) ## Linking issues Rust currently have an issues related to re-exporting of symbols from crate's dependencies (#[2771](https://github.com/rust-lang/rfcs/issues/2771)). As temporary solution you can setup build profile like so: ```toml [profile.release] lto = true incremental = false ``` ## Tokio compatibility This executor incompatible with [tokio](https://github.com/tokio-rs/tokio)'s futures because _tokio_ still has non-trivial executor which mixed with reactor. */ #![cfg_attr(feature = "no_std", no_std)] #[cfg(feature = "no_std")] extern crate alloc; mod types; mod userdata; mod task; pub mod ffi; #[cfg(feature = "uv")] pub mod uv; #[cfg(feature = "dart")] pub mod dart; pub(crate) use types::*; pub(crate) use userdata::*; pub(crate) use task::*; pub(crate) use ffi::*; pub(crate) mod global { use super::{UserData, null_mut}; pub static mut TASK_NEW: UserData = null_mut(); pub static mut TASK_RUN: UserData = null_mut(); pub static mut TASK_WAKE: UserData = null_mut(); pub static mut TASK_DATA: UserData = null_mut(); } /// Spawn task /// /// Create task for future and run it pub fn spawn(future: impl Future + Send + 'static) { let future = Box::pin(future); let task_new: TaskNew = unsafe { transmute(global::TASK_NEW) }; let task_run: TaskRun = unsafe { transmute(global::TASK_RUN) }; let task_data: ExternData = unsafe { global::TASK_DATA }; let task = task_new(task_data); task_run(task, task_wrap(future, task)); } #[deprecated] #[macro_export] macro_rules! externs { () => { /// Initialize async executor by providing task API calls #[no_mangle] pub extern "C" fn rust_async_executor_init(task_new: $crate::ffi::TaskNew, task_run: $crate::ffi::TaskRun, task_wake: $crate::ffi::TaskWake, task_data: $crate::ffi::ExternData) { $crate::ffi::task_init(task_new, task_run, task_wake, task_data); } /// Task poll function which should be called to resume task #[no_mangle] pub extern "C" fn rust_async_executor_poll(task: $crate::ffi::InternTask) -> bool { $crate::ffi::task_poll(task) } /// Task drop function which should be called to delete task #[no_mangle] pub extern "C" fn rust_aync_executor_drop(task: $crate::ffi::InternTask) { $crate::ffi::task_drop(task); } } }