mirror of
https://github.com/denoland/deno.git
synced 2024-11-21 15:04:11 -05:00
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.
This commit is contained in:
parent
1837aed79b
commit
bbd4ae1bc1
15 changed files with 539 additions and 16 deletions
47
Cargo.lock
generated
47
Cargo.lock
generated
|
@ -471,6 +471,26 @@ dependencies = [
|
|||
"which 4.4.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bindgen"
|
||||
version = "0.70.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"cexpr",
|
||||
"clang-sys",
|
||||
"itertools 0.13.0",
|
||||
"log",
|
||||
"prettyplease 0.2.17",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
"rustc-hash 1.1.0",
|
||||
"shlex",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bit-set"
|
||||
version = "0.5.3"
|
||||
|
@ -688,7 +708,7 @@ checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4"
|
|||
dependencies = [
|
||||
"glob",
|
||||
"libc",
|
||||
"libloading 0.8.3",
|
||||
"libloading 0.8.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1064,7 +1084,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "b28bfe653d79bd16c77f659305b195b82bb5ce0c0eb2a4846b82ddbd77586813"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"libloading 0.8.3",
|
||||
"libloading 0.8.5",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
|
@ -1205,6 +1225,7 @@ dependencies = [
|
|||
"lazy-regex",
|
||||
"libc",
|
||||
"libsui",
|
||||
"libuv-sys-lite",
|
||||
"libz-sys",
|
||||
"log",
|
||||
"lsp-types",
|
||||
|
@ -1253,6 +1274,7 @@ dependencies = [
|
|||
"walkdir",
|
||||
"which 4.4.2",
|
||||
"winapi",
|
||||
"windows-sys 0.52.0",
|
||||
"winres",
|
||||
"yoke",
|
||||
"zeromq",
|
||||
|
@ -4039,7 +4061,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"libloading 0.8.3",
|
||||
"libloading 0.8.5",
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
|
@ -4144,9 +4166,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
version = "0.8.3"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19"
|
||||
checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-targets 0.52.4",
|
||||
|
@ -4192,6 +4214,16 @@ dependencies = [
|
|||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libuv-sys-lite"
|
||||
version = "1.48.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca8dfd1a173826d193e3b955e07c22765829890f62c677a59c4a410cb4f47c01"
|
||||
dependencies = [
|
||||
"bindgen 0.70.1",
|
||||
"libloading 0.8.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libz-sys"
|
||||
version = "1.1.16"
|
||||
|
@ -7205,6 +7237,7 @@ dependencies = [
|
|||
name = "test_napi"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"libuv-sys-lite",
|
||||
"napi-build",
|
||||
"napi-sys",
|
||||
"test_server",
|
||||
|
@ -7891,7 +7924,7 @@ version = "0.106.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a381badc47c6f15acb5fe0b5b40234162349ed9d4e4fd7c83a7f5547c0fc69c5"
|
||||
dependencies = [
|
||||
"bindgen",
|
||||
"bindgen 0.69.4",
|
||||
"bitflags 2.6.0",
|
||||
"fslock",
|
||||
"gzip-header",
|
||||
|
@ -8158,7 +8191,7 @@ dependencies = [
|
|||
"js-sys",
|
||||
"khronos-egl",
|
||||
"libc",
|
||||
"libloading 0.8.3",
|
||||
"libloading 0.8.5",
|
||||
"log",
|
||||
"metal",
|
||||
"naga",
|
||||
|
|
|
@ -225,7 +225,7 @@ nix = "=0.26.2"
|
|||
# windows deps
|
||||
junction = "=0.2.0"
|
||||
winapi = "=0.3.9"
|
||||
windows-sys = { version = "0.52.0", features = ["Win32_Foundation", "Win32_Media", "Win32_Storage_FileSystem", "Win32_System_IO", "Win32_System_WindowsProgramming", "Wdk", "Wdk_System", "Wdk_System_SystemInformation", "Win32_System_Pipes", "Wdk_Storage_FileSystem", "Win32_System_Registry"] }
|
||||
windows-sys = { version = "0.52.0", features = ["Win32_Foundation", "Win32_Media", "Win32_Storage_FileSystem", "Win32_System_IO", "Win32_System_WindowsProgramming", "Wdk", "Wdk_System", "Wdk_System_SystemInformation", "Win32_System_Pipes", "Wdk_Storage_FileSystem", "Win32_System_Registry", "Win32_System_Kernel"] }
|
||||
winres = "=0.1.12"
|
||||
|
||||
# NB: the `bench` and `release` profiles must remain EXACTLY the same.
|
||||
|
|
|
@ -170,12 +170,14 @@ zstd.workspace = true
|
|||
[target.'cfg(windows)'.dependencies]
|
||||
junction.workspace = true
|
||||
winapi = { workspace = true, features = ["knownfolders", "mswsock", "objbase", "shlobj", "tlhelp32", "winbase", "winerror", "winsock2"] }
|
||||
windows-sys.workspace = true
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
nix.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
deno_bench_util.workspace = true
|
||||
libuv-sys-lite = "=1.48.2"
|
||||
pretty_assertions.workspace = true
|
||||
test_util.workspace = true
|
||||
|
||||
|
|
|
@ -387,6 +387,8 @@ fn main() {
|
|||
"Missing symbols list! Generate using tools/napi/generate_symbols_lists.js",
|
||||
);
|
||||
|
||||
println!("cargo:rustc-rerun-if-changed={}", symbols_path.display());
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
println!(
|
||||
"cargo:rustc-link-arg-bin=deno=/DEF:{}",
|
||||
|
|
|
@ -1 +1 @@
|
|||
{ "node_api_create_syntax_error"; "napi_make_callback"; "napi_has_named_property"; "napi_async_destroy"; "napi_coerce_to_object"; "napi_get_arraybuffer_info"; "napi_detach_arraybuffer"; "napi_get_undefined"; "napi_reference_unref"; "napi_fatal_error"; "napi_open_callback_scope"; "napi_close_callback_scope"; "napi_get_value_uint32"; "napi_create_function"; "napi_create_arraybuffer"; "napi_get_value_int64"; "napi_get_all_property_names"; "napi_resolve_deferred"; "napi_is_detached_arraybuffer"; "napi_create_string_utf8"; "napi_create_threadsafe_function"; "node_api_throw_syntax_error"; "napi_create_bigint_int64"; "napi_wrap"; "napi_set_property"; "napi_get_value_bigint_int64"; "napi_open_handle_scope"; "napi_create_error"; "napi_create_buffer"; "napi_cancel_async_work"; "napi_is_exception_pending"; "napi_acquire_threadsafe_function"; "napi_create_external"; "napi_get_threadsafe_function_context"; "napi_get_null"; "napi_create_string_utf16"; "node_api_create_external_string_utf16"; "napi_get_value_bigint_uint64"; "napi_module_register"; "napi_is_typedarray"; "napi_create_external_buffer"; "napi_get_new_target"; "napi_get_instance_data"; "napi_close_handle_scope"; "napi_get_value_string_utf16"; "napi_get_property_names"; "napi_is_arraybuffer"; "napi_get_cb_info"; "napi_define_properties"; "napi_add_env_cleanup_hook"; "node_api_get_module_file_name"; "napi_get_node_version"; "napi_create_int64"; "napi_create_double"; "napi_get_and_clear_last_exception"; "napi_create_reference"; "napi_get_typedarray_info"; "napi_call_threadsafe_function"; "napi_get_last_error_info"; "napi_create_array_with_length"; "napi_coerce_to_number"; "napi_get_global"; "napi_is_error"; "napi_set_instance_data"; "napi_create_typedarray"; "napi_throw_type_error"; "napi_has_property"; "napi_get_value_external"; "napi_create_range_error"; "napi_typeof"; "napi_ref_threadsafe_function"; "napi_create_bigint_uint64"; "napi_get_prototype"; "napi_adjust_external_memory"; "napi_release_threadsafe_function"; "napi_delete_async_work"; "napi_create_string_latin1"; "node_api_create_external_string_latin1"; "napi_is_array"; "napi_unref_threadsafe_function"; "napi_throw_error"; "napi_has_own_property"; "napi_get_reference_value"; "napi_remove_env_cleanup_hook"; "napi_get_value_string_utf8"; "napi_is_promise"; "napi_get_boolean"; "napi_run_script"; "napi_get_element"; "napi_get_named_property"; "napi_get_buffer_info"; "napi_get_value_bool"; "napi_reference_ref"; "napi_create_object"; "napi_create_promise"; "napi_create_int32"; "napi_escape_handle"; "napi_open_escapable_handle_scope"; "napi_throw"; "napi_get_value_double"; "napi_set_named_property"; "napi_call_function"; "napi_create_date"; "napi_object_freeze"; "napi_get_uv_event_loop"; "napi_get_value_string_latin1"; "napi_reject_deferred"; "napi_add_finalizer"; "napi_create_array"; "napi_delete_reference"; "napi_get_date_value"; "napi_create_dataview"; "napi_get_version"; "napi_define_class"; "napi_is_date"; "napi_remove_wrap"; "napi_delete_property"; "napi_instanceof"; "napi_create_buffer_copy"; "napi_delete_element"; "napi_object_seal"; "napi_queue_async_work"; "napi_get_value_bigint_words"; "napi_is_buffer"; "napi_get_array_length"; "napi_get_property"; "napi_new_instance"; "napi_set_element"; "napi_create_bigint_words"; "napi_strict_equals"; "napi_is_dataview"; "napi_close_escapable_handle_scope"; "napi_get_dataview_info"; "napi_get_value_int32"; "napi_unwrap"; "napi_throw_range_error"; "napi_coerce_to_bool"; "napi_create_uint32"; "napi_has_element"; "napi_create_external_arraybuffer"; "napi_create_symbol"; "node_api_symbol_for"; "napi_coerce_to_string"; "napi_create_type_error"; "napi_fatal_exception"; "napi_create_async_work"; "napi_async_init"; "node_api_create_property_key_utf16"; "napi_type_tag_object"; "napi_check_object_type_tag"; "node_api_post_finalizer"; "napi_add_async_cleanup_hook"; "napi_remove_async_cleanup_hook"; };
|
||||
{ "node_api_create_syntax_error"; "napi_make_callback"; "napi_has_named_property"; "napi_async_destroy"; "napi_coerce_to_object"; "napi_get_arraybuffer_info"; "napi_detach_arraybuffer"; "napi_get_undefined"; "napi_reference_unref"; "napi_fatal_error"; "napi_open_callback_scope"; "napi_close_callback_scope"; "napi_get_value_uint32"; "napi_create_function"; "napi_create_arraybuffer"; "napi_get_value_int64"; "napi_get_all_property_names"; "napi_resolve_deferred"; "napi_is_detached_arraybuffer"; "napi_create_string_utf8"; "napi_create_threadsafe_function"; "node_api_throw_syntax_error"; "napi_create_bigint_int64"; "napi_wrap"; "napi_set_property"; "napi_get_value_bigint_int64"; "napi_open_handle_scope"; "napi_create_error"; "napi_create_buffer"; "napi_cancel_async_work"; "napi_is_exception_pending"; "napi_acquire_threadsafe_function"; "napi_create_external"; "napi_get_threadsafe_function_context"; "napi_get_null"; "napi_create_string_utf16"; "node_api_create_external_string_utf16"; "napi_get_value_bigint_uint64"; "napi_module_register"; "napi_is_typedarray"; "napi_create_external_buffer"; "napi_get_new_target"; "napi_get_instance_data"; "napi_close_handle_scope"; "napi_get_value_string_utf16"; "napi_get_property_names"; "napi_is_arraybuffer"; "napi_get_cb_info"; "napi_define_properties"; "napi_add_env_cleanup_hook"; "node_api_get_module_file_name"; "napi_get_node_version"; "napi_create_int64"; "napi_create_double"; "napi_get_and_clear_last_exception"; "napi_create_reference"; "napi_get_typedarray_info"; "napi_call_threadsafe_function"; "napi_get_last_error_info"; "napi_create_array_with_length"; "napi_coerce_to_number"; "napi_get_global"; "napi_is_error"; "napi_set_instance_data"; "napi_create_typedarray"; "napi_throw_type_error"; "napi_has_property"; "napi_get_value_external"; "napi_create_range_error"; "napi_typeof"; "napi_ref_threadsafe_function"; "napi_create_bigint_uint64"; "napi_get_prototype"; "napi_adjust_external_memory"; "napi_release_threadsafe_function"; "napi_delete_async_work"; "napi_create_string_latin1"; "node_api_create_external_string_latin1"; "napi_is_array"; "napi_unref_threadsafe_function"; "napi_throw_error"; "napi_has_own_property"; "napi_get_reference_value"; "napi_remove_env_cleanup_hook"; "napi_get_value_string_utf8"; "napi_is_promise"; "napi_get_boolean"; "napi_run_script"; "napi_get_element"; "napi_get_named_property"; "napi_get_buffer_info"; "napi_get_value_bool"; "napi_reference_ref"; "napi_create_object"; "napi_create_promise"; "napi_create_int32"; "napi_escape_handle"; "napi_open_escapable_handle_scope"; "napi_throw"; "napi_get_value_double"; "napi_set_named_property"; "napi_call_function"; "napi_create_date"; "napi_object_freeze"; "napi_get_uv_event_loop"; "napi_get_value_string_latin1"; "napi_reject_deferred"; "napi_add_finalizer"; "napi_create_array"; "napi_delete_reference"; "napi_get_date_value"; "napi_create_dataview"; "napi_get_version"; "napi_define_class"; "napi_is_date"; "napi_remove_wrap"; "napi_delete_property"; "napi_instanceof"; "napi_create_buffer_copy"; "napi_delete_element"; "napi_object_seal"; "napi_queue_async_work"; "napi_get_value_bigint_words"; "napi_is_buffer"; "napi_get_array_length"; "napi_get_property"; "napi_new_instance"; "napi_set_element"; "napi_create_bigint_words"; "napi_strict_equals"; "napi_is_dataview"; "napi_close_escapable_handle_scope"; "napi_get_dataview_info"; "napi_get_value_int32"; "napi_unwrap"; "napi_throw_range_error"; "napi_coerce_to_bool"; "napi_create_uint32"; "napi_has_element"; "napi_create_external_arraybuffer"; "napi_create_symbol"; "node_api_symbol_for"; "napi_coerce_to_string"; "napi_create_type_error"; "napi_fatal_exception"; "napi_create_async_work"; "napi_async_init"; "node_api_create_property_key_utf16"; "napi_type_tag_object"; "napi_check_object_type_tag"; "node_api_post_finalizer"; "napi_add_async_cleanup_hook"; "napi_remove_async_cleanup_hook"; "uv_mutex_init"; "uv_mutex_lock"; "uv_mutex_unlock"; "uv_mutex_destroy"; "uv_async_init"; "uv_async_send"; "uv_close"; };
|
|
@ -151,3 +151,10 @@ _napi_check_object_type_tag
|
|||
_node_api_post_finalizer
|
||||
_napi_add_async_cleanup_hook
|
||||
_napi_remove_async_cleanup_hook
|
||||
_uv_mutex_init
|
||||
_uv_mutex_lock
|
||||
_uv_mutex_unlock
|
||||
_uv_mutex_destroy
|
||||
_uv_async_init
|
||||
_uv_async_send
|
||||
_uv_close
|
|
@ -153,3 +153,10 @@ EXPORTS
|
|||
node_api_post_finalizer
|
||||
napi_add_async_cleanup_hook
|
||||
napi_remove_async_cleanup_hook
|
||||
uv_mutex_init
|
||||
uv_mutex_lock
|
||||
uv_mutex_unlock
|
||||
uv_mutex_destroy
|
||||
uv_async_init
|
||||
uv_async_send
|
||||
uv_close
|
|
@ -18,3 +18,4 @@
|
|||
pub mod js_native_api;
|
||||
pub mod node_api;
|
||||
pub mod util;
|
||||
pub mod uv;
|
||||
|
|
|
@ -547,11 +547,16 @@ fn napi_delete_async_work(env: *mut Env, work: napi_async_work) -> napi_status {
|
|||
}
|
||||
|
||||
#[napi_sym]
|
||||
fn napi_get_uv_event_loop(env: *mut Env, uv_loop: *mut *mut ()) -> napi_status {
|
||||
let env = check_env!(env);
|
||||
fn napi_get_uv_event_loop(
|
||||
env_ptr: *mut Env,
|
||||
uv_loop: *mut *mut (),
|
||||
) -> napi_status {
|
||||
let env = check_env!(env_ptr);
|
||||
check_arg!(env, uv_loop);
|
||||
// There is no uv_loop in Deno
|
||||
napi_set_last_error(env, napi_generic_failure)
|
||||
unsafe {
|
||||
*uv_loop = env_ptr.cast();
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
#[napi_sym]
|
||||
|
|
|
@ -152,6 +152,13 @@
|
|||
"napi_check_object_type_tag",
|
||||
"node_api_post_finalizer",
|
||||
"napi_add_async_cleanup_hook",
|
||||
"napi_remove_async_cleanup_hook"
|
||||
"napi_remove_async_cleanup_hook",
|
||||
"uv_mutex_init",
|
||||
"uv_mutex_lock",
|
||||
"uv_mutex_unlock",
|
||||
"uv_mutex_destroy",
|
||||
"uv_async_init",
|
||||
"uv_async_send",
|
||||
"uv_close"
|
||||
]
|
||||
}
|
||||
|
|
231
cli/napi/uv.rs
Normal file
231
cli/napi/uv.rs
Normal file
|
@ -0,0 +1,231 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use deno_core::parking_lot::Mutex;
|
||||
use deno_runtime::deno_napi::*;
|
||||
use std::mem::MaybeUninit;
|
||||
use std::ptr::addr_of_mut;
|
||||
|
||||
#[allow(clippy::print_stderr)]
|
||||
fn assert_ok(res: c_int) -> c_int {
|
||||
if res != 0 {
|
||||
eprintln!("bad result in uv polyfill: {res}");
|
||||
// don't panic because that might unwind into
|
||||
// c/c++
|
||||
std::process::abort();
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
use crate::napi::js_native_api::napi_create_string_utf8;
|
||||
use crate::napi::node_api::napi_create_async_work;
|
||||
use crate::napi::node_api::napi_delete_async_work;
|
||||
use crate::napi::node_api::napi_queue_async_work;
|
||||
use std::ffi::c_int;
|
||||
|
||||
const UV_MUTEX_SIZE: usize = {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
std::mem::size_of::<libc::pthread_mutex_t>()
|
||||
}
|
||||
#[cfg(windows)]
|
||||
{
|
||||
std::mem::size_of::<windows_sys::Win32::System::Threading::CRITICAL_SECTION>(
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
#[repr(C)]
|
||||
struct uv_mutex_t {
|
||||
mutex: Mutex<()>,
|
||||
_padding: [MaybeUninit<usize>; const {
|
||||
(UV_MUTEX_SIZE - size_of::<Mutex<()>>()) / size_of::<usize>()
|
||||
}],
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn uv_mutex_init(lock: *mut uv_mutex_t) -> c_int {
|
||||
unsafe {
|
||||
addr_of_mut!((*lock).mutex).write(Mutex::new(()));
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn uv_mutex_lock(lock: *mut uv_mutex_t) {
|
||||
unsafe {
|
||||
let guard = (*lock).mutex.lock();
|
||||
// forget the guard so it doesn't unlock when it goes out of scope.
|
||||
// we're going to unlock it manually
|
||||
std::mem::forget(guard);
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn uv_mutex_unlock(lock: *mut uv_mutex_t) {
|
||||
unsafe {
|
||||
(*lock).mutex.force_unlock();
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn uv_mutex_destroy(_lock: *mut uv_mutex_t) {
|
||||
// no cleanup required
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[allow(dead_code)]
|
||||
enum uv_handle_type {
|
||||
UV_UNKNOWN_HANDLE = 0,
|
||||
UV_ASYNC,
|
||||
UV_CHECK,
|
||||
UV_FS_EVENT,
|
||||
UV_FS_POLL,
|
||||
UV_HANDLE,
|
||||
UV_IDLE,
|
||||
UV_NAMED_PIPE,
|
||||
UV_POLL,
|
||||
UV_PREPARE,
|
||||
UV_PROCESS,
|
||||
UV_STREAM,
|
||||
UV_TCP,
|
||||
UV_TIMER,
|
||||
UV_TTY,
|
||||
UV_UDP,
|
||||
UV_SIGNAL,
|
||||
UV_FILE,
|
||||
UV_HANDLE_TYPE_MAX,
|
||||
}
|
||||
|
||||
const UV_HANDLE_SIZE: usize = 96;
|
||||
|
||||
#[repr(C)]
|
||||
struct uv_handle_t {
|
||||
// public members
|
||||
pub data: *mut c_void,
|
||||
pub r#loop: *mut uv_loop_t,
|
||||
pub r#type: uv_handle_type,
|
||||
|
||||
_padding: [MaybeUninit<usize>; const {
|
||||
(UV_HANDLE_SIZE
|
||||
- size_of::<*mut c_void>()
|
||||
- size_of::<*mut uv_loop_t>()
|
||||
- size_of::<uv_handle_type>())
|
||||
/ size_of::<usize>()
|
||||
}],
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
const UV_ASYNC_SIZE: usize = 128;
|
||||
|
||||
#[cfg(windows)]
|
||||
const UV_ASYNC_SIZE: usize = 224;
|
||||
|
||||
#[repr(C)]
|
||||
struct uv_async_t {
|
||||
// public members
|
||||
pub data: *mut c_void,
|
||||
pub r#loop: *mut uv_loop_t,
|
||||
pub r#type: uv_handle_type,
|
||||
// private
|
||||
async_cb: uv_async_cb,
|
||||
work: napi_async_work,
|
||||
_padding: [MaybeUninit<usize>; const {
|
||||
(UV_ASYNC_SIZE
|
||||
- size_of::<*mut c_void>()
|
||||
- size_of::<*mut uv_loop_t>()
|
||||
- size_of::<uv_handle_type>()
|
||||
- size_of::<uv_async_cb>()
|
||||
- size_of::<napi_async_work>())
|
||||
/ size_of::<usize>()
|
||||
}],
|
||||
}
|
||||
|
||||
type uv_loop_t = Env;
|
||||
type uv_async_cb = extern "C" fn(handle: *mut uv_async_t);
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn uv_async_init(
|
||||
r#loop: *mut uv_loop_t,
|
||||
// probably uninitialized
|
||||
r#async: *mut uv_async_t,
|
||||
async_cb: uv_async_cb,
|
||||
) -> c_int {
|
||||
unsafe {
|
||||
addr_of_mut!((*r#async).r#loop).write(r#loop);
|
||||
addr_of_mut!((*r#async).r#type).write(uv_handle_type::UV_ASYNC);
|
||||
addr_of_mut!((*r#async).async_cb).write(async_cb);
|
||||
|
||||
let mut resource_name: MaybeUninit<napi_value> = MaybeUninit::uninit();
|
||||
assert_ok(napi_create_string_utf8(
|
||||
r#loop,
|
||||
c"uv_async".as_ptr(),
|
||||
usize::MAX,
|
||||
resource_name.as_mut_ptr(),
|
||||
));
|
||||
let resource_name = resource_name.assume_init();
|
||||
|
||||
let res = napi_create_async_work(
|
||||
r#loop,
|
||||
None::<v8::Local<'static, v8::Value>>.into(),
|
||||
resource_name,
|
||||
Some(async_exec_wrap),
|
||||
None,
|
||||
r#async.cast(),
|
||||
addr_of_mut!((*r#async).work),
|
||||
);
|
||||
-res
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn uv_async_send(handle: *mut uv_async_t) -> c_int {
|
||||
unsafe { -napi_queue_async_work((*handle).r#loop, (*handle).work) }
|
||||
}
|
||||
|
||||
type uv_close_cb = unsafe extern "C" fn(*mut uv_handle_t);
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn uv_close(handle: *mut uv_handle_t, close: uv_close_cb) {
|
||||
unsafe {
|
||||
if handle.is_null() {
|
||||
close(handle);
|
||||
return;
|
||||
}
|
||||
if let uv_handle_type::UV_ASYNC = (*handle).r#type {
|
||||
let handle: *mut uv_async_t = handle.cast();
|
||||
napi_delete_async_work((*handle).r#loop, (*handle).work);
|
||||
}
|
||||
close(handle);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" fn async_exec_wrap(_env: napi_env, data: *mut c_void) {
|
||||
let data: *mut uv_async_t = data.cast();
|
||||
unsafe {
|
||||
((*data).async_cb)(data);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn sizes() {
|
||||
assert_eq!(
|
||||
std::mem::size_of::<libuv_sys_lite::uv_mutex_t>(),
|
||||
UV_MUTEX_SIZE
|
||||
);
|
||||
assert_eq!(
|
||||
std::mem::size_of::<libuv_sys_lite::uv_handle_t>(),
|
||||
UV_HANDLE_SIZE
|
||||
);
|
||||
assert_eq!(
|
||||
std::mem::size_of::<libuv_sys_lite::uv_async_t>(),
|
||||
UV_ASYNC_SIZE
|
||||
);
|
||||
assert_eq!(std::mem::size_of::<uv_mutex_t>(), UV_MUTEX_SIZE);
|
||||
assert_eq!(std::mem::size_of::<uv_handle_t>(), UV_HANDLE_SIZE);
|
||||
assert_eq!(std::mem::size_of::<uv_async_t>(), UV_ASYNC_SIZE);
|
||||
}
|
||||
}
|
|
@ -13,6 +13,7 @@ repository.workspace = true
|
|||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
libuv-sys-lite = "=1.48.2"
|
||||
napi-sys = { version = "=2.2.2", default-features = false, features = ["napi7"] }
|
||||
|
||||
[dev-dependencies]
|
||||
|
|
|
@ -31,6 +31,7 @@ pub mod strings;
|
|||
pub mod symbol;
|
||||
pub mod tsfn;
|
||||
pub mod typedarray;
|
||||
pub mod uv;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! cstr {
|
||||
|
@ -138,6 +139,7 @@ unsafe extern "C" fn napi_register_module_v1(
|
|||
#[cfg(windows)]
|
||||
{
|
||||
napi_sys::setup();
|
||||
libuv_sys_lite::setup();
|
||||
}
|
||||
|
||||
// We create a fresh exports object and leave the passed
|
||||
|
@ -169,6 +171,7 @@ unsafe extern "C" fn napi_register_module_v1(
|
|||
symbol::init(env, exports);
|
||||
make_callback::init(env, exports);
|
||||
object::init(env, exports);
|
||||
uv::init(env, exports);
|
||||
|
||||
init_cleanup_hook(env, exports);
|
||||
|
||||
|
|
206
tests/napi/src/uv.rs
Normal file
206
tests/napi/src/uv.rs
Normal file
|
@ -0,0 +1,206 @@
|
|||
// 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()
|
||||
));
|
||||
}
|
18
tests/napi/uv_test.js
Normal file
18
tests/napi/uv_test.js
Normal file
|
@ -0,0 +1,18 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { assertEquals, loadTestLibrary } from "./common.js";
|
||||
|
||||
const uv = loadTestLibrary();
|
||||
|
||||
Deno.test("napi uv async", async () => {
|
||||
let called = false;
|
||||
await new Promise((resolve) => {
|
||||
uv.test_uv_async((value) => {
|
||||
called = true;
|
||||
if (value === 5) {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
assertEquals(called, true);
|
||||
});
|
Loading…
Reference in a new issue