1use core::{
2 cell::RefCell,
3 marker::PhantomData,
4 sync::atomic::{AtomicI8, Ordering},
5};
6
7use rand::Rng;
8
9#[allow(unused_imports)]
10use crate::float_polyfill::FloatPolyfill;
11use crate::{
12 bmc::{acceptable_master::AcceptableMasterList, bmca::Bmca},
13 clock::Clock,
14 config::{InstanceConfig, PortConfig},
15 datastructures::{
16 common::PortIdentity,
17 datasets::{
18 InternalCurrentDS, InternalDefaultDS, InternalParentDS, PathTraceDS, TimePropertiesDS,
19 },
20 },
21 filters::{Filter, FilterEstimate},
22 observability::{current::CurrentDS, default::DefaultDS, parent::ParentDS},
23 port::{InBmca, Port},
24 time::Duration,
25};
26
27pub struct PtpInstance<F, S = RefCell<PtpInstanceState>> {
90 state: S,
91 log_bmca_interval: AtomicI8,
92 _filter: PhantomData<F>,
93}
94
95#[derive(Debug)]
97pub struct PtpInstanceState {
98 pub(crate) default_ds: InternalDefaultDS,
99 pub(crate) current_ds: InternalCurrentDS,
100 pub(crate) parent_ds: InternalParentDS,
101 pub(crate) path_trace_ds: PathTraceDS,
102 pub(crate) time_properties_ds: TimePropertiesDS,
103}
104
105impl PtpInstanceState {
106 fn bmca<A: AcceptableMasterList, C: Clock, F: Filter, R: Rng, S: PtpInstanceStateMutex>(
107 &mut self,
108 ports: &mut [&mut Port<'_, InBmca, A, R, C, F, S>],
109 bmca_interval: Duration,
110 ) {
111 debug_assert_eq!(self.default_ds.number_ports as usize, ports.len());
112
113 for port in ports.iter_mut() {
114 port.calculate_best_local_announce_message()
115 }
116
117 let ebest = Bmca::<()>::find_best_announce_message(
118 ports
119 .iter()
120 .filter_map(|port| port.best_local_announce_message_for_bmca()),
121 );
122
123 for port in ports.iter_mut() {
124 let recommended_state = Bmca::<()>::calculate_recommended_state(
125 &self.default_ds,
126 ebest,
127 port.best_local_announce_message_for_state(), port.state(),
129 );
130
131 log::debug!(
132 "Recommended state port {}: {recommended_state:?}",
133 port.number(),
134 );
135
136 if let Some(recommended_state) = recommended_state {
137 port.set_recommended_state(
138 recommended_state,
139 &mut self.path_trace_ds,
140 &mut self.time_properties_ds,
141 &mut self.current_ds,
142 &mut self.parent_ds,
143 &self.default_ds,
144 );
145 }
146 }
147
148 for port in ports.iter_mut() {
150 port.step_announce_age(bmca_interval);
151 }
152 }
153}
154
155impl<F, S: PtpInstanceStateMutex> PtpInstance<F, S> {
156 pub fn new(config: InstanceConfig, time_properties_ds: TimePropertiesDS) -> Self {
159 let default_ds = InternalDefaultDS::new(config);
160
161 Self {
162 state: S::new(PtpInstanceState {
163 default_ds,
164 current_ds: Default::default(),
165 parent_ds: InternalParentDS::new(default_ds),
166 path_trace_ds: PathTraceDS::new(config.path_trace),
167 time_properties_ds,
168 }),
169 log_bmca_interval: AtomicI8::new(i8::MAX),
170 _filter: PhantomData,
171 }
172 }
173
174 pub fn default_ds(&self) -> DefaultDS {
176 self.state.with_ref(|s| (&s.default_ds).into())
177 }
178
179 pub fn current_ds(&self, port_contribution: Option<FilterEstimate>) -> CurrentDS {
181 self.state
182 .with_ref(|s| CurrentDS::from_state(&s.current_ds, port_contribution))
183 }
184
185 pub fn parent_ds(&self) -> ParentDS {
187 self.state.with_ref(|s| (&s.parent_ds).into())
188 }
189
190 pub fn time_properties_ds(&self) -> TimePropertiesDS {
192 self.state.with_ref(|s| s.time_properties_ds)
193 }
194
195 pub fn path_trace_ds(&self) -> PathTraceDS {
197 self.state.with_ref(|s| s.path_trace_ds.clone())
198 }
199}
200
201impl<F: Filter, S: PtpInstanceStateMutex> PtpInstance<F, S> {
202 pub fn add_port<A, C, R: Rng>(
211 &self,
212 config: PortConfig<A>,
213 filter_config: F::Config,
214 clock: C,
215 rng: R,
216 ) -> Port<'_, InBmca, A, R, C, F, S> {
217 self.log_bmca_interval
218 .fetch_min(config.announce_interval.as_log_2(), Ordering::Relaxed);
219 let port_identity = self.state.with_mut(|state| {
220 state.default_ds.number_ports += 1;
221 PortIdentity {
222 clock_identity: state.default_ds.clock_identity,
223 port_number: state.default_ds.number_ports,
224 }
225 });
226
227 Port::new(
228 &self.state,
229 config,
230 filter_config,
231 clock,
232 port_identity,
233 rng,
234 )
235 }
236
237 pub fn bmca<A: AcceptableMasterList, C: Clock, R: Rng>(
242 &self,
243 ports: &mut [&mut Port<'_, InBmca, A, R, C, F, S>],
244 ) {
245 self.state.with_mut(|state| {
246 state.bmca(
247 ports,
248 Duration::from_seconds(
249 2f64.powi(self.log_bmca_interval.load(Ordering::Relaxed) as i32),
250 ),
251 );
252 });
253 }
254
255 pub fn bmca_interval(&self) -> core::time::Duration {
257 core::time::Duration::from_secs_f64(
258 2f64.powi(self.log_bmca_interval.load(Ordering::Relaxed) as i32),
259 )
260 }
261}
262
263pub trait PtpInstanceStateMutex {
269 fn new(state: PtpInstanceState) -> Self;
271
272 fn with_ref<R, F: FnOnce(&PtpInstanceState) -> R>(&self, f: F) -> R;
274
275 fn with_mut<R, F: FnOnce(&mut PtpInstanceState) -> R>(&self, f: F) -> R;
277}
278
279impl PtpInstanceStateMutex for RefCell<PtpInstanceState> {
280 fn new(state: PtpInstanceState) -> Self {
281 RefCell::new(state)
282 }
283
284 fn with_ref<R, F: FnOnce(&PtpInstanceState) -> R>(&self, f: F) -> R {
285 f(&RefCell::borrow(self))
286 }
287
288 fn with_mut<R, F: FnOnce(&mut PtpInstanceState) -> R>(&self, f: F) -> R {
289 f(&mut RefCell::borrow_mut(self))
290 }
291}
292
293#[cfg(feature = "std")]
294impl PtpInstanceStateMutex for std::sync::RwLock<PtpInstanceState> {
295 fn new(state: PtpInstanceState) -> Self {
296 std::sync::RwLock::new(state)
297 }
298
299 fn with_ref<R, F: FnOnce(&PtpInstanceState) -> R>(&self, f: F) -> R {
300 f(&self.read().unwrap())
301 }
302
303 fn with_mut<R, F: FnOnce(&mut PtpInstanceState) -> R>(&self, f: F) -> R {
304 f(&mut self.write().unwrap())
305 }
306}