diff --git a/Cargo.lock b/Cargo.lock index 77b6c0cbb7..f4647350b0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -875,6 +875,7 @@ dependencies = [ "serde_v8", "smallvec", "sourcemap", + "tikv-jemalloc-sys", "tokio", "url", "v8", @@ -5130,6 +5131,16 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "tikv-jemalloc-sys" +version = "0.5.3+5.3.0-patched" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a678df20055b43e57ef8cddde41cdfda9a3c1a060b67f4c5836dfb1d78543ba8" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "time" version = "0.3.20" diff --git a/core/Cargo.toml b/core/Cargo.toml index 141583710b..0e0b1d2c7b 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -39,6 +39,9 @@ sourcemap = "6.1" url.workspace = true v8.workspace = true +[target.'cfg(not(target_env = "msvc"))'.dependencies] +tikv-jemalloc-sys = "0.5" + [[example]] name = "http_bench_json_ops" path = "examples/http_bench_json_ops/main.rs" diff --git a/core/runtime.rs b/core/runtime.rs index 46256b8d8e..5470a89b9b 100644 --- a/core/runtime.rs +++ b/core/runtime.rs @@ -72,6 +72,48 @@ struct IsolateAllocations { Option<(Box>, v8::NearHeapLimitCallback)>, } +/// A custom allocator for array buffers for V8. It uses `jemalloc` so it's +/// not available on Windows. +#[cfg(not(target_env = "msvc"))] +mod custom_allocator { + use std::ffi::c_void; + + pub struct RustAllocator; + + pub unsafe extern "C" fn allocate( + _alloc: &RustAllocator, + n: usize, + ) -> *mut c_void { + tikv_jemalloc_sys::calloc(1, n) + } + + pub unsafe extern "C" fn allocate_uninitialized( + _alloc: &RustAllocator, + n: usize, + ) -> *mut c_void { + tikv_jemalloc_sys::malloc(n) + } + + pub unsafe extern "C" fn free( + _alloc: &RustAllocator, + data: *mut c_void, + _n: usize, + ) { + tikv_jemalloc_sys::free(data) + } + + pub unsafe extern "C" fn reallocate( + _alloc: &RustAllocator, + prev: *mut c_void, + _oldlen: usize, + newlen: usize, + ) -> *mut c_void { + tikv_jemalloc_sys::realloc(prev, newlen) + } + + pub unsafe extern "C" fn drop(_alloc: *const RustAllocator) {} +} + /// A single execution context of JavaScript. Corresponds roughly to the "Web /// Worker" concept in the DOM. A JsRuntime is a Future that can be used with /// an event loop (Tokio, async_std). @@ -393,6 +435,20 @@ impl JsRuntime { } (isolate, snapshot_options) } else { + #[cfg(not(target_env = "msvc"))] + let vtable: &'static v8::RustAllocatorVtable< + custom_allocator::RustAllocator, + > = &v8::RustAllocatorVtable { + allocate: custom_allocator::allocate, + allocate_uninitialized: custom_allocator::allocate_uninitialized, + free: custom_allocator::free, + reallocate: custom_allocator::reallocate, + drop: custom_allocator::drop, + }; + #[cfg(not(target_env = "msvc"))] + let allocator = Arc::new(custom_allocator::RustAllocator); + + #[allow(unused_mut)] let mut params = options .create_params .take() @@ -404,6 +460,14 @@ impl JsRuntime { }) .external_references(&**refs); + #[cfg(not(target_env = "msvc"))] + // SAFETY: We are leaking the created `allocator` variable so we're sure + // it will outlive the created isolate. We also made sure that the vtable + // is correct. + let mut params = params.array_buffer_allocator(unsafe { + v8::new_rust_allocator(Arc::into_raw(allocator), vtable) + }); + if let Some(snapshot) = options.startup_snapshot { params = match snapshot { Snapshot::Static(data) => params.snapshot_blob(data),