diff options
author | Nathan Whitaker <17734409+nathanwhit@users.noreply.github.com> | 2024-10-02 10:43:42 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-10-02 10:43:42 -0700 |
commit | bbd4ae1bc12dc6b34d4a455015096b7113a5cec5 (patch) | |
tree | 1d91babfa3fdfe47d0a3b01809081150ede27628 /cli | |
parent | 1837aed79b77b3137563d4730d02e466c85b2b87 (diff) |
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.
Diffstat (limited to 'cli')
-rw-r--r-- | cli/Cargo.toml | 2 | ||||
-rw-r--r-- | cli/build.rs | 2 | ||||
-rw-r--r-- | cli/napi/generated_symbol_exports_list_linux.def | 2 | ||||
-rw-r--r-- | cli/napi/generated_symbol_exports_list_macos.def | 9 | ||||
-rw-r--r-- | cli/napi/generated_symbol_exports_list_windows.def | 9 | ||||
-rw-r--r-- | cli/napi/mod.rs | 1 | ||||
-rw-r--r-- | cli/napi/node_api.rs | 13 | ||||
-rw-r--r-- | cli/napi/sym/symbol_exports.json | 9 | ||||
-rw-r--r-- | cli/napi/uv.rs | 231 |
9 files changed, 270 insertions, 8 deletions
diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 41636c961..6167baa0b 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -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 diff --git a/cli/build.rs b/cli/build.rs index c3b6f8b04..aa5d3d18c 100644 --- a/cli/build.rs +++ b/cli/build.rs @@ -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:{}", diff --git a/cli/napi/generated_symbol_exports_list_linux.def b/cli/napi/generated_symbol_exports_list_linux.def index 06e94f05b..614880ebf 100644 --- a/cli/napi/generated_symbol_exports_list_linux.def +++ b/cli/napi/generated_symbol_exports_list_linux.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"; };
\ No newline at end of file +{ "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"; };
\ No newline at end of file diff --git a/cli/napi/generated_symbol_exports_list_macos.def b/cli/napi/generated_symbol_exports_list_macos.def index cac7100c6..36b2f37fa 100644 --- a/cli/napi/generated_symbol_exports_list_macos.def +++ b/cli/napi/generated_symbol_exports_list_macos.def @@ -150,4 +150,11 @@ _napi_type_tag_object _napi_check_object_type_tag _node_api_post_finalizer _napi_add_async_cleanup_hook -_napi_remove_async_cleanup_hook
\ No newline at end of file +_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
\ No newline at end of file diff --git a/cli/napi/generated_symbol_exports_list_windows.def b/cli/napi/generated_symbol_exports_list_windows.def index 5386b46e5..b7355112e 100644 --- a/cli/napi/generated_symbol_exports_list_windows.def +++ b/cli/napi/generated_symbol_exports_list_windows.def @@ -152,4 +152,11 @@ EXPORTS napi_check_object_type_tag node_api_post_finalizer napi_add_async_cleanup_hook - napi_remove_async_cleanup_hook
\ No newline at end of file + 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
\ No newline at end of file diff --git a/cli/napi/mod.rs b/cli/napi/mod.rs index 122d2ff06..811efb1ec 100644 --- a/cli/napi/mod.rs +++ b/cli/napi/mod.rs @@ -18,3 +18,4 @@ pub mod js_native_api; pub mod node_api; pub mod util; +pub mod uv; diff --git a/cli/napi/node_api.rs b/cli/napi/node_api.rs index 81cb800a7..2efb71c26 100644 --- a/cli/napi/node_api.rs +++ b/cli/napi/node_api.rs @@ -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] diff --git a/cli/napi/sym/symbol_exports.json b/cli/napi/sym/symbol_exports.json index 64b548d49..00946b8ed 100644 --- a/cli/napi/sym/symbol_exports.json +++ b/cli/napi/sym/symbol_exports.json @@ -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" ] } diff --git a/cli/napi/uv.rs b/cli/napi/uv.rs new file mode 100644 index 000000000..d4cb5c0b3 --- /dev/null +++ b/cli/napi/uv.rs @@ -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); + } +} |