1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-13 19:12:20 -05:00
denoland-deno/tests/napi/src/uv.rs
Nathan Whitaker bbd4ae1bc1
fix(node): implement libuv APIs needed to support npm:sqlite3 (#25893)
Fixes #24740.

Implements the `uv_mutex_*` and `uv_async_*` APIs.

The mutex API is implemented exactly as libuv, a thin wrapper over the
OS's native mutex.

The async API is implemented in terms of napi_async_work. As documented
in the napi docs, you really shouldn't call `napi_queue_async_work`
multiple times (it is documented as undefined behavior). However, our
implementation doesn't have any issue with this, so I believe it suits
our purpose here.
2024-10-02 10:43:42 -07:00

206 lines
4.9 KiB
Rust

// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
use crate::assert_napi_ok;
use crate::napi_get_callback_info;
use crate::napi_new_property;
use libuv_sys_lite::uv_async_init;
use libuv_sys_lite::uv_async_t;
use libuv_sys_lite::uv_close;
use libuv_sys_lite::uv_handle_t;
use libuv_sys_lite::uv_mutex_destroy;
use libuv_sys_lite::uv_mutex_lock;
use libuv_sys_lite::uv_mutex_t;
use libuv_sys_lite::uv_mutex_unlock;
use napi_sys::*;
use std::mem::MaybeUninit;
use std::ptr;
use std::ptr::addr_of_mut;
use std::ptr::null_mut;
use std::time::Duration;
struct KeepAlive {
tsfn: napi_threadsafe_function,
}
impl KeepAlive {
fn new(env: napi_env) -> Self {
let mut name = null_mut();
assert_napi_ok!(napi_create_string_utf8(
env,
c"test_uv_async".as_ptr(),
13,
&mut name
));
unsafe extern "C" fn dummy(
_env: napi_env,
_cb: napi_callback_info,
) -> napi_value {
ptr::null_mut()
}
let mut func = null_mut();
assert_napi_ok!(napi_create_function(
env,
c"dummy".as_ptr(),
usize::MAX,
Some(dummy),
null_mut(),
&mut func,
));
let mut tsfn = null_mut();
assert_napi_ok!(napi_create_threadsafe_function(
env,
func,
null_mut(),
name,
0,
1,
null_mut(),
None,
null_mut(),
None,
&mut tsfn,
));
assert_napi_ok!(napi_ref_threadsafe_function(env, tsfn));
Self { tsfn }
}
}
impl Drop for KeepAlive {
fn drop(&mut self) {
assert_napi_ok!(napi_release_threadsafe_function(
self.tsfn,
ThreadsafeFunctionReleaseMode::release,
));
}
}
struct Async {
mutex: *mut uv_mutex_t,
env: napi_env,
value: u32,
callback: napi_ref,
_keep_alive: KeepAlive,
}
#[derive(Clone, Copy)]
struct UvAsyncPtr(*mut uv_async_t);
unsafe impl Send for UvAsyncPtr {}
fn new_raw<T>(t: T) -> *mut T {
Box::into_raw(Box::new(t))
}
unsafe extern "C" fn close_cb(handle: *mut uv_handle_t) {
let handle = handle.cast::<uv_async_t>();
let async_ = (*handle).data as *mut Async;
let env = (*async_).env;
assert_napi_ok!(napi_delete_reference(env, (*async_).callback));
uv_mutex_destroy((*async_).mutex);
let _ = Box::from_raw((*async_).mutex);
let _ = Box::from_raw(async_);
let _ = Box::from_raw(handle);
}
unsafe extern "C" fn callback(handle: *mut uv_async_t) {
eprintln!("callback");
let async_ = (*handle).data as *mut Async;
uv_mutex_lock((*async_).mutex);
let env = (*async_).env;
let mut js_cb = null_mut();
assert_napi_ok!(napi_get_reference_value(
env,
(*async_).callback,
&mut js_cb
));
let mut global: napi_value = ptr::null_mut();
assert_napi_ok!(napi_get_global(env, &mut global));
let mut result: napi_value = ptr::null_mut();
let value = (*async_).value;
eprintln!("value is {value}");
let mut value_js = ptr::null_mut();
assert_napi_ok!(napi_create_uint32(env, value, &mut value_js));
let args = &[value_js];
assert_napi_ok!(napi_call_function(
env,
global,
js_cb,
1,
args.as_ptr(),
&mut result,
));
uv_mutex_unlock((*async_).mutex);
if value == 5 {
uv_close(handle.cast(), Some(close_cb));
}
}
unsafe fn uv_async_send(ptr: UvAsyncPtr) {
assert_napi_ok!(libuv_sys_lite::uv_async_send(ptr.0));
}
fn make_uv_mutex() -> *mut uv_mutex_t {
let mutex = new_raw(MaybeUninit::<uv_mutex_t>::uninit());
assert_napi_ok!(libuv_sys_lite::uv_mutex_init(mutex.cast()));
mutex.cast()
}
#[allow(unused_unsafe)]
extern "C" fn test_uv_async(
env: napi_env,
info: napi_callback_info,
) -> napi_value {
let (args, argc, _) = napi_get_callback_info!(env, info, 1);
assert_eq!(argc, 1);
let mut loop_ = null_mut();
assert_napi_ok!(napi_get_uv_event_loop(env, &mut loop_));
let uv_async = new_raw(MaybeUninit::<uv_async_t>::uninit());
let uv_async = uv_async.cast::<uv_async_t>();
let mut js_cb = null_mut();
assert_napi_ok!(napi_create_reference(env, args[0], 1, &mut js_cb));
// let mut tsfn = null_mut();
let data = new_raw(Async {
env,
callback: js_cb,
mutex: make_uv_mutex(),
value: 0,
_keep_alive: KeepAlive::new(env),
});
unsafe {
addr_of_mut!((*uv_async).data).write(data.cast());
assert_napi_ok!(uv_async_init(loop_.cast(), uv_async, Some(callback)));
let uv_async = UvAsyncPtr(uv_async);
std::thread::spawn({
move || {
let data = (*uv_async.0).data as *mut Async;
for _ in 0..5 {
uv_mutex_lock((*data).mutex);
(*data).value += 1;
uv_mutex_unlock((*data).mutex);
std::thread::sleep(Duration::from_millis(10));
uv_async_send(uv_async);
}
}
});
}
ptr::null_mut()
}
pub fn init(env: napi_env, exports: napi_value) {
let properties = &[napi_new_property!(env, "test_uv_async", test_uv_async)];
assert_napi_ok!(napi_define_properties(
env,
exports,
properties.len(),
properties.as_ptr()
));
}