Skip to main content

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}