iceoryx2_cal/dynamic_storage/mod.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//! Traits that provide modifyable memory which can be accessed by multiple processes
14//! identified by a name.
15//!
16//! A [`DynamicStorage`] has to fulfill the following contract:
17//! * zero sized names are not valid
18//! * **unique:** multiple [`DynamicStorage`]s with the same cannot be created
19//! * non-existing [`DynamicStorage`]s cannot be opened
20//!
21//! The contract is verified by the corresponding unit tests. Every [`DynamicStorage`] must
22//! pass the test.
23//!
24//! **Important:** It is not the task of the [`DynamicStorage`] to ensure a thread-safe access to
25//! the underlying object. If the [`DynamicStorage`] is used in an inter-process environment every
26//! access must be considered as a concurrent access!
27//!
28//! # Example
29//!
30//! ```
31//! use iceoryx2_bb_posix::access_mode::AccessMode;
32//! use iceoryx2_bb_system_types::file_name::FileName;
33//! use iceoryx2_bb_container::semantic_string::SemanticString;
34//! use iceoryx2_cal::dynamic_storage::*;
35//! use iceoryx2_cal::named_concept::*;
36//! use core::sync::atomic::{AtomicU64, Ordering};
37//!
38//! // the following two functions can be implemented in different processes
39//! fn process_one<Storage: DynamicStorage<AtomicU64>>() {
40//! let storage_name = FileName::new(b"myStorageName").unwrap();
41//! let mut storage = Storage::Builder::new(&storage_name)
42//! .create(AtomicU64::new(873)).unwrap();
43//!
44//! println!("Created storage {}", storage.name());
45//! storage.get().store(991, Ordering::Relaxed);
46//! }
47//!
48//! fn process_two<Storage: DynamicStorage<AtomicU64>>() {
49//! let storage_name = FileName::new(b"myStorageName").unwrap();
50//! let mut storage = Storage::Builder::new(&storage_name)
51//! .open(AccessMode::ReadWrite).unwrap();
52//!
53//! println!("Opened storage {}", storage.name());
54//! println!("Current value {}", storage.get().swap(1001, Ordering::Relaxed));
55//! }
56//! ```
57
58use core::{fmt::Debug, time::Duration};
59
60use iceoryx2_bb_elementary::enum_gen;
61use iceoryx2_bb_elementary_traits::testing::abandonable::Abandonable;
62use iceoryx2_bb_memory::bump_allocator::BumpAllocator;
63use iceoryx2_bb_posix::file::AccessMode;
64use iceoryx2_bb_system_types::file_name::*;
65use tiny_fn::tiny_fn;
66
67use crate::static_storage::file::{NamedConcept, NamedConceptBuilder, NamedConceptMgmt};
68
69tiny_fn! {
70 /// The callback called to initialize the data inside the [`DynamicStorage`]
71 pub struct Initializer<T> = FnMut(value: &mut T, allocator: &mut BumpAllocator) -> bool;
72}
73
74impl<T> Debug for Initializer<'_, T> {
75 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
76 write!(f, "")
77 }
78}
79
80#[doc(hidden)]
81pub mod dynamic_storage_configuration;
82pub mod file;
83pub mod posix_shared_memory;
84pub mod process_local;
85pub mod recommended;
86
87/// Describes failures when creating a new [`DynamicStorage`]
88#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]
89pub enum DynamicStorageCreateError {
90 AlreadyExists,
91 InsufficientPermissions,
92 InitializationFailed,
93 InternalError,
94}
95
96impl core::fmt::Display for DynamicStorageCreateError {
97 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
98 write!(f, "DynamicStorageCreateError::{self:?}")
99 }
100}
101
102impl core::error::Error for DynamicStorageCreateError {}
103
104/// Describes failures when opening a new [`DynamicStorage`]
105#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]
106pub enum DynamicStorageOpenError {
107 DoesNotExist,
108 InitializationNotYetFinalized,
109 VersionMismatch,
110 InternalError,
111}
112
113impl core::fmt::Display for DynamicStorageOpenError {
114 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
115 write!(f, "DynamicStorageOpenError::{self:?}")
116 }
117}
118
119impl core::error::Error for DynamicStorageOpenError {}
120
121enum_gen! {
122 DynamicStorageOpenOrCreateError
123 mapping:
124 DynamicStorageOpenError,
125 DynamicStorageCreateError
126}
127
128/// Builder for the [`DynamicStorage`]. T is not allowed to implement the [`Drop`] trait.
129pub trait DynamicStorageBuilder<'builder, T: Send + Sync, D: DynamicStorage<T>>:
130 Debug + Sized + NamedConceptBuilder<D>
131{
132 /// Defines if `T::Drop` shall be called when the [`DynamicStorage`] is removed. The default
133 /// is [`true`].
134 fn call_drop_on_destruction(self, value: bool) -> Self;
135
136 /// Defines if a newly created [`DynamicStorage`] owns the underlying resources. The default
137 /// is [`true`].
138 fn has_ownership(self, value: bool) -> Self;
139
140 /// Sets the size of the supplementary data. Only relevant when it is newly created otherwise
141 /// the already initialized [`DynamicStorage`] with the full size is used.
142 fn supplementary_size(self, value: usize) -> Self;
143
144 /// The timeout defines how long the [`DynamicStorageBuilder`] should wait for
145 /// [`DynamicStorageBuilder::create()`]
146 /// to finialize the initialization. This is required when the [`DynamicStorage`] is
147 /// created and initialized concurrently from another process.
148 /// By default it is set to [`Duration::ZERO`] for no timeout.
149 fn timeout(self, value: Duration) -> Self;
150
151 /// Before the construction is finalized the initializer is called
152 /// with a mutable reference to the new value and a mutable reference to a bump allocator
153 /// which provides access to the supplementary memory. If the initialization failed it
154 /// shall return false, otherwise true.
155 fn initializer<F: FnMut(&mut T, &mut BumpAllocator) -> bool + 'builder>(self, value: F)
156 -> Self;
157
158 /// Creates a new [`DynamicStorage`]. The returned object has the ownership of the
159 /// [`DynamicStorage`] and when it goes out of scope the underlying resources shall be
160 /// removed without corrupting already opened [`DynamicStorage`]s.
161 fn create(self, initial_value: T) -> Result<D, DynamicStorageCreateError>;
162
163 /// Opens a [`DynamicStorage`]. The implementation must ensure that a [`DynamicStorage`]
164 /// which is in the midst of creation cannot be opened. If the [`DynamicStorage`] does not
165 /// exist or is not initialized it fails.
166 fn open(self, access_mode: AccessMode) -> Result<D, DynamicStorageOpenError>;
167
168 /// Opens the [`DynamicStorage`] if it exists, otherwise it creates it.
169 fn open_or_create(self, initial_value: T) -> Result<D, DynamicStorageOpenOrCreateError>;
170}
171
172/// Is being built by the [`DynamicStorageBuilder`]. The [`DynamicStorage`] trait shall provide
173/// inter-process access to a modifyable piece of memory identified by some name.
174pub trait DynamicStorage<T: Send + Sync>:
175 Sized + Debug + NamedConceptMgmt + NamedConcept + Send + Sync + Abandonable
176{
177 type Builder<'builder>: DynamicStorageBuilder<'builder, T, Self>;
178
179 /// Returns if the [`DynamicStorage`] supports persistency, meaning that the underlying OS
180 /// resource remain even when every [`DynamicStorage`] instance in every process was removed.
181 fn does_support_persistency() -> bool;
182
183 /// Returns true if the storage holds the ownership, otherwise false.
184 fn has_ownership(&self) -> bool;
185
186 /// Releases the ownership of the [`DynamicStorage`]. When the object goes out of scope it is
187 /// no longer removed.
188 fn release_ownership(&self);
189
190 /// Acquires the ownership of the [`DynamicStorage`]. When the object goes out of scope the
191 /// underlying resources will be removed.
192 fn acquire_ownership(&self);
193
194 /// Returns a const reference to the underlying object. It is const since the [`DynamicStorage`]
195 /// can be accessed by multiple processes concurrently therefore it must be constant or
196 /// thread-safe.
197 fn get(&self) -> &T;
198
199 /// The default suffix of every dynamic storage
200 fn default_suffix() -> FileName {
201 unsafe { FileName::new_unchecked(b".dyn") }
202 }
203
204 #[doc(hidden)]
205 /// # Safety
206 ///
207 /// * ensure that the contained type matches the semantic type_name given with `T`
208 /// * if `T` is some arbitary placeholder, then only use it to remove the concept or list it
209 /// * DO NOT OPEN IT
210 unsafe fn __internal_set_type_name_in_config(config: &mut Self::Configuration, type_name: &str);
211}