Skip to main content

iceoryx2_cal/shm_allocator/
pool_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
13use core::{alloc::Layout, ptr::NonNull};
14
15use crate::shm_allocator::{ShmAllocator, ShmAllocatorConfig};
16
17use iceoryx2_bb_concurrency::atomic::AtomicUsize;
18use iceoryx2_bb_concurrency::atomic::Ordering;
19use iceoryx2_bb_elementary_traits::allocator::BaseAllocator;
20use iceoryx2_log::fail;
21
22use super::{
23    AllocationStrategy, PointerOffset, SharedMemorySetupHint, ShmAllocationError,
24    ShmAllocatorInitError,
25};
26
27#[derive(Clone, Copy, Debug)]
28pub struct Config {
29    pub bucket_layout: Layout,
30}
31
32impl Default for Config {
33    fn default() -> Self {
34        Self {
35            bucket_layout: unsafe { Layout::from_size_align_unchecked(1024, 8) },
36        }
37    }
38}
39
40impl ShmAllocatorConfig for Config {}
41
42#[derive(Debug)]
43pub struct PoolAllocator {
44    allocator: iceoryx2_bb_memory::pool_allocator::PoolAllocator,
45    // is even with absolut base address relocatable since every process acquire and return
46    // the same relative offset which map then to the same absolut base address
47    // the allocator only manages a range of numbers
48    base_address: usize,
49    max_supported_alignment_by_memory: usize,
50    number_of_used_buckets: AtomicUsize,
51}
52
53impl PoolAllocator {
54    pub fn bucket_size(&self) -> usize {
55        self.allocator.bucket_size()
56    }
57
58    pub fn number_of_buckets(&self) -> u32 {
59        self.allocator.number_of_buckets()
60    }
61
62    /// # Safety
63    ///
64    ///  * provided [`PointerOffset`] must be allocated with [`PoolAllocator::allocate()`]
65    pub unsafe fn deallocate_bucket(&self, offset: PointerOffset) {
66        self.number_of_used_buckets.fetch_sub(1, Ordering::Relaxed);
67        unsafe {
68            self.allocator.deallocate_bucket(NonNull::new_unchecked(
69                (offset.offset() + self.allocator.start_address()) as *mut u8,
70            ));
71        }
72    }
73}
74
75impl ShmAllocator for PoolAllocator {
76    type Configuration = Config;
77
78    fn resize_hint(
79        &self,
80        layout: Layout,
81        strategy: AllocationStrategy,
82    ) -> SharedMemorySetupHint<Self::Configuration> {
83        let current_layout = unsafe {
84            Layout::from_size_align_unchecked(
85                self.allocator.bucket_size(),
86                self.allocator.max_alignment(),
87            )
88        };
89
90        let adjusted_number_of_buckets = if self.number_of_used_buckets.load(Ordering::Relaxed)
91            == self.number_of_buckets() as usize
92        {
93            match strategy {
94                AllocationStrategy::BestFit => self.allocator.number_of_buckets() + 1,
95                AllocationStrategy::PowerOfTwo => {
96                    (self.allocator.number_of_buckets() + 1).next_power_of_two()
97                }
98                AllocationStrategy::Static => self.allocator.number_of_buckets(),
99            }
100        } else {
101            self.number_of_buckets()
102        };
103
104        let adjusted_layout =
105            if current_layout.size() < layout.size() || current_layout.align() < layout.align() {
106                match strategy {
107                    AllocationStrategy::Static => current_layout,
108                    AllocationStrategy::BestFit => unsafe {
109                        let align = layout.align().max(current_layout.align());
110                        let size = layout
111                            .size()
112                            .max(current_layout.size())
113                            .next_multiple_of(align);
114                        Layout::from_size_align_unchecked(size, align)
115                    },
116                    AllocationStrategy::PowerOfTwo => unsafe {
117                        let align = layout
118                            .align()
119                            .max(current_layout.align())
120                            .next_power_of_two();
121                        let size = layout
122                            .size()
123                            .max(current_layout.size())
124                            .next_power_of_two()
125                            .next_multiple_of(align);
126                        Layout::from_size_align_unchecked(size, align)
127                    },
128                }
129            } else {
130                current_layout
131            };
132
133        Self::initial_setup_hint(adjusted_layout, adjusted_number_of_buckets as usize)
134    }
135
136    fn initial_setup_hint(
137        max_chunk_layout: Layout,
138        max_number_of_chunks: usize,
139    ) -> SharedMemorySetupHint<Self::Configuration> {
140        SharedMemorySetupHint {
141            payload_size: max_chunk_layout.size() * max_number_of_chunks,
142            config: Self::Configuration {
143                bucket_layout: max_chunk_layout,
144            },
145        }
146    }
147
148    fn management_size(memory_size: usize, config: &Self::Configuration) -> usize {
149        iceoryx2_bb_memory::pool_allocator::PoolAllocator::memory_size(
150            config.bucket_layout,
151            memory_size,
152        )
153    }
154
155    fn relative_start_address(&self) -> usize {
156        self.allocator.start_address() - self.base_address
157    }
158
159    unsafe fn new_uninit(
160        max_supported_alignment_by_memory: usize,
161        managed_memory: NonNull<[u8]>,
162        config: &Self::Configuration,
163    ) -> Self {
164        Self {
165            allocator: unsafe {
166                iceoryx2_bb_memory::pool_allocator::PoolAllocator::new_uninit(
167                    config.bucket_layout,
168                    NonNull::new_unchecked(managed_memory.as_ptr() as *mut u8),
169                    managed_memory.len(),
170                )
171            },
172            base_address: (managed_memory.as_ptr() as *mut u8) as usize,
173            max_supported_alignment_by_memory,
174            number_of_used_buckets: AtomicUsize::new(0),
175        }
176    }
177
178    fn max_alignment(&self) -> usize {
179        self.allocator.max_alignment()
180    }
181
182    unsafe fn init<Allocator: BaseAllocator>(
183        &mut self,
184        mgmt_allocator: &Allocator,
185    ) -> Result<(), ShmAllocatorInitError> {
186        let msg = "Unable to initialize allocator";
187        if self.max_supported_alignment_by_memory < self.max_alignment() {
188            fail!(from self, with ShmAllocatorInitError::MaxSupportedMemoryAlignmentInsufficient,
189                "{} since the required alignment {} exceeds the maximum supported alignment {} of the memory.",
190                msg, self.max_alignment(), self.max_supported_alignment_by_memory);
191        }
192
193        fail!(from self, when unsafe { self.allocator.init(mgmt_allocator)  },
194            with ShmAllocatorInitError::AllocationFailed,
195            "{} since the allocation of the allocator managment memory failed.", msg);
196
197        Ok(())
198    }
199
200    fn unique_id() -> u8 {
201        0
202    }
203
204    unsafe fn allocate(&self, layout: Layout) -> Result<PointerOffset, ShmAllocationError> {
205        let msg = "Unable to allocate memory";
206        if layout.align() > self.max_alignment() {
207            fail!(from self, with ShmAllocationError::ExceedsMaxSupportedAlignment,
208                "{} since an alignment of {} exceeds the maximum supported alignment of {}.",
209                msg, layout.align(), self.max_alignment());
210        }
211
212        let chunk = fail!(from self, when self.allocator.allocate(layout), "{}.", msg);
213        self.number_of_used_buckets.fetch_add(1, Ordering::Relaxed);
214        Ok(PointerOffset::new(
215            (chunk.as_ptr() as *const u8) as usize - self.allocator.start_address(),
216        ))
217    }
218
219    unsafe fn deallocate(&self, offset: PointerOffset, _layout: Layout) {
220        unsafe {
221            self.deallocate_bucket(offset);
222        }
223    }
224}