Skip to main content

statime_netptp/
interface.rs

1use std::{collections::hash_map::Entry, sync::Arc};
2
3use timestamped_socket::interface::InterfaceName;
4
5use crate::{
6    ConnectedSocket, NetworkManagerData, OpenSocket, PACKET_BUFFER_SIZE, PtpAddressFamily,
7    SocketData, TimestampSource,
8};
9
10/// A handle for a network interface
11///
12/// Describes the configured network interface and way of timestamping for its
13/// sockets. It also ensures the requested timestamping clock remains available
14/// on the interface during its existence.
15pub struct Interface<A: PtpAddressFamily> {
16    pub(crate) state: Arc<NetworkManagerData<A>>,
17    pub(crate) name: Option<InterfaceName>,
18    pub(crate) timestamp_source: TimestampSource,
19}
20
21impl<A: PtpAddressFamily> Clone for Interface<A> {
22    fn clone(&self) -> Self {
23        // The mutex can only be poisoned from an earlier panic. It is ok for
24        // us to propagate that to all the threads.
25        let mut interfaces = self.state.interfaces.write().unwrap();
26        // The interface will always be present because of the refcounting.
27        let entry = interfaces.get_mut(&self.name).unwrap();
28        // An overflowing reference count is an error condition from which we
29        // cannot reasonably recover. A panic here is the best solution.
30        entry.refcount = entry.refcount.checked_add(1).unwrap();
31        Self {
32            state: self.state.clone(),
33            name: self.name,
34            timestamp_source: self.timestamp_source,
35        }
36    }
37}
38
39impl<A: PtpAddressFamily> Drop for Interface<A> {
40    fn drop(&mut self) {
41        // The mutex can only be poisoned from an earlier panic. It is ok for
42        // us to propagate that to all the threads.
43        let mut interfaces = self.state.interfaces.write().unwrap();
44        // The interface will always be present because of the refcounting.
45        let entry = interfaces.get_mut(&self.name).unwrap();
46        // An underflowing refcount should never happen. If it does, something
47        // is seriously wrong and aborting is the best solution.
48        entry.refcount = entry.refcount.checked_sub(1).unwrap();
49        if entry.refcount == 0 {
50            interfaces.remove(&self.name);
51        }
52    }
53}
54
55impl<A: PtpAddressFamily> Interface<A> {
56    /// Create an open socket intended for listening to the network.
57    ///
58    /// Creates a socket that is intended to receive traffic from any remote,
59    /// potentially providing responses to that traffic.
60    #[must_use]
61    #[expect(
62        clippy::missing_panics_doc,
63        reason = "Function will only panic if there is an implementation bug in this crate."
64    )]
65    pub fn listen_socket(&self) -> OpenSocket<A> {
66        let (socket_channel_tx, socket_channel_rx) = tokio::sync::mpsc::channel(PACKET_BUFFER_SIZE);
67
68        let socket = SocketData {
69            remote_filter: None,
70            local_filter: None,
71            interface_filter: self.name,
72            timestamp_source: self.timestamp_source,
73            message_channel: socket_channel_tx,
74        };
75
76        {
77            // The mutex can only be poisoned from an earlier panic. It is ok for
78            // us to propagate that to all the threads.
79            let mut interfaces = self.state.interfaces.write().unwrap();
80            // The interface will always be present because of the refcounting.
81            let entry = interfaces.get_mut(&self.name).unwrap();
82            // An overflowing reference count is an error condition from which we
83            // cannot reasonably recover. A panic here is the best solution.
84            entry.refcount = entry.refcount.checked_add(1).unwrap();
85        }
86
87        let socket_id = {
88            // The mutex can only be poisoned from an earlier panic. It is ok for
89            // us to propagate that to all the threads.
90            let mut sockets = self.state.sockets.write().unwrap();
91            loop {
92                let socket_id = self
93                    .state
94                    .next_socket_id
95                    .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
96                if let Entry::Vacant(vacant_entry) = sockets.entry(socket_id) {
97                    vacant_entry.insert(socket);
98                    break socket_id;
99                }
100            }
101        };
102
103        OpenSocket {
104            state: self.state.clone(),
105            socket_id,
106            packet_receiver: socket_channel_rx,
107        }
108    }
109
110    /// Create a socket connected to a specific remote address.
111    ///
112    /// This creates a socket intended for use in communicating with a single
113    /// remote party over the interface. The `remote` address specifies the
114    /// network address of that party.
115    ///
116    /// The `local` address specifies which address should be used as the
117    /// sender address on the messages. If this is not specified, the operating
118    /// system will select a send address automatically.
119    #[expect(
120        clippy::missing_panics_doc,
121        reason = "Function will only panic if there is an implementation bug in this crate."
122    )]
123    pub fn connected_socket(&self, local: Option<A>, remote: A) -> ConnectedSocket<A> {
124        let (socket_channel_tx, socket_channel_rx) = tokio::sync::mpsc::channel(PACKET_BUFFER_SIZE);
125
126        let socket = SocketData {
127            remote_filter: Some(remote),
128            local_filter: local,
129            interface_filter: self.name,
130            timestamp_source: self.timestamp_source,
131            message_channel: socket_channel_tx,
132        };
133
134        {
135            // The mutex can only be poisoned from an earlier panic. It is ok for
136            // us to propagate that to all the threads.
137            let mut interfaces = self.state.interfaces.write().unwrap();
138            // The interface will always be present because of the refcounting.
139            let entry = interfaces.get_mut(&self.name).unwrap();
140            // An overflowing reference count is an error condition from which we
141            // cannot reasonably recover. A panic here is the best solution.
142            entry.refcount = entry.refcount.checked_add(1).unwrap();
143        }
144
145        let socket_id = {
146            // The mutex can only be poisoned from an earlier panic. It is ok for
147            // us to propagate that to all the threads.
148            let mut sockets = self.state.sockets.write().unwrap();
149            loop {
150                let socket_id = self
151                    .state
152                    .next_socket_id
153                    .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
154                if let Entry::Vacant(vacant_entry) = sockets.entry(socket_id) {
155                    vacant_entry.insert(socket);
156                    break socket_id;
157                }
158            }
159        };
160
161        ConnectedSocket {
162            state: self.state.clone(),
163            socket_id,
164            packet_receiver: socket_channel_rx,
165        }
166    }
167}