statime_netptp/socket/send.rs
1use std::{
2 future::poll_fn,
3 io::Result,
4 task::{Context, Poll, Waker},
5};
6
7use statime_base::UniversalTimestamp;
8use timestamped_socket::interface::InterfaceName;
9
10use crate::{
11 BoundInterface, CACHED_TIMESTAMPS, ConnectedSocket, NetworkManagerData, OpenSocket,
12 PtpAddressFamily, TIMESTAMP_FETCH_TIMEOUT, TimestampSource,
13};
14
15impl<A: PtpAddressFamily> NetworkManagerData<A> {
16 async fn send_event(
17 &self,
18 interface_name: Option<InterfaceName>,
19 timestamp_source: TimestampSource,
20 buf: &[u8],
21 from: Option<A>,
22 to: A,
23 ) -> std::prelude::v1::Result<Option<UniversalTimestamp>, std::io::Error> {
24 let (timestamp_id, last_seen) = poll_fn(|cx| {
25 // The mutex can only be poisoned from an earlier panic. It is ok for
26 // us to propagate that to all the threads.
27 let interfaces = self.interfaces.read().unwrap();
28 // Reference counting ensures the interface will always be available.
29 let interface = &interfaces[&interface_name];
30
31 let last_seen = *interfaces[&interface_name]
32 .timestamp_counter
33 .read()
34 .unwrap();
35
36 interface.send_wakers.add_waker(cx.waker().clone());
37 interface
38 .interface
39 .poll_send_event(
40 buf,
41 from,
42 to,
43 &mut Context::from_waker(&Waker::from(interface.send_wakers.clone())),
44 )
45 .map(|v| v.map(|u| (u, last_seen)))
46 })
47 .await?;
48
49 self.wait_for_send_timestamp(interface_name, timestamp_source, timestamp_id, last_seen)
50 .await
51 }
52
53 #[expect(
54 clippy::too_many_lines,
55 reason = "FIXME: Figure out if we can split this function in a reasonable way."
56 )]
57 async fn wait_for_send_timestamp(
58 &self,
59 interface_name: Option<InterfaceName>,
60 timestamp_source: TimestampSource,
61 timestamp_id: timestamped_socket::socket::SendTimestampToken,
62 mut last_seen: usize,
63 ) -> std::prelude::v1::Result<Option<UniversalTimestamp>, std::io::Error> {
64 match tokio::time::timeout(
65 TIMESTAMP_FETCH_TIMEOUT,
66 poll_fn(|cx| {
67 // The mutex can only be poisoned from an earlier panic. It is ok for
68 // us to propagate that to all the threads.
69 let interfaces = self.interfaces.read().unwrap();
70 // Reference counting ensures the interface will always be available.
71 let interface = &interfaces[&interface_name];
72
73 loop {
74 interface.ts_wakers.add_waker(cx.waker().clone());
75 // The mutex can only be poisoned from an earlier panic. It is ok for
76 // us to propagate that to all the threads.
77 let cur_seen = *interface.timestamp_counter.read().unwrap();
78 let count = last_seen.wrapping_sub(cur_seen);
79 let start = last_seen % CACHED_TIMESTAMPS;
80
81 if count >= CACHED_TIMESTAMPS {
82 for ts_entry in &interface.previous_timestamps {
83 // The mutex can only be poisoned from an earlier panic. It is ok for
84 // us to propagate that to all the threads.
85 if let Some((cur_id, ts_data)) = *ts_entry.read().unwrap()
86 && cur_id == timestamp_id
87 {
88 return Poll::Ready(match timestamp_source {
89 TimestampSource::System => {
90 ts_data.software.map(UniversalTimestamp::from)
91 }
92 TimestampSource::Hardware => {
93 ts_data.hardware.map(UniversalTimestamp::from)
94 }
95 });
96 }
97 }
98 } else if start + count > CACHED_TIMESTAMPS {
99 // Will never underflow as start is guaranteed smaller than
100 // CACHED_TIMESTAMPS.
101 let head_size = CACHED_TIMESTAMPS - start;
102 // All the indexing here is guaranteed to be in range since
103 // head_size < count, and count < CACHED_TIMESTAMPS, and
104 // start < CACHED_TIMESTAMPS.
105 for ts_entry in interface.previous_timestamps[start..]
106 .iter()
107 .chain(interface.previous_timestamps[..(count - head_size)].iter())
108 {
109 if let Some((cur_id, ts_data)) = *ts_entry.read().unwrap()
110 && cur_id == timestamp_id
111 {
112 return Poll::Ready(match timestamp_source {
113 TimestampSource::System => {
114 ts_data.software.map(UniversalTimestamp::from)
115 }
116 TimestampSource::Hardware => {
117 ts_data.hardware.map(UniversalTimestamp::from)
118 }
119 });
120 }
121 }
122 } else {
123 // All the indexing here is guaranteed to be in range since
124 // start + count < CACHED_TIMESTAMPS, and both are smaller than
125 // CACHED_TIMESTAMPS, which in turn is smaller than usize::max/2.
126 for ts_entry in &interface.previous_timestamps[start..] {
127 if let Some((cur_id, ts_data)) = *ts_entry.read().unwrap()
128 && cur_id == timestamp_id
129 {
130 return Poll::Ready(match timestamp_source {
131 TimestampSource::System => {
132 ts_data.software.map(UniversalTimestamp::from)
133 }
134 TimestampSource::Hardware => {
135 ts_data.hardware.map(UniversalTimestamp::from)
136 }
137 });
138 }
139 }
140 }
141
142 last_seen = cur_seen;
143
144 match interface
145 .interface
146 .poll_recv_timestamp(&mut Context::from_waker(&Waker::from(
147 interface.ts_wakers.clone(),
148 ))) {
149 Poll::Ready(Ok((ts_id, ts_data))) => {
150 // The mutex can only be poisoned from an earlier panic. It is ok for
151 // us to propagate that to all the threads.
152 let mut ts_counter = interface.timestamp_counter.write().unwrap();
153 let write_idx = *ts_counter % CACHED_TIMESTAMPS;
154 // The mutex can only be poisoned from an earlier panic. It is ok for
155 // us to propagate that to all the threads.
156 let mut entry =
157 interface.previous_timestamps[write_idx].write().unwrap();
158 *entry = Some((ts_id, ts_data));
159 *ts_counter = ts_counter.wrapping_add(1);
160 if ts_id == timestamp_id {
161 return Poll::Ready(match timestamp_source {
162 TimestampSource::System => {
163 ts_data.software.map(UniversalTimestamp::from)
164 }
165 TimestampSource::Hardware => {
166 ts_data.hardware.map(UniversalTimestamp::from)
167 }
168 });
169 }
170 }
171 Poll::Ready(Err(e)) => {
172 tracing::warn!(
173 ?interface_name,
174 "Error trying to fetch send timestamp: {e}"
175 );
176 }
177 Poll::Pending => return Poll::Pending,
178 }
179 }
180 }),
181 )
182 .await
183 {
184 Ok(result) => Ok(result),
185 Err(_) => Ok(None),
186 }
187 }
188}
189
190impl<A: PtpAddressFamily> OpenSocket<A> {
191 /// Send an event message.
192 ///
193 /// Send the event message to the specified remote. If provided, `from`
194 /// sets the sender address for this message, otherwise this is inferred.
195 ///
196 /// Returns the actual time the message was sent if the operating system
197 /// was able to provide this.
198 ///
199 /// # Errors
200 ///
201 /// Returns any IO errors that occured trying to send the message.
202 #[expect(
203 clippy::missing_panics_doc,
204 reason = "Function will only panic if there is an implementation bug in this crate."
205 )]
206 pub async fn send_event(
207 &self,
208 buf: &[u8],
209 from: Option<A>,
210 to: A,
211 ) -> Result<Option<UniversalTimestamp>> {
212 let (interface_name, timestamp_source) = {
213 // The mutex can only be poisoned from an earlier panic. It is ok for
214 // us to propagate that to all the threads.
215 let sockets = self.state.sockets.read().unwrap();
216 // The socket will always be available
217 let socket = &sockets[&self.socket_id];
218 (socket.interface_filter, socket.timestamp_source)
219 };
220
221 self.state
222 .send_event(interface_name, timestamp_source, buf, from, to)
223 .await
224 }
225
226 /// Send a general message.
227 ///
228 /// Send the event message to the specified remote. If provided, `from`
229 /// sets the sender address for this message, otherwise this is inferred.
230 ///
231 /// # Errors
232 ///
233 /// Returns any IO errors that occured trying to send the message.
234 #[expect(
235 clippy::missing_panics_doc,
236 reason = "Function will only panic if there is an implementation bug in this crate."
237 )]
238 pub async fn send_general(&self, buf: &[u8], from: Option<A>, to: A) -> Result<()> {
239 poll_fn(|cx| {
240 // The mutex can only be poisoned from an earlier panic. It is ok for
241 // us to propagate that to all the threads.
242 let sockets = self.state.sockets.read().unwrap();
243 // The mutex can only be poisoned from an earlier panic. It is ok for
244 // us to propagate that to all the threads.
245 let interfaces = self.state.interfaces.read().unwrap();
246 // The socket will always be available
247 let interface_name = sockets[&self.socket_id].interface_filter;
248 // Reference counting ensures the interface will always be available.
249 // The same holds for the socket.
250 let interface = &interfaces[&interface_name];
251 interface.send_wakers.add_waker(cx.waker().clone());
252 interface.interface.poll_send_general(
253 buf,
254 from,
255 to,
256 &mut Context::from_waker(&Waker::from(interface.send_wakers.clone())),
257 )
258 })
259 .await
260 }
261}
262
263impl<A: PtpAddressFamily> ConnectedSocket<A> {
264 /// Send an event message
265 ///
266 /// Send the event message to the connected remote.
267 ///
268 /// Returns the actual time the message was sent if the operating system
269 /// was able to provide this.
270 ///
271 /// # Errors
272 ///
273 /// Returns any IO errors that occured trying to send the message.
274 #[expect(
275 clippy::missing_panics_doc,
276 reason = "Function will only panic if there is an implementation bug in this crate."
277 )]
278 pub async fn send_event(&self, buf: &[u8]) -> Result<Option<UniversalTimestamp>> {
279 let (interface_name, timestamp_source, from, to) = {
280 // The mutex can only be poisoned from an earlier panic. It is ok for
281 // us to propagate that to all the threads.
282 let sockets = self.state.sockets.read().unwrap();
283 // The socket will always be available
284 let socket = &sockets[&self.socket_id];
285 // Connected sockets will always have a remote filter.
286 (
287 socket.interface_filter,
288 socket.timestamp_source,
289 socket.local_filter,
290 socket.remote_filter.unwrap(),
291 )
292 };
293
294 self.state
295 .send_event(interface_name, timestamp_source, buf, from, to)
296 .await
297 }
298
299 /// Send a general message.
300 ///
301 /// Send the general message to the connected remote.
302 ///
303 /// # Errors
304 ///
305 /// Returns any IO errors that occured trying to send the message.
306 #[expect(
307 clippy::missing_panics_doc,
308 reason = "Function will only panic if there is an implementation bug in this crate."
309 )]
310 pub async fn send_general(&self, buf: &[u8]) -> Result<()> {
311 poll_fn(|cx| {
312 // The mutex can only be poisoned from an earlier panic. It is ok for
313 // us to propagate that to all the threads.
314 let sockets = self.state.sockets.read().unwrap();
315 // The mutex can only be poisoned from an earlier panic. It is ok for
316 // us to propagate that to all the threads.
317 let interfaces = self.state.interfaces.read().unwrap();
318 // The socket will always be available
319 let socket = &sockets[&self.socket_id];
320 // Reference counting ensures the interface will always be available.
321 // The same holds for the socket.
322 let interface = &interfaces[&socket.interface_filter];
323 interface.send_wakers.add_waker(cx.waker().clone());
324 // The remote filter is always in use for connected sockets.
325 interface.interface.poll_send_general(
326 buf,
327 socket.local_filter,
328 socket.remote_filter.unwrap(),
329 &mut Context::from_waker(&Waker::from(interface.send_wakers.clone())),
330 )
331 })
332 .await
333 }
334}