iceoryx2_bb_memory/
bump_allocator.rs

1// Copyright (c) 2023 Contributors to the Eclipse Foundation
2//
3// See the NOTICE file(s) distributed with this work for additional
4// information regarding copyright ownership.
5//
6// This program and the accompanying materials are made available under the
7// terms of the Apache Software License 2.0 which is available at
8// https://www.apache.org/licenses/LICENSE-2.0, or the MIT license
9// which is available at https://opensource.org/licenses/MIT.
10//
11// SPDX-License-Identifier: Apache-2.0 OR MIT
12
13//! A **threadsafe** and **lock-free** bump allocator which implements the [`BaseAllocator`].
14//! It can be allocated with [`BumpAllocator::allocate()`] but [`BumpAllocator::deallocate`]
15//! deallocate all allocated chunks. See this: `https://os.phil-opp.com/allocator-designs/`
16//! for more details.
17
18use core::{fmt::Display, ptr::NonNull};
19
20use iceoryx2_bb_concurrency::atomic::AtomicUsize;
21use iceoryx2_bb_concurrency::atomic::Ordering;
22use iceoryx2_bb_elementary::math::align;
23use iceoryx2_log::fail;
24
25pub use iceoryx2_bb_elementary_traits::allocator::{AllocationError, BaseAllocator};
26
27#[derive(Debug)]
28pub struct BumpAllocator {
29    pub(crate) start: usize,
30    size: usize,
31    current_position: AtomicUsize,
32}
33
34impl Display for BumpAllocator {
35    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
36        write!(
37            f,
38            "BumpAllocator {{ start: {}, size: {}, current_position: {} }}",
39            self.start,
40            self.size,
41            self.current_position
42                .load(core::sync::atomic::Ordering::Relaxed)
43        )
44    }
45}
46
47impl BumpAllocator {
48    pub fn new(ptr: NonNull<u8>, size: usize) -> Self {
49        Self {
50            start: ptr.as_ptr() as usize,
51            size,
52            current_position: AtomicUsize::new(0),
53        }
54    }
55
56    pub fn start_address(&self) -> usize {
57        self.start
58    }
59
60    pub fn used_space(&self) -> usize {
61        self.current_position.load(Ordering::Relaxed)
62    }
63
64    pub fn free_space(&self) -> usize {
65        self.size - self.used_space()
66    }
67
68    pub fn total_space(&self) -> usize {
69        self.size
70    }
71}
72
73impl BaseAllocator for BumpAllocator {
74    fn allocate(&self, layout: core::alloc::Layout) -> Result<NonNull<[u8]>, AllocationError> {
75        let msg = "Unable to allocate chunk with";
76        let mut aligned_position;
77
78        if layout.size() == 0 {
79            fail!(from self, with AllocationError::SizeIsZero,
80                "{} {:?} since the requested size was zero.", msg, layout);
81        }
82
83        let mut current_position = self
84            .current_position
85            .load(core::sync::atomic::Ordering::Relaxed);
86        loop {
87            aligned_position = align(self.start + current_position, layout.align()) - self.start;
88            if aligned_position + layout.size() > self.size {
89                fail!(from self, with AllocationError::OutOfMemory,
90                    "{} {:?} since there is not enough memory available.", msg, layout);
91            }
92
93            match self.current_position.compare_exchange_weak(
94                current_position,
95                aligned_position + layout.size(),
96                core::sync::atomic::Ordering::Relaxed,
97                core::sync::atomic::Ordering::Relaxed,
98            ) {
99                Ok(_) => break,
100                Err(v) => current_position = v,
101            }
102        }
103
104        Ok(unsafe {
105            NonNull::new_unchecked(core::ptr::slice_from_raw_parts_mut(
106                (self.start + aligned_position) as *mut u8,
107                layout.size(),
108            ))
109        })
110    }
111
112    unsafe fn deallocate(&self, _ptr: NonNull<u8>, _layout: core::alloc::Layout) {
113        self.current_position
114            .store(0, core::sync::atomic::Ordering::Relaxed);
115    }
116}