Skip to main content

ouisync/network/
connection.rs

1use super::{
2    peer_addr::PeerAddr,
3    peer_info::PeerInfo,
4    peer_source::{ConnectionDirection, PeerSource},
5    peer_state::PeerState,
6    runtime_id::PublicRuntimeId,
7    stats::{ByteCounters, StatsTracker},
8};
9use crate::sync::{AwaitDrop, DropAwaitable, WatchSenderExt};
10use portable_atomic::{AtomicU64, Ordering};
11use std::{
12    collections::{HashMap, hash_map::Entry},
13    fmt,
14    sync::Arc,
15    time::SystemTime,
16};
17use tokio::sync::watch;
18
19/// Container for known connections.
20pub(super) struct ConnectionSet {
21    connections: watch::Sender<HashMap<ConnectionKey, ConnectionData>>,
22}
23
24impl ConnectionSet {
25    pub fn new() -> Self {
26        Self {
27            connections: watch::Sender::new(HashMap::default()),
28        }
29    }
30
31    /// Attempt to reserve an connection to the given peer. If the connection hasn't been reserved
32    /// yet, it returns a `ConnectionPermit` which keeps the connection reserved as long as it
33    /// lives. Otherwise it returns `None`. To release a connection the permit needs to be dropped.
34    /// Also returns a notification object that can be used to wait until the permit gets released.
35    pub fn reserve(&self, addr: PeerAddr, source: PeerSource) -> ReserveResult {
36        let key = ConnectionKey {
37            addr,
38            dir: source.direction(),
39        };
40
41        self.connections
42            .send_if_modified_return(|connections| match connections.entry(key) {
43                Entry::Vacant(entry) => {
44                    let id = ConnectionId::next();
45
46                    entry.insert(ConnectionData {
47                        id,
48                        state: InnerPeerState::Known,
49                        source,
50                        stats_tracker: StatsTracker::default(),
51                        on_release: DropAwaitable::new(),
52                    });
53
54                    (
55                        true,
56                        ReserveResult::Permit(ConnectionPermit {
57                            connections: self.connections.clone(),
58                            key,
59                            id,
60                        }),
61                    )
62                }
63                Entry::Occupied(entry) => {
64                    let data = entry.get();
65
66                    (
67                        false,
68                        ReserveResult::Occupied(data.on_release.subscribe(), data.source, data.id),
69                    )
70                }
71            })
72    }
73
74    pub fn peer_info_collector(&self) -> PeerInfoCollector {
75        PeerInfoCollector(self.connections.clone())
76    }
77
78    pub fn get_peer_info(&self, addr: PeerAddr) -> Option<PeerInfo> {
79        self.with(addr, |data| data.peer_info(addr))
80    }
81
82    pub fn get_peer_key(&self, addr: PeerAddr) -> Option<usize> {
83        self.with(addr, |data| match data.state {
84            InnerPeerState::Active { peer_key, .. } => Some(peer_key),
85            InnerPeerState::Known | InnerPeerState::Connecting | InnerPeerState::Handshaking => {
86                None
87            }
88        })
89        .flatten()
90    }
91
92    fn with<F, R>(&self, addr: PeerAddr, f: F) -> Option<R>
93    where
94        F: FnOnce(&ConnectionData) -> R,
95    {
96        let connections = self.connections.borrow();
97
98        connections
99            .get(&ConnectionKey {
100                addr,
101                dir: ConnectionDirection::Incoming,
102            })
103            .or_else(|| {
104                connections.get(&ConnectionKey {
105                    addr,
106                    dir: ConnectionDirection::Outgoing,
107                })
108            })
109            .map(f)
110    }
111
112    pub fn subscribe(&self) -> watch::Receiver<HashMap<ConnectionKey, ConnectionData>> {
113        self.connections.subscribe()
114    }
115}
116
117/// Unique identifier of a connection. Connections are mostly already identified by the peer address
118/// and direction (incoming / outgoing), but this type allows to distinguish even connections with
119/// the same address/direction but that were established in two separate occasions.
120#[derive(Clone, Copy, Eq, PartialEq, Debug)]
121#[repr(transparent)]
122pub(super) struct ConnectionId(u64);
123
124impl ConnectionId {
125    pub fn next() -> Self {
126        static NEXT: AtomicU64 = AtomicU64::new(0);
127        Self(NEXT.fetch_add(1, Ordering::Relaxed))
128    }
129}
130
131impl fmt::Display for ConnectionId {
132    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
133        write!(f, "{}", self.0)
134    }
135}
136
137pub(super) enum ReserveResult {
138    Permit(ConnectionPermit),
139    // Use the receiver to get notified when the existing permit is destroyed.
140    Occupied(AwaitDrop, PeerSource, ConnectionId),
141}
142
143#[derive(Clone)]
144pub struct PeerInfoCollector(watch::Sender<HashMap<ConnectionKey, ConnectionData>>);
145
146impl PeerInfoCollector {
147    pub fn collect(&self) -> Vec<PeerInfo> {
148        self.0
149            .borrow()
150            .iter()
151            .map(|(key, data)| data.peer_info(key.addr))
152            .collect()
153    }
154}
155
156/// Connection permit that prevents another connection to the same peer (socket address) to be
157/// established as long as it remains in scope.
158pub(super) struct ConnectionPermit {
159    connections: watch::Sender<HashMap<ConnectionKey, ConnectionData>>,
160    key: ConnectionKey,
161    id: ConnectionId,
162}
163
164impl ConnectionPermit {
165    pub fn mark_as_connecting(&self) {
166        self.set_state(InnerPeerState::Connecting);
167    }
168
169    pub fn mark_as_handshaking(&self) {
170        self.set_state(InnerPeerState::Handshaking);
171    }
172
173    pub fn mark_as_active(&self, runtime_id: PublicRuntimeId, peer_key: usize) {
174        self.set_state(InnerPeerState::Active {
175            id: runtime_id,
176            since: SystemTime::now(),
177            peer_key,
178        });
179    }
180
181    fn set_state(&self, new_state: InnerPeerState) {
182        self.connections.send_if_modified(|connections| {
183            // unwrap is ok because if `self` exists then the entry should exists as well.
184            let peer = connections.get_mut(&self.key).unwrap();
185
186            if peer.state != new_state {
187                peer.state = new_state;
188                true
189            } else {
190                false
191            }
192        });
193    }
194
195    /// Returns a `AwaitDrop` that gets notified when this permit gets released.
196    pub fn released(&self) -> AwaitDrop {
197        self.with(|data| data.on_release.subscribe())
198    }
199
200    pub fn addr(&self) -> PeerAddr {
201        self.key.addr
202    }
203
204    pub fn id(&self) -> ConnectionId {
205        self.id
206    }
207
208    pub fn source(&self) -> PeerSource {
209        self.with(|data| data.source)
210    }
211
212    pub fn byte_counters(&self) -> Arc<ByteCounters> {
213        self.with(|data| data.stats_tracker.bytes.clone())
214    }
215
216    fn with<F, R>(&self, f: F) -> R
217    where
218        F: FnOnce(&ConnectionData) -> R,
219    {
220        // unwrap is ok because if `self` exists then the entry exists as well.
221        f(self.connections.borrow().get(&self.key).unwrap())
222    }
223}
224
225impl fmt::Debug for ConnectionPermit {
226    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
227        f.debug_struct("ConnectionPermit")
228            .field("key", &self.key)
229            .field("id", &self.id)
230            .finish_non_exhaustive()
231    }
232}
233
234impl Drop for ConnectionPermit {
235    fn drop(&mut self) {
236        self.connections.send_if_modified(|connections| {
237            let Entry::Occupied(entry) = connections.entry(self.key) else {
238                return false;
239            };
240
241            if entry.get().id != self.id {
242                return false;
243            }
244
245            entry.remove();
246            true
247        });
248    }
249}
250
251#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
252pub(super) struct ConnectionKey {
253    addr: PeerAddr,
254    dir: ConnectionDirection,
255}
256
257pub(super) struct ConnectionData {
258    id: ConnectionId,
259    state: InnerPeerState,
260    source: PeerSource,
261    stats_tracker: StatsTracker,
262    on_release: DropAwaitable,
263}
264
265impl ConnectionData {
266    fn peer_info(&self, addr: PeerAddr) -> PeerInfo {
267        let stats = self.stats_tracker.read();
268
269        PeerInfo {
270            addr,
271            source: self.source,
272            state: self.state.to_peer_state(),
273            stats,
274        }
275    }
276}
277
278#[derive(Eq, PartialEq)]
279enum InnerPeerState {
280    Known,
281    Connecting,
282    Handshaking,
283    Active {
284        id: PublicRuntimeId,
285        since: SystemTime,
286        peer_key: usize,
287    },
288}
289
290impl InnerPeerState {
291    fn to_peer_state(&self) -> PeerState {
292        match self {
293            Self::Known => PeerState::Known,
294            Self::Connecting => PeerState::Connecting,
295            Self::Handshaking => PeerState::Handshaking,
296            Self::Active { id, since, .. } => PeerState::Active {
297                id: *id,
298                since: *since,
299            },
300        }
301    }
302}