Skip to content

Commit

Permalink
Added executor for Rust futures driven by Libuv
Browse files Browse the repository at this point in the history
  • Loading branch information
alshdavid committed Jun 22, 2024
1 parent 9edc005 commit 637bb5f
Show file tree
Hide file tree
Showing 13 changed files with 467 additions and 6 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ pkgs/create-neon/create-neon-manual-test-project
test/cli/lib
npm-debug.log
rls*.log
.vscode
161 changes: 157 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion crates/neon/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ doc-comment = { version = "0.3.3", optional = true }
send_wrapper = "0.6.0"
serde = { version = "1.0.197", optional = true }
serde_json = { version = "1.0.114", optional = true }
futures = { version = "0.3", optional = true }
# Fork of libuv bindings for Rust
libuv = { package = "alsh_libuv", version = "2", features = ["sys"], optional = true }

[dependencies.tokio]
version = "1.34.0"
Expand All @@ -43,7 +46,7 @@ features = ["sync"]
optional = true

[features]
default = ["napi-8"]
default = ["napi-8", "futures"]

# Enable extracting values by serializing to JSON
serde = ["dep:serde", "dep:serde_json"]
Expand All @@ -57,6 +60,8 @@ external-buffers = []
# https://github.com/neon-bindings/rfcs/pull/46
futures = ["tokio"]

asynch = ["dep:futures", "dep:libuv"]

# Enable low-level system APIs. The `sys` API allows augmenting the Neon API
# from external crates.
sys = []
Expand Down
26 changes: 26 additions & 0 deletions crates/neon/src/asynch/libuv.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use std::mem::MaybeUninit;
use std::rc::Rc;

use crate::context::internal::Env;
use crate::sys::bindings::get_uv_event_loop;
use libuv::sys::uv_loop_t;
use libuv::Loop;
use once_cell::unsync::OnceCell;

thread_local! {
pub static LIB_UV: OnceCell<Rc<Loop>> = OnceCell::new();
}

/// Gets a reference to Libuv
pub fn get_lib_uv<'a>(env: &Env) -> Rc<Loop> {
LIB_UV.with(move |cell| {
cell.get_or_init(move || {
let mut result = MaybeUninit::uninit();
unsafe { get_uv_event_loop(env.to_raw(), result.as_mut_ptr()) };
let ptr = unsafe { *result.as_mut_ptr() };
let ptr = ptr as *mut uv_loop_t;
Rc::new(unsafe { libuv::r#loop::Loop::from_external(ptr) })
})
.clone()
})
}
6 changes: 6 additions & 0 deletions crates/neon/src/asynch/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
//! This module extends Libuv to work as an executor for Rust futures
pub mod root;
mod libuv;
mod runtime;

pub use runtime::*;
76 changes: 76 additions & 0 deletions crates/neon/src/asynch/root.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
This is a basic method of persisting JavaScript values so
they are given a static lifetime and not collected by GC
This is needed because not all JsValues can be called with .root()
and is probably temporary
*/
use std::cell::RefCell;

use crate::context::Context;
use crate::handle::Handle;
use crate::object::Object;
use crate::types::JsObject;
use crate::types::Value;
use once_cell::unsync::Lazy;

thread_local! {
pub static THREAD_LOCAL_COUNT: Lazy<RefCell<usize>> = Lazy::new(|| RefCell::new(0));
pub static GLOBAL_KEY: Lazy<String> = Lazy::new(|| {
let mut lower = [0; std::mem::size_of::<u16>()];
getrandom::getrandom(&mut lower).expect("Unable to generate number");
let lower = u16::from_ne_bytes(lower);
format!("__neon_root_cache_{}", lower)
});
}

fn ref_count_inc() -> usize {
THREAD_LOCAL_COUNT.with(|c| {
let mut c = c.borrow_mut();
let current = (*c).clone();
*c += 1;
current
})
}

pub fn root<'a>(cx: &mut impl Context<'a>) -> Handle<'a, JsObject> {
let global = cx.global_object();
let key = GLOBAL_KEY.with(|k| cx.string(&*k.as_str()));
match global.get_opt(cx, key).unwrap() {
Some(obj) => obj,
None => {
let init = cx.empty_object();
global.set(cx, key, init).unwrap();
global.get_opt(cx, key).unwrap().unwrap()
}
}
}

#[derive(Clone, Debug)]
pub struct RootGlobal {
inner: String,
}

impl RootGlobal {
pub fn new<'a, V: Value>(cx: &mut impl Context<'a>, value: Handle<V>) -> Self {
let index = ref_count_inc();
let key_str = format!("{}", index);
let key = cx.string(&key_str);
let cache = root(cx);
cache.set(cx, key, value).unwrap();
Self { inner: key_str }
}

pub fn into_inner<'a, V: Value>(&self, cx: &mut impl Context<'a>) -> Handle<'a, V> {
let key = cx.string(&self.inner);
let cache = root(cx);
cache.get(cx, key).unwrap()
}

pub fn remove<'a>(&self, cx: &mut impl Context<'a>) -> bool {
let key = cx.string(&self.inner);
let val = cx.undefined();
let cache = root(cx);
cache.set(cx, key, val).unwrap()
}
}
Loading

0 comments on commit 637bb5f

Please sign in to comment.