iceoryx2/port/details/
data_segment.rs

1// Copyright (c) 2023 - 2024 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;
14
15use iceoryx2_bb_log::fail;
16use iceoryx2_bb_system_types::file_name::FileName;
17use iceoryx2_cal::{
18    event::NamedConceptBuilder,
19    resizable_shared_memory::*,
20    shared_memory::{
21        SharedMemory, SharedMemoryBuilder, SharedMemoryCreateError, SharedMemoryForPoolAllocator,
22        SharedMemoryOpenError, ShmPointer,
23    },
24    shm_allocator::{
25        self, pool_allocator::PoolAllocator, AllocationError, AllocationStrategy, PointerOffset,
26        SegmentId, ShmAllocationError,
27    },
28};
29
30use crate::{
31    config,
32    service::{
33        self,
34        config_scheme::{data_segment_config, resizable_data_segment_config},
35    },
36};
37
38/// Defines the data segment type of a zero copy capable sender port.
39#[repr(C)]
40#[derive(Debug, Clone, Copy, Eq, PartialEq)]
41pub enum DataSegmentType {
42    /// The data segment can be resized if no more memory is available.
43    Dynamic,
44    /// The data segment is allocated once. If it is out-of-memory no reallocation will occur.
45    Static,
46}
47
48impl DataSegmentType {
49    pub(crate) fn new_from_allocation_strategy(v: AllocationStrategy) -> Self {
50        match v {
51            AllocationStrategy::Static => DataSegmentType::Static,
52            _ => DataSegmentType::Dynamic,
53        }
54    }
55}
56
57#[derive(Debug)]
58enum MemoryType<Service: service::Service> {
59    Static(Service::SharedMemory),
60    Dynamic(Service::ResizableSharedMemory),
61}
62
63#[derive(Debug)]
64pub(crate) struct DataSegment<Service: service::Service> {
65    memory: MemoryType<Service>,
66}
67
68impl<Service: service::Service> DataSegment<Service> {
69    pub(crate) fn create_static_segment(
70        segment_name: &FileName,
71        chunk_layout: Layout,
72        global_config: &config::Config,
73        number_of_chunks: usize,
74    ) -> Result<Self, SharedMemoryCreateError> {
75        let allocator_config = shm_allocator::pool_allocator::Config {
76            bucket_layout: chunk_layout,
77        };
78        let msg = "Unable to create the static data segment since the underlying shared memory could not be created.";
79        let origin = "DataSegment::create_static_segment()";
80
81        let segment_config = data_segment_config::<Service>(global_config);
82        let memory = fail!(from origin,
83                                when <<Service::SharedMemory as SharedMemory<PoolAllocator>>::Builder as NamedConceptBuilder<
84                                Service::SharedMemory,
85                                    >>::new(segment_name)
86                                    .config(&segment_config)
87                                    .size(chunk_layout.size() * number_of_chunks + chunk_layout.align() - 1)
88                                    .create(&allocator_config),
89                                "{msg}");
90
91        Ok(Self {
92            memory: MemoryType::Static(memory),
93        })
94    }
95
96    pub(crate) fn create_dynamic_segment(
97        segment_name: &FileName,
98        chunk_layout: Layout,
99        global_config: &config::Config,
100        number_of_chunks: usize,
101        allocation_strategy: AllocationStrategy,
102    ) -> Result<Self, SharedMemoryCreateError> {
103        let msg = "Unable to create the dynamic data segment since the underlying shared memory could not be created.";
104        let origin = "DataSegment::create_dynamic_segment()";
105
106        let segment_config = resizable_data_segment_config::<Service>(global_config);
107        let memory = fail!(from origin,
108                    when <<Service::ResizableSharedMemory as ResizableSharedMemory<
109                        PoolAllocator,
110                        Service::SharedMemory,
111                    >>::MemoryBuilder as NamedConceptBuilder<Service::ResizableSharedMemory>>::new(
112                        segment_name,
113                    )
114                    .config(&segment_config)
115                    .max_number_of_chunks_hint(number_of_chunks)
116                    .max_chunk_layout_hint(chunk_layout)
117                    .allocation_strategy(allocation_strategy)
118                    .create(),
119                    "{msg}");
120
121        Ok(Self {
122            memory: MemoryType::Dynamic(memory),
123        })
124    }
125
126    pub(crate) fn allocate(&self, layout: Layout) -> Result<ShmPointer, ShmAllocationError> {
127        let msg = "Unable to allocate memory from the data segment";
128        match &self.memory {
129            MemoryType::Static(memory) => Ok(fail!(from self, when memory.allocate(layout),
130                                            "{msg}.")),
131            MemoryType::Dynamic(memory) => match memory.allocate(layout) {
132                Ok(ptr) => Ok(ptr),
133                Err(ResizableShmAllocationError::ShmAllocationError(e)) => {
134                    fail!(from self, with e,
135                        "{msg} caused by {:?}.", e);
136                }
137                Err(ResizableShmAllocationError::MaxReallocationsReached) => {
138                    fail!(from self,
139                        with ShmAllocationError::AllocationError(AllocationError::OutOfMemory),
140                        "{msg} since the maxmimum number of reallocations was reached. Try to provide initial_max_slice_len({}) as hint when creating the publisher to have a more fitting initial setup.", layout.size());
141                }
142                Err(ResizableShmAllocationError::SharedMemoryCreateError(e)) => {
143                    fail!(from self,
144                        with ShmAllocationError::AllocationError(AllocationError::InternalError),
145                        "{msg} since the shared memory segment creation failed while resizing the memory due to ({:?}).", e);
146                }
147            },
148        }
149    }
150
151    pub(crate) unsafe fn deallocate_bucket(&self, offset: PointerOffset) {
152        match &self.memory {
153            MemoryType::Static(memory) => memory.deallocate_bucket(offset),
154            MemoryType::Dynamic(memory) => memory.deallocate_bucket(offset),
155        }
156    }
157
158    pub(crate) fn bucket_size(&self, segment_id: SegmentId) -> usize {
159        match &self.memory {
160            MemoryType::Static(memory) => memory.bucket_size(),
161            MemoryType::Dynamic(memory) => memory.bucket_size(segment_id),
162        }
163    }
164
165    pub(crate) fn max_number_of_segments(data_segment_type: DataSegmentType) -> u8 {
166        match data_segment_type {
167            DataSegmentType::Static => 1,
168            DataSegmentType::Dynamic => {
169                (Service::ResizableSharedMemory::max_number_of_reallocations() - 1) as u8
170            }
171        }
172    }
173}
174
175#[derive(Debug)]
176enum MemoryViewType<Service: service::Service> {
177    Static(Service::SharedMemory),
178    Dynamic(
179        <Service::ResizableSharedMemory as ResizableSharedMemory<
180            PoolAllocator,
181            Service::SharedMemory,
182        >>::View,
183    ),
184}
185
186#[derive(Debug)]
187pub(crate) struct DataSegmentView<Service: service::Service> {
188    memory: MemoryViewType<Service>,
189}
190
191impl<Service: service::Service> DataSegmentView<Service> {
192    pub(crate) fn open_static_segment(
193        segment_name: &FileName,
194        global_config: &config::Config,
195    ) -> Result<Self, SharedMemoryOpenError> {
196        let origin = "DataSegment::open()";
197        let msg =
198            "Unable to open data segment since the underlying shared memory could not be opened.";
199
200        let segment_config = data_segment_config::<Service>(global_config);
201        let memory = fail!(from origin,
202                            when <Service::SharedMemory as SharedMemory<PoolAllocator>>::
203                                Builder::new(segment_name)
204                                .config(&segment_config)
205                                .timeout(global_config.global.service.creation_timeout)
206                                .open(),
207                            "{msg}");
208
209        Ok(Self {
210            memory: MemoryViewType::Static(memory),
211        })
212    }
213
214    pub(crate) fn open_dynamic_segment(
215        segment_name: &FileName,
216        global_config: &config::Config,
217    ) -> Result<Self, SharedMemoryOpenError> {
218        let origin = "DataSegment::open()";
219        let msg =
220            "Unable to open data segment since the underlying shared memory could not be opened.";
221
222        let segment_config = resizable_data_segment_config::<Service>(global_config);
223        let memory = fail!(from origin,
224                    when <<Service::ResizableSharedMemory as ResizableSharedMemory<
225                        PoolAllocator,
226                        Service::SharedMemory,
227                    >>::ViewBuilder as NamedConceptBuilder<Service::ResizableSharedMemory>>::new(
228                        segment_name,
229                    )
230                    .config(&segment_config)
231                    .open(),
232                    "{msg}");
233
234        Ok(Self {
235            memory: MemoryViewType::Dynamic(memory),
236        })
237    }
238
239    pub(crate) fn register_and_translate_offset(
240        &self,
241        offset: PointerOffset,
242    ) -> Result<usize, SharedMemoryOpenError> {
243        match &self.memory {
244            MemoryViewType::Static(memory) => Ok(offset.offset() + memory.payload_start_address()),
245            MemoryViewType::Dynamic(memory) => unsafe {
246                match memory.register_and_translate_offset(offset) {
247                    Ok(ptr) => Ok(ptr as usize),
248                    Err(e) => {
249                        fail!(from self, with e,
250                            "Failed to register and translate pointer due to a failure while opening the corresponding shared memory segment ({:?}).",
251                            e);
252                    }
253                }
254            },
255        }
256    }
257
258    pub(crate) unsafe fn unregister_offset(&self, offset: PointerOffset) {
259        if let MemoryViewType::Dynamic(memory) = &self.memory {
260            memory.unregister_offset(offset);
261        }
262    }
263
264    pub(crate) fn is_dynamic(&self) -> bool {
265        matches!(&self.memory, MemoryViewType::Dynamic(_))
266    }
267}