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) + }; +} diff --git a/src/llff.rs b/src/llff.rs index a07e73c..bcbb52f 100644 --- a/src/llff.rs +++ b/src/llff.rs @@ -1,5 +1,5 @@ use core::alloc::{GlobalAlloc, Layout}; -use core::cell::RefCell; +use core::cell::{Cell, RefCell}; use core::ptr::{self, NonNull}; use critical_section::Mutex; @@ -8,6 +8,7 @@ use linked_list_allocator::Heap as LLHeap; /// A linked list first fit heap. pub struct Heap { heap: Mutex>, + once_flag: Mutex>, } impl Heap { @@ -18,6 +19,7 @@ impl Heap { pub const fn empty() -> Heap { Heap { heap: Mutex::new(RefCell::new(LLHeap::empty())), + once_flag: Mutex::new(Cell::new(false)), } } @@ -46,7 +48,12 @@ impl Heap { /// - This function must be called exactly ONCE. /// - `size > 0` pub unsafe fn init(&self, start_addr: usize, size: usize) { + assert!(size > 0); critical_section::with(|cs| { + let once_flag = self.once_flag.borrow(cs); + assert!(!once_flag.get()); + once_flag.set(true); + self.heap .borrow(cs) .borrow_mut() diff --git a/src/tlsf.rs b/src/tlsf.rs index 75f5255..a08f66a 100644 --- a/src/tlsf.rs +++ b/src/tlsf.rs @@ -1,5 +1,5 @@ use core::alloc::{GlobalAlloc, Layout}; -use core::cell::RefCell; +use core::cell::{Cell, RefCell}; use core::ptr::{self, NonNull}; use const_default::ConstDefault; @@ -11,6 +11,7 @@ type TlsfHeap = Tlsf<'static, usize, usize, { usize::BITS as usize }, { usize::B /// A two-Level segregated fit heap. pub struct Heap { heap: Mutex>, + once_flag: Mutex>, } impl Heap { @@ -21,6 +22,7 @@ impl Heap { pub const fn empty() -> Heap { Heap { heap: Mutex::new(RefCell::new(ConstDefault::DEFAULT)), + once_flag: Mutex::new(Cell::new(false)), } } @@ -49,7 +51,12 @@ impl Heap { /// - This function must be called exactly ONCE. /// - `size > 0` pub unsafe fn init(&self, start_addr: usize, size: usize) { + assert!(size > 0); critical_section::with(|cs| { + let once_flag = self.once_flag.borrow(cs); + assert!(!once_flag.get()); + once_flag.set(true); + let block: &[u8] = core::slice::from_raw_parts(start_addr as *const u8, size); self.heap .borrow(cs)