From 182f8125da35e08b5dd8affca216196bcd992bb3 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Wed, 27 Dec 2023 14:09:26 +0100 Subject: [PATCH] Implement simple allocator using enif_{alloc,free} Since we can only expect a 64bit alignment from the Erlang allocator, we have to ensure larger alignments by overallocating. The allocator for this case behaves the same way as the zigler "large beam allocator", see https://github.com/E-xyza/zigler/blob/main/priv/beam/allocator.zig. If the alignment is greater than 8, we allocate enough memory to store an additional pointer. The result of the initial allocation is then written immediately before the aligned pointer, which is returned from the allocator. When deallocating, we can retrieve the original pointer and pass it on to `enif_free`. --- Cargo.toml | 5 +++ rustler/Cargo.toml | 1 + rustler/src/alloc.rs | 44 +++++++++++++++++++ rustler/src/lib.rs | 2 + .../native/deprecated_macros/Cargo.toml | 2 +- rustler_tests/native/dynamic_load/Cargo.toml | 2 +- .../native/rustler_bigint_test/Cargo.toml | 2 +- .../native/rustler_compile_tests/Cargo.toml | 2 +- rustler_tests/native/rustler_test/Cargo.toml | 2 +- 9 files changed, 57 insertions(+), 5 deletions(-) create mode 100644 rustler/src/alloc.rs diff --git a/Cargo.toml b/Cargo.toml index bebd1858..24b6b8f2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,3 +13,8 @@ members = [ "rustler_tests/native/rustler_compile_tests", "rustler_benchmarks/native/benchmark", ] +default-members = [ + "rustler", + "rustler_codegen", + "rustler_sys", +] diff --git a/rustler/Cargo.toml b/rustler/Cargo.toml index 68b1e7cd..ac7b0100 100644 --- a/rustler/Cargo.toml +++ b/rustler/Cargo.toml @@ -13,6 +13,7 @@ big_integer = ["dep:num-bigint"] default = ["derive", "nif_version_2_15"] derive = ["rustler_codegen"] alternative_nif_init_name = [] +allocator = [] nif_version_2_14 = ["rustler_sys/nif_version_2_14"] nif_version_2_15 = ["nif_version_2_14", "rustler_sys/nif_version_2_15"] nif_version_2_16 = ["nif_version_2_15", "rustler_sys/nif_version_2_16"] diff --git a/rustler/src/alloc.rs b/rustler/src/alloc.rs new file mode 100644 index 00000000..c62d1593 --- /dev/null +++ b/rustler/src/alloc.rs @@ -0,0 +1,44 @@ +use std::alloc::{GlobalAlloc, Layout}; + +const SIZEOF_USIZE: usize = std::mem::size_of::(); +const MAX_ALIGN: usize = 8; + +#[cfg(feature = "allocator")] +#[global_allocator] +static ALLOCATOR: EnifAllocator = EnifAllocator; + +/// Allocator implementation that forwards all allocation calls to Erlang's allocator. Allows the +/// memory usage to be tracked by the BEAM. +pub struct EnifAllocator; + +unsafe impl GlobalAlloc for EnifAllocator { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + if layout.align() > MAX_ALIGN { + // overallocate + let padded = layout.pad_to_align(); + let total_size = SIZEOF_USIZE + padded.size(); + let ptr = rustler_sys::enif_alloc(total_size) as *mut u8; + + let ptr1 = ptr.wrapping_add(SIZEOF_USIZE); + let aligned_ptr = ptr1.wrapping_add(ptr1.align_offset(layout.align())); + + let header = aligned_ptr.wrapping_sub(SIZEOF_USIZE); + *(header as *mut usize) = ptr as usize; + + aligned_ptr + } else { + rustler_sys::enif_alloc(layout.size()) as *mut u8 + } + } + + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + let ptr = if layout.align() > MAX_ALIGN { + let header = ptr.wrapping_sub(SIZEOF_USIZE); + let ptr = *(header as *mut usize); + ptr as *mut rustler_sys::c_void + } else { + ptr as *mut rustler_sys::c_void + }; + rustler_sys::enif_free(ptr); + } +} diff --git a/rustler/src/lib.rs b/rustler/src/lib.rs index 356bee86..da4ac1cf 100644 --- a/rustler/src/lib.rs +++ b/rustler/src/lib.rs @@ -29,6 +29,8 @@ pub mod wrapper; #[doc(hidden)] pub mod codegen_runtime; +mod alloc; + pub use lazy_static; #[macro_use] diff --git a/rustler_tests/native/deprecated_macros/Cargo.toml b/rustler_tests/native/deprecated_macros/Cargo.toml index 39b6c5c1..f487b144 100644 --- a/rustler_tests/native/deprecated_macros/Cargo.toml +++ b/rustler_tests/native/deprecated_macros/Cargo.toml @@ -10,4 +10,4 @@ path = "src/lib.rs" crate-type = ["cdylib"] [dependencies] -rustler = { path = "../../../rustler" } +rustler = { path = "../../../rustler", features = ["allocator"]} diff --git a/rustler_tests/native/dynamic_load/Cargo.toml b/rustler_tests/native/dynamic_load/Cargo.toml index 4cc7c5d1..126263b3 100644 --- a/rustler_tests/native/dynamic_load/Cargo.toml +++ b/rustler_tests/native/dynamic_load/Cargo.toml @@ -9,4 +9,4 @@ path = "src/lib.rs" crate-type = ["cdylib"] [dependencies] -rustler = { path = "../../../rustler", features = ["big_integer"] } +rustler = { path = "../../../rustler", features = ["big_integer", "allocator"] } diff --git a/rustler_tests/native/rustler_bigint_test/Cargo.toml b/rustler_tests/native/rustler_bigint_test/Cargo.toml index 9f64a529..47374e85 100644 --- a/rustler_tests/native/rustler_bigint_test/Cargo.toml +++ b/rustler_tests/native/rustler_bigint_test/Cargo.toml @@ -9,4 +9,4 @@ path = "src/lib.rs" crate-type = ["cdylib"] [dependencies] -rustler = { path = "../../../rustler", features = ["big_integer"] } +rustler = { path = "../../../rustler", features = ["big_integer", "allocator"] } diff --git a/rustler_tests/native/rustler_compile_tests/Cargo.toml b/rustler_tests/native/rustler_compile_tests/Cargo.toml index 7db15519..a3f6aa1c 100644 --- a/rustler_tests/native/rustler_compile_tests/Cargo.toml +++ b/rustler_tests/native/rustler_compile_tests/Cargo.toml @@ -11,4 +11,4 @@ crate-type = ["cdylib"] [dependencies] lazy_static = "1.4" -rustler = { path = "../../../rustler" } +rustler = { path = "../../../rustler", features = ["allocator"] } diff --git a/rustler_tests/native/rustler_test/Cargo.toml b/rustler_tests/native/rustler_test/Cargo.toml index dc341923..24899c9b 100644 --- a/rustler_tests/native/rustler_test/Cargo.toml +++ b/rustler_tests/native/rustler_test/Cargo.toml @@ -21,4 +21,4 @@ nif_version_2_17 = ["nif_version_2_16", "rustler/nif_version_2_17"] [dependencies] lazy_static = "1.4" -rustler = { path = "../../../rustler" } +rustler = { path = "../../../rustler", features = ["allocator"] }