From eb87f1a58450e76702676575273cedce8c8c915b Mon Sep 17 00:00:00 2001 From: Jalon Wong Date: Thu, 14 Aug 2025 14:30:50 -0500 Subject: [PATCH 1/2] 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 3ff3036b7b5150d9c7c0951050d05e92f3414e18 Mon Sep 17 00:00:00 2001 From: Jalon Wong Date: Mon, 25 Aug 2025 13:00:13 -0500 Subject: [PATCH 2/2] Add a flag to prevent duplicate initialization --- CHANGELOG.md | 1 + src/lib.rs | 7 +++++++ src/llff.rs | 31 ++++++++++++++++--------------- src/tlsf.rs | 25 +++++++++++++------------ 4 files changed, 37 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e23d086..50a92c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Added - Added a `init` macro to make initialization easier. +- Added a flag to prevent duplicate initialization ## [v0.6.0] - 2024-09-01 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..4b721d6 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,35 @@ impl Heap { /// /// # Safety /// - /// Obey these or Bad Stuff will happen. + /// This function will panic if either of the following are true: /// - /// - This function must be called exactly ONCE. - /// - `size > 0` + /// - 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 +78,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..6107b4f 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,30 @@ impl Heap { /// /// # Safety /// - /// Obey these or Bad Stuff will happen. + /// This function will panic if either of the following are true: /// - /// - This function must be called exactly ONCE. - /// - `size > 0` + /// - 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()) }) }