summaryrefslogtreecommitdiff
path: root/cli
diff options
context:
space:
mode:
authorNathan Whitaker <17734409+nathanwhit@users.noreply.github.com>2024-10-02 10:43:42 -0700
committerGitHub <noreply@github.com>2024-10-02 10:43:42 -0700
commitbbd4ae1bc12dc6b34d4a455015096b7113a5cec5 (patch)
tree1d91babfa3fdfe47d0a3b01809081150ede27628 /cli
parent1837aed79b77b3137563d4730d02e466c85b2b87 (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.toml2
-rw-r--r--cli/build.rs2
-rw-r--r--cli/napi/generated_symbol_exports_list_linux.def2
-rw-r--r--cli/napi/generated_symbol_exports_list_macos.def9
-rw-r--r--cli/napi/generated_symbol_exports_list_windows.def9
-rw-r--r--cli/napi/mod.rs1
-rw-r--r--cli/napi/node_api.rs13
-rw-r--r--cli/napi/sym/symbol_exports.json9
-rw-r--r--cli/napi/uv.rs231
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);
+ }
+}