From d8e8b04cd877bcf00157eeee9b7af0b4244a1827 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donny/=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Tue, 16 Jul 2024 00:25:00 +0900 Subject: [PATCH] fix(allocator): Remove wrong assertions and add tests (#9252) --- crates/swc_allocator/src/alloc.rs | 10 +++++----- crates/swc_allocator/src/lib.rs | 16 ++++++++++++++-- crates/swc_allocator/tests/escape.rs | 24 ++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 7 deletions(-) create mode 100644 crates/swc_allocator/tests/escape.rs diff --git a/crates/swc_allocator/src/alloc.rs b/crates/swc_allocator/src/alloc.rs index 91cd4397d0d4..caf934f661f1 100644 --- a/crates/swc_allocator/src/alloc.rs +++ b/crates/swc_allocator/src/alloc.rs @@ -23,6 +23,11 @@ pub struct Allocator { impl Allocator { /// Invokes `f` in a scope where the allocations are done in this allocator. + /// + /// # Safety + /// + /// [Allocator] must be dropped after dropping all [crate::boxed::Box] and + /// [crate::vec::Vec] created in the scope. #[inline(always)] pub fn scope<'a, F, R>(&'a self, f: F) -> R where @@ -116,11 +121,6 @@ unsafe impl allocator_api2::alloc::Allocator for FastAlloc { unsafe fn deallocate(&self, ptr: NonNull, layout: Layout) { #[cfg(feature = "scoped")] if self.alloc.is_some() { - debug_assert!( - ALLOC.get().is_some(), - "Deallocating a pointer allocated with arena mode with a non-arena mode allocator" - ); - self.with_allocator(|alloc, _| alloc.deallocate(ptr, layout)); return; } diff --git a/crates/swc_allocator/src/lib.rs b/crates/swc_allocator/src/lib.rs index 5f36f29225df..5fd3f8e57faf 100644 --- a/crates/swc_allocator/src/lib.rs +++ b/crates/swc_allocator/src/lib.rs @@ -19,8 +19,8 @@ //! [crate::vec::Vec] very fast. //! //! In this mode, you need to be careful while using [crate::boxed::Box] and -//! [crate::vec::Vec]. Be sure to use same [Allocator] for allocation and -//! deallocation. +//! [crate::vec::Vec]. You should ensure that [Allocator] outlives all +//! [crate::boxed::Box] and [crate::vec::Vec] created in the scope. //! //! Recommened way to use this mode is to wrap the whole operations in //! a call to [Allocator::scope]. @@ -61,3 +61,15 @@ pub struct FastAlloc { #[cfg(feature = "scoped")] alloc: Option<&'static Allocator>, } + +impl FastAlloc { + /// [crate::boxed::Box] or [crate::vec::Vec] created with this instance is + /// managed by the global allocator and it can outlive the + /// [crate::Allocator] instance used for [Allocator::scope]. + pub const fn global() -> Self { + Self { + #[cfg(feature = "scoped")] + alloc: None, + } + } +} diff --git a/crates/swc_allocator/tests/escape.rs b/crates/swc_allocator/tests/escape.rs new file mode 100644 index 000000000000..a215d29e6bb2 --- /dev/null +++ b/crates/swc_allocator/tests/escape.rs @@ -0,0 +1,24 @@ +use swc_allocator::{boxed::Box, Allocator, FastAlloc}; + +#[test] +fn escape() { + let allocator = Allocator::default(); + + let obj = allocator.scope(|| Box::new(1234)); + + assert_eq!(*obj, 1234); + // It should not segfault, because the allocator is still alive. + drop(obj); +} + +#[test] +fn global_allocator() { + let allocator = Allocator::default(); + + let obj = allocator.scope(|| Box::new_in(1234, FastAlloc::global())); + + assert_eq!(*obj, 1234); + drop(allocator); + // Object created with global allocator should outlive the allocator. + drop(obj); +}