iceoryx2_bb_elementary/
relocatable_ptr.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//! Building block to handle inter process communication with multiple shared memory object. Every
14//! process has mapped them to a different virtual memory location therefore pointer inside that
15//! memory region should be distances starting from a fix point which maybe different in every
16//! process.
17//!
18//! # Example
19//!
20//! ```
21//! use iceoryx2_bb_elementary::relocatable_ptr::*;
22//! use iceoryx2_bb_elementary::math::align_to;
23//!
24//! #[repr(C)]
25//! pub struct Container {
26//!     data_ptr: RelocatablePointer<u128>,
27//!     capacity: usize,
28//! }
29//!
30//! impl Container {
31//!     pub fn new(capacity: usize, distance: isize) -> Self {
32//!         Self {
33//!             data_ptr: RelocatablePointer::new(distance),
34//!             capacity
35//!         }
36//!     }
37//!
38//!     pub fn get_mut(&mut self, index: usize) -> &mut u128 {
39//!         unsafe { &mut *self.data_ptr.as_mut_ptr() }
40//!     }
41//! }
42//!
43//! #[repr(C)]
44//! pub struct FixedSizeContainer {
45//!     base: Container,
46//!     data: [u128; 128],
47//! }
48//!
49//! impl FixedSizeContainer {
50//!     pub fn new() -> Self {
51//!         Self {
52//!             base: Container::new(128,
53//!                         // the data_ptr is the first member of container. The distance from
54//!                         // the memory location of the RelocatablePointer `data_ptr` is
55//!                         // therefore the size of `Container` aligned to the type `u128`
56//!                         align_to::<u128>(core::mem::size_of::<Container>()) as isize),
57//!             data: [0; 128]
58//!         }
59//!     }
60//!
61//!     pub fn get_mut(&mut self, index: usize) -> &mut u128 {
62//!         self.base.get_mut(index)
63//!     }
64//! }
65//! ```
66
67use core::{fmt::Debug, marker::PhantomData, ptr::NonNull};
68
69pub use iceoryx2_bb_elementary_traits::pointer_trait::PointerTrait;
70
71use iceoryx2_bb_concurrency::atomic::AtomicIsize;
72use iceoryx2_bb_elementary_traits::generic_pointer::GenericPointer;
73
74#[derive(Debug)]
75pub struct GenericRelocatablePointer;
76
77/// A [`RelocatablePointer`] stores only the distance from its memory starting position to the
78/// memory location it is pointing to. When the [`RelocatablePointer`] is now shared between
79/// processes its virtual memory starting position changes but the distance to the object it is
80/// pointing to is the same.
81///
82/// **Important:**
83///   1. Every construct which relies on a [`RelocatablePointer`] must be declared with
84///      `[repr(C)]`. Otherwise different compilation units may have a different structural layout of
85///      the data type which is shared between processes which leads to undefined behavior.
86///   2. The construct which is using the [`RelocatablePointer`] and the pointee must be stored in
87///      the same shared memory object. Pointing to a different shared memory segment most likely
88///      leads to crashes since it can be mapped in a different order, at a different position and
89///      the distance to the memory destination is off.
90#[repr(C)]
91#[derive(Debug)]
92pub struct RelocatablePointer<T> {
93    distance: AtomicIsize,
94    _phantom: PhantomData<T>,
95}
96
97impl<T> RelocatablePointer<T> {
98    /// Creates a new [`RelocatablePointer`]. The distance is the relative distance to the memory
99    /// destination starting from the memory location of this [`RelocatablePointer`].
100    pub fn new(distance: isize) -> Self {
101        Self {
102            distance: AtomicIsize::new(distance),
103            _phantom: PhantomData,
104        }
105    }
106
107    /// Creates a new uninitialized [`RelocatablePointer`].
108    ///
109    /// # Safety
110    ///
111    ///  * [`RelocatablePointer::init()`] must be called once before use
112    ///
113    pub unsafe fn new_uninit() -> Self {
114        Self::new(0)
115    }
116
117    /// Initializes the [`RelocatablePointer`] by setting the distance to the memory destination
118    /// by providing an absolut pointer to it. The distance can be calculated from the
119    /// [`RelocatablePointer`] memory location and the absolut position of the destination.
120    /// **Important:** The pointer must point into the same shared memory object.
121    ///
122    /// # Safety
123    ///
124    ///  * [`RelocatablePointer`] was created with [`RelocatablePointer::new_uninit()`]
125    ///  * ptr has an alignment of [`core::mem::align_of<T>()`]
126    ///  * ptr has a size which is an multiple of [`core::mem::size_of<T>()`]
127    ///  * It must be called exactly once before using the [`RelocatablePointer`]
128    ///
129    pub unsafe fn init(&self, ptr: NonNull<[u8]>) {
130        self.distance.store(
131            (ptr.as_ptr() as *const u8) as isize - (self as *const Self) as isize,
132            core::sync::atomic::Ordering::Relaxed,
133        );
134    }
135}
136
137impl<T> PointerTrait<T> for RelocatablePointer<T> {
138    unsafe fn as_ptr(&self) -> *const T {
139        ((self as *const Self) as isize + self.distance.load(core::sync::atomic::Ordering::Relaxed))
140            as *const T
141    }
142
143    unsafe fn as_mut_ptr(&mut self) -> *mut T {
144        self.as_ptr() as *mut T
145    }
146
147    fn is_initialized(&self) -> bool {
148        self.distance.load(core::sync::atomic::Ordering::Relaxed) != 0
149    }
150}
151
152impl GenericPointer for GenericRelocatablePointer {
153    type Type<T: Debug> = RelocatablePointer<T>;
154}