Skip to content

Commit bdd18cb

Browse files
committed
Add TLSF memory usage API and update API naming
- Added `Heap::free` and `Heap::used` for the TLSF heap. - Added example showcasing regular heap exhaustion and example which shows the memory usage API.
1 parent c3d2764 commit bdd18cb

File tree

7 files changed

+147
-11
lines changed

7 files changed

+147
-11
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1010
### Added
1111

1212
- Added a `init` macro to make initialization easier.
13+
- Added `Heap::free` and `Heap::used` for the TLSF heap.
1314

1415
### Changed
1516

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ llff = ["linked_list_allocator"]
3535
[dependencies]
3636
critical-section = "1.0"
3737
linked_list_allocator = { version = "0.10.5", default-features = false, optional = true }
38-
rlsf = { version = "0.2.1", default-features = false, optional = true }
38+
rlsf = { version = "0.2.1", default-features = false, features = ["unstable"], optional = true }
3939
const-default = { version = "1.0.0", default-features = false, optional = true }
4040

4141
[dev-dependencies]

examples/allocator_api.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,5 +35,5 @@ fn main() -> ! {
3535
#[panic_handler]
3636
fn panic(info: &PanicInfo) -> ! {
3737
defmt::error!("{}", info);
38-
semihosting::process::exit(0);
38+
semihosting::process::exit(-1);
3939
}

examples/exhaustion.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//! Example which shows behavior on pool exhaustion. It simply panics.
2+
#![no_std]
3+
#![no_main]
4+
5+
extern crate alloc;
6+
7+
use cortex_m as _;
8+
use cortex_m_rt::entry;
9+
use defmt::Debug2Format;
10+
use defmt_semihosting as _;
11+
12+
use core::panic::PanicInfo;
13+
use embedded_alloc::TlsfHeap as Heap;
14+
15+
#[global_allocator]
16+
static HEAP: Heap = Heap::empty();
17+
18+
#[entry]
19+
fn main() -> ! {
20+
// Initialize the allocator BEFORE you use it
21+
unsafe {
22+
embedded_alloc::init!(HEAP, 16);
23+
}
24+
25+
let _vec = alloc::vec![0; 16];
26+
27+
defmt::error!("unexpected vector allocation success");
28+
29+
// Panic is expected here.
30+
semihosting::process::exit(-1);
31+
}
32+
33+
#[panic_handler]
34+
fn panic(info: &PanicInfo) -> ! {
35+
defmt::warn!("received expected heap exhaustion panic");
36+
defmt::warn!("{}: {}", info, Debug2Format(&info.message()));
37+
semihosting::process::exit(0);
38+
}

examples/global_alloc.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,5 +38,5 @@ fn main() -> ! {
3838
#[panic_handler]
3939
fn panic(info: &PanicInfo) -> ! {
4040
defmt::error!("{}", info);
41-
semihosting::process::exit(0);
41+
semihosting::process::exit(-1);
4242
}

examples/track_usage.rs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#![no_std]
2+
#![no_main]
3+
4+
extern crate alloc;
5+
6+
use cortex_m as _;
7+
use cortex_m_rt::entry;
8+
use defmt::Debug2Format;
9+
use defmt_semihosting as _;
10+
11+
use core::{mem::MaybeUninit, panic::PanicInfo};
12+
use embedded_alloc::TlsfHeap as Heap;
13+
//use embedded_alloc::LlffHeap as Heap;
14+
15+
#[global_allocator]
16+
static HEAP: Heap = Heap::empty();
17+
18+
#[entry]
19+
fn main() -> ! {
20+
// Initialize the allocator BEFORE you use it
21+
const HEAP_SIZE: usize = 4096;
22+
static mut HEAP_MEM: [MaybeUninit<u8>; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE];
23+
unsafe { HEAP.init(&raw mut HEAP_MEM as usize, HEAP_SIZE) }
24+
25+
let mut long_vec = alloc::vec::Vec::new();
26+
let mut free_memory = HEAP_SIZE;
27+
// Keep allocating until we are getting low on memory. It doesn't have to end in a panic.
28+
while free_memory > 512 {
29+
defmt::info!(
30+
"{} of {} heap memory allocated so far...",
31+
HEAP_SIZE - free_memory,
32+
HEAP_SIZE
33+
);
34+
long_vec.push([1; 16].to_vec());
35+
free_memory = HEAP.free_memory();
36+
}
37+
38+
drop(long_vec);
39+
40+
defmt::warn!(
41+
"{} of {} heap memory are allocated after drop",
42+
HEAP_SIZE - HEAP.free_memory(),
43+
HEAP_SIZE
44+
);
45+
46+
// Panic is expected here.
47+
semihosting::process::exit(0);
48+
}
49+
50+
#[panic_handler]
51+
fn panic(info: &PanicInfo) -> ! {
52+
defmt::error!("{}: {}", info, Debug2Format(&info.message()));
53+
semihosting::process::exit(-1);
54+
}

src/tlsf.rs

Lines changed: 51 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,20 @@ use rlsf::Tlsf;
88

99
type TlsfHeap = Tlsf<'static, usize, usize, { usize::BITS as usize }, { usize::BITS as usize }>;
1010

11+
struct Inner {
12+
tlsf: TlsfHeap,
13+
initialized: bool,
14+
raw_block: Option<NonNull<[u8]>>,
15+
raw_block_size: usize,
16+
}
17+
18+
// Safety: The whole inner type is wrapped by a [Mutex].
19+
unsafe impl Sync for Inner {}
20+
unsafe impl Send for Inner {}
21+
1122
/// A two-Level segregated fit heap.
1223
pub struct Heap {
13-
heap: Mutex<RefCell<(TlsfHeap, bool)>>,
24+
heap: Mutex<RefCell<Inner>>,
1425
}
1526

1627
impl Heap {
@@ -20,7 +31,12 @@ impl Heap {
2031
/// [`init`](Self::init) method before using the allocator.
2132
pub const fn empty() -> Heap {
2233
Heap {
23-
heap: Mutex::new(RefCell::new((ConstDefault::DEFAULT, false))),
34+
heap: Mutex::new(RefCell::new(Inner {
35+
tlsf: ConstDefault::DEFAULT,
36+
initialized: false,
37+
raw_block: None,
38+
raw_block_size: 0,
39+
})),
2440
}
2541
}
2642

@@ -59,25 +75,52 @@ impl Heap {
5975
assert!(size > 0);
6076
critical_section::with(|cs| {
6177
let mut heap = self.heap.borrow_ref_mut(cs);
62-
assert!(!heap.1);
63-
heap.1 = true;
64-
let block: &[u8] = core::slice::from_raw_parts(start_addr as *const u8, size);
65-
heap.0.insert_free_block_ptr(block.into());
78+
assert!(!heap.initialized);
79+
heap.initialized = true;
80+
let block: NonNull<[u8]> =
81+
NonNull::slice_from_raw_parts(NonNull::new_unchecked(start_addr as *mut u8), size);
82+
heap.tlsf.insert_free_block_ptr(block);
83+
heap.raw_block = Some(block);
84+
heap.raw_block_size = size;
6685
});
6786
}
6887

6988
fn alloc(&self, layout: Layout) -> Option<NonNull<u8>> {
70-
critical_section::with(|cs| self.heap.borrow_ref_mut(cs).0.allocate(layout))
89+
critical_section::with(|cs| self.heap.borrow_ref_mut(cs).tlsf.allocate(layout))
7190
}
7291

7392
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
7493
critical_section::with(|cs| {
7594
self.heap
7695
.borrow_ref_mut(cs)
77-
.0
96+
.tlsf
7897
.deallocate(NonNull::new_unchecked(ptr), layout.align())
7998
})
8099
}
100+
101+
/// Get the amount of bytes used by the allocator.
102+
pub fn used(&self) -> usize {
103+
critical_section::with(|cs| {
104+
self.heap.borrow_ref_mut(cs).raw_block_size - self.free_with_cs(cs)
105+
})
106+
}
107+
108+
/// Get the amount of free bytes in the allocator.
109+
pub fn free(&self) -> usize {
110+
critical_section::with(|cs| self.free_with_cs(cs))
111+
}
112+
113+
fn free_with_cs(&self, cs: critical_section::CriticalSection) -> usize {
114+
let inner_mut = self.heap.borrow_ref_mut(cs);
115+
unsafe {
116+
inner_mut
117+
.tlsf
118+
.iter_blocks(inner_mut.raw_block.unwrap())
119+
.filter(|block_info| !block_info.is_occupied())
120+
.map(|block_info| block_info.max_payload_size())
121+
.sum::<usize>()
122+
}
123+
}
81124
}
82125

83126
unsafe impl GlobalAlloc for Heap {

0 commit comments

Comments
 (0)