Skip to main content

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}