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
19pub(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 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#[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 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
156pub(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 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 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 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}