Skip to main content

odem_rs_core/continuation/
share.rs

1use core::{any::Any, cell::Cell, fmt, num::NonZero, ptr::NonNull};
2
3use crate::{
4	config::Config,
5	ptr::Irc,
6	simulator::{Mark, Sim},
7};
8
9/* ************************************************************** Shared Data */
10
11/// This structure acts as a public interface for accessing the shared state of
12/// an agent.
13///
14/// It lets external observers inspect an [`Agent`]’s state in a controlled
15/// manner, by providing access to the agent’s human-readable name and unique
16/// identifier (which together form the agent’s [`Label`]), as well as a
17/// reference to the state the agent explicitly exposes. Note that an agent's
18/// own jobs can access state-internals directly, so this interface is intended
19/// solely for outside inspection through [Pucks].
20///
21/// [`Agent`]: crate::agent::Agent
22/// [Pucks]: crate::Puck
23pub struct Share<C: ?Sized + Config> {
24	/// Pointer to the simulator.
25	sim: Irc<Sim<C>>,
26	/// Contains the type name of the agent.
27	name: &'static str,
28	/// Contains the agent-unique ID for this specific instance.
29	pid: Option<NonZero<usize>>,
30	/// Marks the priority of continuations with shared data.
31	rank: Cell<C::Rank>,
32	/// Marks the order in which continuations associated with the same agent have
33	/// been inserted.
34	mark: Cell<Mark>,
35	/// A reference to the shared instance over which continuations with shared data
36	/// operate over.
37	item: NonNull<dyn Any>,
38}
39
40impl<C: ?Sized + Config> Share<C> {
41	/// Initialize the shared data for the root job of a simulation run.
42	pub(crate) fn root(sim: Irc<Sim<C>>) -> Self {
43		let default_rank = sim.config().default_rank();
44		Self {
45			sim,
46			name: "SimMain",
47			pid: None,
48			rank: Cell::new(default_rank),
49			mark: Cell::new(Mark::default()),
50			item: NonNull::from(&()),
51		}
52	}
53
54	/// Initialize the shared data for a new agent.
55	///
56	/// # Safety
57	/// The caller is responsible for `item` outliving the shared data, but
58	/// `Share` guarantees that it will not access `item` in its `drop` impl.
59	pub(crate) unsafe fn new<I: Any>(
60		sim: Irc<Sim<C>>,
61		item: &I,
62		rank: C::Rank,
63		name: &'static str,
64		pid: NonZero<usize>,
65	) -> Share<C> {
66		Share {
67			sim,
68			name,
69			pid: Some(pid),
70			rank: Cell::new(rank),
71			mark: Cell::new(Mark::default()),
72			item: NonNull::from(item),
73		}
74	}
75
76	/// Returns the current rank of the owning [`Agent`].
77	///
78	/// [`Agent`]: crate::agent::Agent
79	pub fn rank(&self) -> C::Rank {
80		self.rank.get()
81	}
82
83	/// Returns the chosen identifier for the type of the shared instance.
84	pub fn name(&self) -> &'static str {
85		self.name
86	}
87
88	/// Returns the ID of the agent associated with the shared instance if
89	/// one has been set or `None` if it hasn't.
90	pub fn pid(&self) -> Option<NonZero<usize>> {
91		self.pid
92	}
93
94	/// Returns a reference of the contained instance.
95	pub fn subject(&self) -> &dyn Any {
96		// SAFETY: This is safe per the invariant on `Self::new`.
97		unsafe { self.item.as_ref() }
98	}
99
100	/// Returns the agent-unique [Label] for this instance.
101	pub fn label(&self) -> Label {
102		Label {
103			name: self.name(),
104			pid: self.pid(),
105		}
106	}
107
108	/// Returns a [Cell] with the currently stored [Mark].
109	///
110	/// The mark is used to sort [jobs] belonging to the same [agent] together.
111	///
112	/// [jobs]: crate::job
113	/// [agent]: crate::agent
114	pub(crate) fn mark(&self) -> &Cell<Mark> {
115		&self.mark
116	}
117
118	/// Sets the rank of the agent, influencing all agent's jobs.
119	///
120	/// The new rank takes immediate effect and causes the rearrangement
121	/// of all jobs currently scheduled, both in the present and future.
122	///
123	/// Lowering the rank of the active `Agent` can lead to another `Agent`
124	/// gaining control if one with a higher rank after the change has jobs
125	/// scheduled at the current model time. This change takes effect once the
126	/// currently active agent suspends.
127	pub fn update_rank(&self, rank: C::Rank) {
128		// notify the calendar first
129		self.sim
130			.calendar()
131			.update_rank(self.mark.get(), &self.rank, rank);
132		// now change the rank in case the calendar didn't have to reschedule
133		self.rank.set(rank);
134	}
135
136	/// Returns a reference to the simulation context.
137	pub(crate) fn sim(&self) -> &Irc<Sim<C>> {
138		&self.sim
139	}
140}
141
142impl<C: Config> fmt::Debug for Share<C> {
143	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
144		f.debug_struct("Share")
145			.field("name", &self.name())
146			.field("pid", &self.pid())
147			.field("mark", &self.mark.get())
148			.field("rank", &self.rank())
149			.finish()
150	}
151}
152
153/* ************************************************************ Agent Label */
154
155/// Agent-specific identifier that uniquely identifies an instance over the
156/// course of a simulation run.
157///
158/// It [renders] as a string containing the base name - usually the type name,
159/// but user-definable by setting the name during [agent building] or by
160/// implementing [`Behavior::name`] - followed by a pound symbol and an instance
161/// number, e.g. `"JamesBond#7"`. The name is pretty printed by default but can
162/// be written out by using alternative formatting:
163///
164/// ```
165/// # use std::{pin::pin, rc::Rc};
166/// # use odem_rs_core::{agent::Agent, simulator::Sim, Puck};
167/// # struct MyAgent;
168/// # impl MyAgent { async fn actions(self: &Rc<Self>, _sim: &Sim) {} }
169///
170/// # async fn sim_main(sim: &Sim) {
171/// let agent = pin!(Agent::new((Rc::new(MyAgent), MyAgent::actions)));
172/// let puck = sim.activate(agent);
173///
174/// // outputs "Rc<MyAgent>#1"
175/// println!("{}", puck.label());
176///
177/// // outputs "alloc::rc::Rc<lab::MyAgent>#1"
178/// println!("{:#}", puck.label());
179/// # }
180///
181/// ```
182///
183/// [renders]: fmt::Display
184/// [agent building]: crate::agent::Builder
185/// [`Behavior::name`]: crate::agent::Behavior::name
186#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
187pub struct Label {
188	/// The agent name.
189	pub name: &'static str,
190	/// The agent-name-specific ID.
191	pub pid: Option<NonZero<usize>>,
192}
193
194impl Label {
195	/// Returns an iterator of string slices into the agent name that
196	/// corresponds to a pretty-printed version of the type path.
197	///
198	/// The implementation is ~~stolen~~ inspired by [Jakob Hellermann's]
199	/// [`pretty-type-name`] crate, adapted to use a heapless iterator rather
200	/// than a `String`.
201	///
202	/// [Jakob Hellermann's]: https://crates.io/users/jakobhellermann
203	/// [`pretty-type-name`]: https://crates.io/crates/pretty-type-name
204	pub fn pretty_name(&self) -> impl Iterator<Item = &'static str> {
205		self.name
206			.split_inclusive(&['<', '>', '(', ')', '[', ']', ',', ';'])
207			.filter_map(|part| part.rsplit(':').next())
208	}
209}
210
211impl fmt::Display for Label {
212	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
213		if !f.alternate() {
214			// use the pretty-printed format
215			for ident in self.pretty_name() {
216				f.write_str(ident)?;
217			}
218		} else {
219			// print the whole name
220			f.write_str(self.name)?;
221		}
222
223		// only print the pid if it exists
224		if let Some(pid) = self.pid {
225			write!(f, "#{pid}")
226		} else {
227			Ok(())
228		}
229	}
230}