iceoryx2_cal/monitoring/mod.rs
1// Copyright (c) 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
13//! Allows one process to monitor the state of another process. Can detect if the process is
14//! [`State::Alive`], [`State::Dead`] or the existance with [`State::DoesNotExist`]. To activate
15//! monitoring the process that shall be monitored must instantiate a [`MonitoringToken`]. As long
16//! as the [`MonitoringToken`] is in scope the [`MonitoringMonitor`] will detect the process as
17//! [`State::Alive`]. When the process crashes it will be detected as [`State::Dead`]. If the
18//! process does not yet have instantiated a [`MonitoringMonitor`] the process is identified as
19//! [`State::DoesNotExist`].
20//!
21//! # Example
22//!
23//! ```
24//! use iceoryx2_cal::monitoring::*;
25//!
26//! fn monitored_process<M: Monitoring>() {
27//! let token =
28//! M::Builder::new(&FileName::new(b"unique_process_identifier").unwrap()).
29//! token().unwrap();
30//!
31//! // keep the token in scope and do what a process shall do
32//!
33//! // process can no longer be monitored
34//! drop(token);
35//! }
36//!
37//! fn watching_process<M: Monitoring>() {
38//! let monitor = M::Builder::new(&FileName::new(b"unique_process_identifier").unwrap()).
39//! monitor().unwrap();
40//!
41//! match monitor.state().unwrap() {
42//! State::Alive => println!("process is alive"),
43//! State::Dead => println!("process is dead"),
44//! State::DoesNotExist => println!("process does not exist"),
45//! }
46//! }
47//!
48//! fn cleaning_process<M: Monitoring>() {
49//! let cleaner = match M::Builder::new(&FileName::new(b"unique_process_identifier")
50//! .unwrap()).cleaner() {
51//! Ok(cleaner) => cleaner,
52//! Err(MonitoringCreateCleanerError::AlreadyOwnedByAnotherInstance) => {
53//! // someone is already cleaning up for us - perfect :)
54//! return;
55//! }
56//! Err(MonitoringCreateCleanerError::InstanceStillAlive) => {
57//! // whoopsie, the monitored instance is not dead
58//! return;
59//! }
60//! Err(e) => {
61//! // usual error handling
62//! return;
63//! }
64//! };
65//!
66//! // cleanup all stale resources of the dead process
67//! drop(cleaner);
68//! }
69
70//! ```
71
72use core::fmt::Debug;
73
74pub use iceoryx2_bb_container::semantic_string::SemanticString;
75use iceoryx2_bb_elementary_traits::testing::abandonable::Abandonable;
76pub use iceoryx2_bb_system_types::file_name::FileName;
77
78pub use crate::{
79 named_concept::NamedConcept, named_concept::NamedConceptBuilder,
80 named_concept::NamedConceptMgmt,
81};
82
83pub mod file_lock;
84pub mod process_local;
85pub mod recommended;
86
87/// Represents the state of a monitored process.
88#[derive(Debug, Clone, Copy, PartialEq, Eq)]
89pub enum State {
90 Alive,
91 Dead,
92 DoesNotExist,
93}
94
95/// Represents the possible errors that can occur when a new [`MonitoringToken`] is created with
96/// [`MonitoringBuilder::token()`].
97#[derive(Debug, Clone, Copy, PartialEq, Eq)]
98pub enum MonitoringCreateTokenError {
99 InsufficientPermissions,
100 AlreadyExists,
101 SystemCorrupted,
102 InternalError,
103}
104
105impl core::fmt::Display for MonitoringCreateTokenError {
106 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
107 write!(f, "MonitoringCreateTokenError::{self:?}")
108 }
109}
110
111impl core::error::Error for MonitoringCreateTokenError {}
112
113/// Represents the possible errors that can occur when a new [`MonitoringCleaner`] is created with
114/// [`MonitoringBuilder::cleaner()`].
115#[derive(Debug, Clone, Copy, PartialEq, Eq)]
116pub enum MonitoringCreateCleanerError {
117 Interrupt,
118 InstanceStillAlive,
119 AlreadyOwnedByAnotherInstance,
120 IsBeingCleanedUpOrAnotherCleanerCrashedDuringCleanup,
121 DoesNotExist,
122 InternalError,
123}
124
125impl core::fmt::Display for MonitoringCreateCleanerError {
126 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
127 write!(f, "MonitoringCreateCleanerError::{self:?}")
128 }
129}
130
131impl core::error::Error for MonitoringCreateCleanerError {}
132
133/// Represents the possible errors that can occur when a new [`MonitoringMonitor`] is created with
134/// [`MonitoringBuilder::monitor()`].
135#[derive(Debug, Clone, Copy, PartialEq, Eq)]
136pub enum MonitoringCreateMonitorError {
137 InsufficientPermissions,
138 Interrupt,
139 ConceptNameNotSupportedOnPlatform,
140 InternalError,
141}
142
143impl core::fmt::Display for MonitoringCreateMonitorError {
144 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
145 write!(f, "MonitoringCreateMonitorError::{self:?}")
146 }
147}
148
149impl core::error::Error for MonitoringCreateMonitorError {}
150
151/// Represents the possible errors that can occur when the [`State`] is acquired via
152/// [`MonitoringMonitor::state()`].
153#[derive(Debug, Clone, Copy, PartialEq, Eq)]
154pub enum MonitoringStateError {
155 Interrupt,
156 InsufficientPermissions,
157 InternalError,
158}
159
160impl core::fmt::Display for MonitoringStateError {
161 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
162 write!(f, "MonitoringStateError::{self:?}")
163 }
164}
165
166impl core::error::Error for MonitoringStateError {}
167
168/// The token enables a process to be monitored by another process.
169pub trait MonitoringToken: NamedConcept + Abandonable {}
170
171/// The cleaner owns the remains of a dead process and is the only one that is allowed to clean up
172/// those resources.
173pub trait MonitoringCleaner: NamedConcept + Abandonable {
174 /// Relinquishs the [`MonitoringCleaner`] without removing the underlying [`Monitoring`] concept. This is useful
175 /// when another process tried to cleanup the stale resources of the dead process but is unable
176 /// to due to insufficient permissions.
177 fn relinquish(self);
178}
179
180/// The monitor allows to monitor another process that has instantiated a [`MonitoringToken`]
181pub trait MonitoringMonitor: NamedConcept {
182 /// Returns the current [`State`] of the monitored process. On failure it returns
183 /// [`MonitoringStateError`].
184 fn state(&self) -> Result<State, MonitoringStateError>;
185}
186
187/// Creates either a [`MonitoringToken`] or instantiates a [`MonitoringMonitor`] that can monitor
188/// the state of a token.
189pub trait MonitoringBuilder<T: Monitoring>: NamedConceptBuilder<T> {
190 /// Creates a new [`MonitoringToken`] on success or returns a [`MonitoringCreateTokenError`]
191 /// on failure.
192 fn token(self) -> Result<T::Token, MonitoringCreateTokenError>;
193
194 /// Instantiates a [`MonitoringMonitor`] to monitor a [`MonitoringToken`]
195 fn monitor(self) -> Result<T::Monitor, MonitoringCreateMonitorError>;
196
197 /// Instantiates a [`MonitoringCleaner`]. If it could be instantiated successfully the owner is
198 /// allowed to remove all stale resources from the former dead process.
199 fn cleaner(self) -> Result<T::Cleaner, MonitoringCreateCleanerError>;
200}
201
202/// Concept that allows to monitor a process from within another process. The process must hereby
203/// instantiate a [`MonitoringToken`] with [`MonitoringBuilder`] so that it can be monitored with
204/// the [`MonitoringMonitor`].
205pub trait Monitoring: NamedConceptMgmt + Sized {
206 type Token: MonitoringToken;
207 type Monitor: MonitoringMonitor;
208 type Cleaner: MonitoringCleaner;
209 type Builder: MonitoringBuilder<Self>;
210
211 /// Returns the default suffix that shall be used for every [`MonitoringToken`].
212 fn default_suffix() -> FileName {
213 unsafe { FileName::new_unchecked(b".monitor") }
214 }
215}