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}