From eb87f1a58450e76702676575273cedce8c8c915b Mon Sep 17 00:00:00 2001 From: Jalon Wong Date: Thu, 14 Aug 2025 14:30:50 -0500 Subject: [PATCH 1/3] Add a macro to make initialization easier --- CHANGELOG.md | 6 +++++ Cargo.toml | 2 +- README.md | 4 +++ examples/global_alloc.rs | 7 ++--- examples/llff_integration_test.rs | 6 ++--- examples/tlsf_integration_test.rs | 5 ++-- src/lib.rs | 45 +++++++++++++++++++++++++++++++ 7 files changed, 62 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f8c5a6..e23d086 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## [Unreleased] + +### Added + +- Added a `init` macro to make initialization easier. + ## [v0.6.0] - 2024-09-01 ### Added diff --git a/Cargo.toml b/Cargo.toml index bcf197a..d23dfe6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,7 +29,7 @@ allocator_api = [] # Use the Two-Level Segregated Fit allocator tlsf = ["rlsf", "const-default"] - # Use the LinkedList first-fit allocator +# Use the LinkedList first-fit allocator llff = ["linked_list_allocator"] [dependencies] diff --git a/README.md b/README.md index a1022f4..868ae51 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,10 @@ static HEAP: Heap = Heap::empty(); #[entry] fn main() -> ! { // Initialize the allocator BEFORE you use it + unsafe { + embedded_alloc::init!(HEAP, 1024); + } + // Alternatively, you can write the code directly to meet specific requirements. { use core::mem::MaybeUninit; const HEAP_SIZE: usize = 1024; diff --git a/examples/global_alloc.rs b/examples/global_alloc.rs index 81705fc..b47f0ef 100644 --- a/examples/global_alloc.rs +++ b/examples/global_alloc.rs @@ -17,11 +17,8 @@ static HEAP: Heap = Heap::empty(); #[entry] fn main() -> ! { // Initialize the allocator BEFORE you use it - { - use core::mem::MaybeUninit; - const HEAP_SIZE: usize = 1024; - static mut HEAP_MEM: [MaybeUninit; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE]; - unsafe { HEAP.init(&raw mut HEAP_MEM as usize, HEAP_SIZE) } + unsafe { + embedded_alloc::init!(HEAP, 1024); } let mut xs = Vec::new(); diff --git a/examples/llff_integration_test.rs b/examples/llff_integration_test.rs index ce9b553..5b7f4b2 100644 --- a/examples/llff_integration_test.rs +++ b/examples/llff_integration_test.rs @@ -63,10 +63,8 @@ fn test_allocator_api() { #[entry] fn main() -> ! { - { - const HEAP_SIZE: usize = 1024; - static mut HEAP_MEM: [MaybeUninit; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE]; - unsafe { HEAP.init(&raw mut HEAP_MEM as usize, HEAP_SIZE) } + unsafe { + embedded_alloc::init!(HEAP, 1024); } #[allow(clippy::type_complexity)] diff --git a/examples/tlsf_integration_test.rs b/examples/tlsf_integration_test.rs index 26a4353..591b7d3 100644 --- a/examples/tlsf_integration_test.rs +++ b/examples/tlsf_integration_test.rs @@ -81,9 +81,8 @@ fn test_allocator_api() { #[entry] fn main() -> ! { - { - static mut HEAP_MEM: [MaybeUninit; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE]; - unsafe { HEAP.init(&raw mut HEAP_MEM as usize, HEAP_SIZE) } + unsafe { + embedded_alloc::init!(HEAP, HEAP_SIZE); } #[allow(clippy::type_complexity)] diff --git a/src/lib.rs b/src/lib.rs index 4308790..860c82b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,3 +12,48 @@ mod tlsf; pub use llff::Heap as LlffHeap; #[cfg(feature = "tlsf")] pub use tlsf::Heap as TlsfHeap; + +/// Initialize the global heap. +/// +/// This macro creates a static, uninitialized memory buffer of the specified size and +/// initializes the heap instance with that buffer. +/// +/// # Parameters +/// +/// - `$heap:ident`: The identifier of the global heap instance to initialize. +/// - `$size:expr`: An expression evaluating to a `usize` that specifies the size of the +/// static memory buffer in bytes. It must be **greater than zero**. +/// +/// # Safety +/// +/// This macro must be called first, before any operations on the heap, and **only once**. +/// It internally calls `Heap::init(...)` on the heap, +/// so `Heap::init(...)` should not be called directly if this macro is used. +/// +/// # Example +/// +/// ```rust +/// use cortex_m_rt::entry; +/// use embedded_alloc::LlffHeap as Heap; +/// +/// #[global_allocator] +/// static HEAP: Heap = Heap::empty(); +/// +/// #[entry] +/// fn main() -> ! { +/// // Initialize the allocator BEFORE you use it +/// unsafe { +/// embedded_alloc::init!(HEAP, 1024); +/// } +/// let mut xs = Vec::new(); +/// // ... +/// } +/// ``` +#[macro_export] +macro_rules! init { + ($heap:ident, $size:expr) => { + static mut HEAP_MEM: [::core::mem::MaybeUninit; $size] = + [::core::mem::MaybeUninit::uninit(); $size]; + $heap.init(&raw mut HEAP_MEM as usize, $size) + }; +} From 7d9604211e7bba7ca2bbb9c5c633b725cdfb925b Mon Sep 17 00:00:00 2001 From: Jalon Wong Date: Mon, 25 Aug 2025 13:00:13 -0500 Subject: [PATCH 2/3] Add a flag to prevent duplicate initialization --- CHANGELOG.md | 4 ++++ src/lib.rs | 7 +++++++ src/llff.rs | 38 +++++++++++++++++++++++--------------- src/tlsf.rs | 32 ++++++++++++++++++++------------ 4 files changed, 54 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e23d086..a5ac15b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Added a `init` macro to make initialization easier. +### Changed + +- The `init` function will panic if it's called more than once or with `size == 0`. + ## [v0.6.0] - 2024-09-01 ### Added diff --git a/src/lib.rs b/src/lib.rs index 860c82b..0b6bd29 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -30,6 +30,13 @@ pub use tlsf::Heap as TlsfHeap; /// It internally calls `Heap::init(...)` on the heap, /// so `Heap::init(...)` should not be called directly if this macro is used. /// +/// # Panics +/// +/// This macro will panic if either of the following are true: +/// +/// - this function is called more than ONCE. +/// - `size == 0`. +/// /// # Example /// /// ```rust diff --git a/src/llff.rs b/src/llff.rs index a07e73c..aae2485 100644 --- a/src/llff.rs +++ b/src/llff.rs @@ -7,7 +7,7 @@ use linked_list_allocator::Heap as LLHeap; /// A linked list first fit heap. pub struct Heap { - heap: Mutex>, + heap: Mutex>, } impl Heap { @@ -17,7 +17,7 @@ impl Heap { /// [`init`](Self::init) method before using the allocator. pub const fn empty() -> Heap { Heap { - heap: Mutex::new(RefCell::new(LLHeap::empty())), + heap: Mutex::new(RefCell::new((LLHeap::empty(), false))), } } @@ -41,34 +41,42 @@ impl Heap { /// /// # Safety /// - /// Obey these or Bad Stuff will happen. + /// This function is safe if the following invariants hold: /// - /// - This function must be called exactly ONCE. - /// - `size > 0` + /// - `start_addr` points to valid memory. + /// - `size` is correct. + /// + /// # Panics + /// + /// This function will panic if either of the following are true: + /// + /// - this function is called more than ONCE. + /// - `size == 0`. pub unsafe fn init(&self, start_addr: usize, size: usize) { + assert!(size > 0); critical_section::with(|cs| { - self.heap - .borrow(cs) - .borrow_mut() - .init(start_addr as *mut u8, size); + let mut heap = self.heap.borrow_ref_mut(cs); + assert!(!heap.1); + heap.1 = true; + heap.0.init(start_addr as *mut u8, size); }); } /// Returns an estimate of the amount of bytes in use. pub fn used(&self) -> usize { - critical_section::with(|cs| self.heap.borrow(cs).borrow_mut().used()) + critical_section::with(|cs| self.heap.borrow_ref_mut(cs).0.used()) } /// Returns an estimate of the amount of bytes available. pub fn free(&self) -> usize { - critical_section::with(|cs| self.heap.borrow(cs).borrow_mut().free()) + critical_section::with(|cs| self.heap.borrow_ref_mut(cs).0.free()) } fn alloc(&self, layout: Layout) -> Option> { critical_section::with(|cs| { self.heap - .borrow(cs) - .borrow_mut() + .borrow_ref_mut(cs) + .0 .allocate_first_fit(layout) .ok() }) @@ -77,8 +85,8 @@ impl Heap { unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { critical_section::with(|cs| { self.heap - .borrow(cs) - .borrow_mut() + .borrow_ref_mut(cs) + .0 .deallocate(NonNull::new_unchecked(ptr), layout) }); } diff --git a/src/tlsf.rs b/src/tlsf.rs index 75f5255..37935d1 100644 --- a/src/tlsf.rs +++ b/src/tlsf.rs @@ -10,7 +10,7 @@ type TlsfHeap = Tlsf<'static, usize, usize, { usize::BITS as usize }, { usize::B /// A two-Level segregated fit heap. pub struct Heap { - heap: Mutex>, + heap: Mutex>, } impl Heap { @@ -20,7 +20,7 @@ impl Heap { /// [`init`](Self::init) method before using the allocator. pub const fn empty() -> Heap { Heap { - heap: Mutex::new(RefCell::new(ConstDefault::DEFAULT)), + heap: Mutex::new(RefCell::new((ConstDefault::DEFAULT, false))), } } @@ -44,29 +44,37 @@ impl Heap { /// /// # Safety /// - /// Obey these or Bad Stuff will happen. + /// This function is safe if the following invariants hold: /// - /// - This function must be called exactly ONCE. - /// - `size > 0` + /// - `start_addr` points to valid memory. + /// - `size` is correct. + /// + /// # Panics + /// + /// This function will panic if either of the following are true: + /// + /// - this function is called more than ONCE. + /// - `size == 0`. pub unsafe fn init(&self, start_addr: usize, size: usize) { + assert!(size > 0); critical_section::with(|cs| { + let mut heap = self.heap.borrow_ref_mut(cs); + assert!(!heap.1); + heap.1 = true; let block: &[u8] = core::slice::from_raw_parts(start_addr as *const u8, size); - self.heap - .borrow(cs) - .borrow_mut() - .insert_free_block_ptr(block.into()); + heap.0.insert_free_block_ptr(block.into()); }); } fn alloc(&self, layout: Layout) -> Option> { - critical_section::with(|cs| self.heap.borrow(cs).borrow_mut().allocate(layout)) + critical_section::with(|cs| self.heap.borrow_ref_mut(cs).0.allocate(layout)) } unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { critical_section::with(|cs| { self.heap - .borrow(cs) - .borrow_mut() + .borrow_ref_mut(cs) + .0 .deallocate(NonNull::new_unchecked(ptr), layout.align()) }) } From 61a268565e8878e7807b8eb4fa963d715df51528 Mon Sep 17 00:00:00 2001 From: Jalon Wong Date: Sun, 31 Aug 2025 11:50:21 -0500 Subject: [PATCH 3/3] Update CHANGELOG.md Co-authored-by: Zeeshan Ali Khan --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a5ac15b..0921f25 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Changed -- The `init` function will panic if it's called more than once or with `size == 0`. +- The `Heap::init` methods now panic if they're called more than once or with `size == 0`. ## [v0.6.0] - 2024-09-01