1use crate::{
2 api::{ConnectAddr, ListenAddr, NetworkConnectError, Participant},
3 channel::Protocols,
4 metrics::{NetworkMetrics, ProtocolInfo},
5 participant::{B2sPrioStatistic, BParticipant, S2bCreateChannel, S2bShutdownBparticipant},
6};
7use futures_util::StreamExt;
8use hashbrown::HashMap;
9use network_protocol::{Cid, Pid, ProtocolMetricCache, ProtocolMetrics};
10#[cfg(feature = "metrics")]
11use prometheus::Registry;
12use rand::RngExt;
13use std::{
14 sync::{
15 Arc,
16 atomic::{AtomicBool, AtomicU64, Ordering},
17 },
18 time::Duration,
19};
20use tokio::{
21 io,
22 sync::{Mutex, mpsc, oneshot},
23};
24use tokio_stream::wrappers::UnboundedReceiverStream;
25use tracing::*;
26
27#[derive(Debug)]
37struct ParticipantInfo {
38 secret: u128,
39 #[expect(dead_code)]
40 s2b_create_channel_s: mpsc::UnboundedSender<S2bCreateChannel>,
41 s2b_shutdown_bparticipant_s: Option<oneshot::Sender<S2bShutdownBparticipant>>,
42}
43
44type A2sListen = (ListenAddr, oneshot::Sender<io::Result<()>>);
45pub(crate) type A2sConnect = (
46 ConnectAddr,
47 oneshot::Sender<Result<Participant, NetworkConnectError>>,
48);
49type A2sDisconnect = (Pid, S2bShutdownBparticipant);
50
51#[derive(Debug)]
52struct ControlChannels {
53 a2s_listen_r: mpsc::UnboundedReceiver<A2sListen>,
54 a2s_connect_r: mpsc::UnboundedReceiver<A2sConnect>,
55 a2s_scheduler_shutdown_r: oneshot::Receiver<()>,
56 a2s_disconnect_r: mpsc::UnboundedReceiver<A2sDisconnect>,
57 b2s_prio_statistic_r: mpsc::UnboundedReceiver<B2sPrioStatistic>,
58}
59
60#[derive(Debug, Clone)]
61struct ParticipantChannels {
62 s2a_connected_s: mpsc::UnboundedSender<Participant>,
63 a2s_disconnect_s: mpsc::UnboundedSender<A2sDisconnect>,
64 b2s_prio_statistic_s: mpsc::UnboundedSender<B2sPrioStatistic>,
65}
66
67#[derive(Debug)]
68pub struct Scheduler {
69 local_pid: Pid,
70 local_secret: u128,
71 closed: AtomicBool,
72 run_channels: Option<ControlChannels>,
73 participant_channels: Arc<Mutex<Option<ParticipantChannels>>>,
74 participants: Arc<Mutex<HashMap<Pid, ParticipantInfo>>>,
75 channel_ids: Arc<AtomicU64>,
76 channel_listener: Mutex<HashMap<ProtocolInfo, oneshot::Sender<()>>>,
77 metrics: Arc<NetworkMetrics>,
78 protocol_metrics: Arc<ProtocolMetrics>,
79 output_limit: usize,
80}
81
82impl Scheduler {
83 pub fn new(
84 local_pid: Pid,
85 #[cfg(feature = "metrics")] registry: Option<&Registry>,
86 output_limit: usize,
87 ) -> (
88 Self,
89 mpsc::UnboundedSender<A2sListen>,
90 mpsc::UnboundedSender<A2sConnect>,
91 mpsc::UnboundedReceiver<Participant>,
92 oneshot::Sender<()>,
93 ) {
94 let (a2s_listen_s, a2s_listen_r) = mpsc::unbounded_channel::<A2sListen>();
95 let (a2s_connect_s, a2s_connect_r) = mpsc::unbounded_channel::<A2sConnect>();
96 let (s2a_connected_s, s2a_connected_r) = mpsc::unbounded_channel::<Participant>();
97 let (a2s_scheduler_shutdown_s, a2s_scheduler_shutdown_r) = oneshot::channel::<()>();
98 let (a2s_disconnect_s, a2s_disconnect_r) = mpsc::unbounded_channel::<A2sDisconnect>();
99 let (b2s_prio_statistic_s, b2s_prio_statistic_r) =
100 mpsc::unbounded_channel::<B2sPrioStatistic>();
101
102 let run_channels = Some(ControlChannels {
103 a2s_listen_r,
104 a2s_connect_r,
105 a2s_scheduler_shutdown_r,
106 a2s_disconnect_r,
107 b2s_prio_statistic_r,
108 });
109
110 let participant_channels = ParticipantChannels {
111 s2a_connected_s,
112 a2s_disconnect_s,
113 b2s_prio_statistic_s,
114 };
115
116 let metrics = Arc::new(NetworkMetrics::new(&local_pid).unwrap());
117 let protocol_metrics = Arc::new(ProtocolMetrics::new().unwrap());
118
119 #[cfg(feature = "metrics")]
120 {
121 if let Some(registry) = registry {
122 metrics.register(registry).unwrap();
123 protocol_metrics.register(registry).unwrap();
124 }
125 }
126
127 let mut rng = rand::rng();
128 let local_secret: u128 = rng.random();
129
130 (
131 Self {
132 local_pid,
133 local_secret,
134 closed: AtomicBool::new(false),
135 run_channels,
136 participant_channels: Arc::new(Mutex::new(Some(participant_channels))),
137 participants: Arc::new(Mutex::new(HashMap::new())),
138 channel_ids: Arc::new(AtomicU64::new(0)),
139 channel_listener: Mutex::new(HashMap::new()),
140 metrics,
141 protocol_metrics,
142 output_limit,
143 },
144 a2s_listen_s,
145 a2s_connect_s,
146 s2a_connected_r,
147 a2s_scheduler_shutdown_s,
148 )
149 }
150
151 pub async fn run(mut self) {
152 let run_channels = self
153 .run_channels
154 .take()
155 .expect("run() can only be called once");
156
157 tokio::join!(
158 self.listen_mgr(run_channels.a2s_listen_r),
159 self.connect_mgr(run_channels.a2s_connect_r),
160 self.disconnect_mgr(run_channels.a2s_disconnect_r),
161 self.prio_adj_mgr(run_channels.b2s_prio_statistic_r),
162 self.scheduler_shutdown_mgr(run_channels.a2s_scheduler_shutdown_r),
163 );
164 }
165
166 async fn listen_mgr(&self, a2s_listen_r: mpsc::UnboundedReceiver<A2sListen>) {
167 trace!("Start listen_mgr");
168 let a2s_listen_r = UnboundedReceiverStream::new(a2s_listen_r);
169 a2s_listen_r
170 .for_each_concurrent(None, |(address, s2a_listen_result_s)| {
171 let address = address;
172 let cids = Arc::clone(&self.channel_ids);
173
174 #[cfg(feature = "metrics")]
175 let mcache = self.metrics.connect_requests_cache(&address);
176
177 debug!(?address, "Got request to open a channel_creator");
178 self.metrics.listen_request(&address);
179 let (s2s_stop_listening_s, s2s_stop_listening_r) = oneshot::channel::<()>();
180 let (c2s_protocol_s, mut c2s_protocol_r) = mpsc::unbounded_channel();
181 let metrics = Arc::clone(&self.protocol_metrics);
182
183 async move {
184 self.channel_listener
185 .lock()
186 .await
187 .insert(address.clone().into(), s2s_stop_listening_s);
188
189 #[cfg(feature = "metrics")]
190 mcache.inc();
191
192 let res = match address {
193 ListenAddr::Tcp(addr) => {
194 Protocols::with_tcp_listen(
195 addr,
196 cids,
197 metrics,
198 s2s_stop_listening_r,
199 c2s_protocol_s,
200 )
201 .await
202 },
203 #[cfg(feature = "quic")]
204 ListenAddr::Quic(addr, ref server_config) => {
205 Protocols::with_quic_listen(
206 addr,
207 server_config.clone(),
208 cids,
209 metrics,
210 s2s_stop_listening_r,
211 c2s_protocol_s,
212 )
213 .await
214 },
215 ListenAddr::Mpsc(addr) => {
216 Protocols::with_mpsc_listen(
217 addr,
218 cids,
219 metrics,
220 s2s_stop_listening_r,
221 c2s_protocol_s,
222 )
223 .await
224 },
225 _ => unimplemented!(),
226 };
227 let _ = s2a_listen_result_s.send(res);
228
229 while let Some((prot, con_addr, cid)) = c2s_protocol_r.recv().await {
230 self.init_protocol(prot, con_addr, cid, None, true).await;
231 }
232 }
233 })
234 .await;
235 trace!("Stop listen_mgr");
236 }
237
238 async fn connect_mgr(&self, mut a2s_connect_r: mpsc::UnboundedReceiver<A2sConnect>) {
239 trace!("Start connect_mgr");
240 while let Some((addr, pid_sender)) = a2s_connect_r.recv().await {
241 let cid = self.channel_ids.fetch_add(1, Ordering::Relaxed);
242 let metrics =
243 ProtocolMetricCache::new(&cid.to_string(), Arc::clone(&self.protocol_metrics));
244 self.metrics.connect_request(&addr);
245 let protocol = match addr.clone() {
246 ConnectAddr::Tcp(addr) => Protocols::with_tcp_connect(addr, metrics).await,
247 #[cfg(feature = "quic")]
248 ConnectAddr::Quic(addr, ref config, name) => {
249 Protocols::with_quic_connect(addr, config.clone(), name, metrics).await
250 },
251 ConnectAddr::Mpsc(addr) => Protocols::with_mpsc_connect(addr, metrics).await,
252 _ => unimplemented!(),
253 };
254 let protocol = match protocol {
255 Ok(p) => p,
256 Err(e) => {
257 pid_sender.send(Err(e)).unwrap();
258 continue;
259 },
260 };
261 self.init_protocol(protocol, addr, cid, Some(pid_sender), false)
262 .await;
263 }
264 trace!("Stop connect_mgr");
265 }
266
267 async fn disconnect_mgr(&self, a2s_disconnect_r: mpsc::UnboundedReceiver<A2sDisconnect>) {
268 trace!("Start disconnect_mgr");
269
270 let a2s_disconnect_r = UnboundedReceiverStream::new(a2s_disconnect_r);
271 a2s_disconnect_r
272 .for_each_concurrent(
273 None,
274 |(pid, (timeout_time, return_once_successful_shutdown))| {
275 let participants = Arc::clone(&self.participants);
281 async move {
282 trace!(?pid, "Got request to close participant");
283 let pi = participants.lock().await.remove(&pid);
284 trace!(?pid, "dropped participants lock");
285 let r = if let Some(mut pi) = pi {
286 let (finished_sender, finished_receiver) = oneshot::channel();
287 let _ = pi
290 .s2b_shutdown_bparticipant_s
291 .take()
292 .unwrap()
293 .send((timeout_time, finished_sender));
294 drop(pi);
295 trace!(?pid, "dropped bparticipant, waiting for finish");
296 let e = finished_receiver.await.unwrap_or(Ok(()));
298 trace!(?pid, "waiting completed");
299 return_once_successful_shutdown.send(e)
301 } else {
302 debug!(?pid, "Looks like participant is already dropped");
303 return_once_successful_shutdown.send(Ok(()))
304 };
305 if r.is_err() {
306 trace!(?pid, "Closed participant with timeout");
307 } else {
308 trace!(?pid, "Closed participant");
309 }
310 }
311 },
312 )
313 .await;
314 trace!("Stop disconnect_mgr");
315 }
316
317 async fn prio_adj_mgr(
318 &self,
319 mut b2s_prio_statistic_r: mpsc::UnboundedReceiver<B2sPrioStatistic>,
320 ) {
321 trace!("Start prio_adj_mgr");
322 while let Some((_pid, _frame_cnt, _unused)) = b2s_prio_statistic_r.recv().await {
323
324 }
326 trace!("Stop prio_adj_mgr");
327 }
328
329 async fn scheduler_shutdown_mgr(&self, a2s_scheduler_shutdown_r: oneshot::Receiver<()>) {
330 trace!("Start scheduler_shutdown_mgr");
331 if a2s_scheduler_shutdown_r.await.is_err() {
332 warn!("Schedule shutdown got triggered because a2s_scheduler_shutdown_r failed");
333 };
334 info!("Shutdown of scheduler requested");
335 self.closed.store(true, Ordering::SeqCst);
336 debug!("Shutting down all BParticipants gracefully");
337 let mut participants = self.participants.lock().await;
338 let waitings = participants
339 .drain()
340 .map(|(pid, mut pi)| {
341 trace!(?pid, "Shutting down BParticipants");
342 let (finished_sender, finished_receiver) = oneshot::channel();
343 pi.s2b_shutdown_bparticipant_s
344 .take()
345 .unwrap()
346 .send((Duration::from_secs(120), finished_sender))
347 .unwrap();
348 (pid, finished_receiver)
349 })
350 .collect::<Vec<_>>();
351 drop(participants);
352 debug!("Wait for partiticipants to be shut down");
353 for (pid, recv) in waitings {
354 if let Err(e) = recv.await {
355 error!(
356 ?pid,
357 ?e,
358 "Failed to finish sending all remaining messages to participant when shutting \
359 down"
360 );
361 };
362 }
363 debug!("shutting down protocol listeners");
364 for (addr, end_channel_sender) in self.channel_listener.lock().await.drain() {
365 trace!(?addr, "stopping listen on protocol");
366 if let Err(e) = end_channel_sender.send(()) {
367 warn!(?addr, ?e, "listener crashed/disconnected already");
368 }
369 }
370 debug!("Scheduler shut down gracefully");
371 self.participant_channels.lock().await.take();
374
375 trace!("Stop scheduler_shutdown_mgr");
376 }
377
378 async fn init_protocol(
379 &self,
380 mut protocol: Protocols,
381 con_addr: ConnectAddr, cid: Cid,
383 s2a_return_pid_s: Option<oneshot::Sender<Result<Participant, NetworkConnectError>>>,
384 send_handshake: bool,
385 ) {
386 let participant_channels = self.participant_channels.lock().await.clone().unwrap();
393 let participants = Arc::clone(&self.participants);
398 let metrics = Arc::clone(&self.metrics);
399 let local_pid = self.local_pid;
400 let local_secret = self.local_secret;
401 let output_limit = self.output_limit;
402 tokio::spawn(
404 async move {
405 trace!(?cid, "Open channel and be ready for Handshake");
406 use network_protocol::InitProtocol;
407 let init_result = protocol
408 .initialize(send_handshake, local_pid, local_secret)
409 .instrument(info_span!("handshake", ?cid))
410 .await;
411 match init_result {
412 Ok((pid, sid, secret)) => {
413 trace!(
414 ?cid,
415 ?pid,
416 "Detected that my channel is ready!, activating it :)"
417 );
418 let mut participants = participants.lock().await;
419 if !participants.contains_key(&pid) {
420 debug!(?cid, "New participant connected via a channel");
421 let (
422 bparticipant,
423 a2b_open_stream_s,
424 b2a_stream_opened_r,
425 b2a_event_r,
426 s2b_create_channel_s,
427 s2b_shutdown_bparticipant_s,
428 b2a_bandwidth_stats_r,
429 ) = BParticipant::new(
430 local_pid,
431 pid,
432 sid,
433 Arc::clone(&metrics),
434 output_limit,
435 );
436
437 let participant = Participant::new(
438 local_pid,
439 pid,
440 a2b_open_stream_s,
441 b2a_stream_opened_r,
442 b2a_event_r,
443 b2a_bandwidth_stats_r,
444 participant_channels.a2s_disconnect_s,
445 );
446
447 #[cfg(feature = "metrics")]
448 metrics.participants_connected_total.inc();
449 participants.insert(pid, ParticipantInfo {
450 secret,
451 s2b_create_channel_s: s2b_create_channel_s.clone(),
452 s2b_shutdown_bparticipant_s: Some(s2b_shutdown_bparticipant_s),
453 });
454 drop(participants);
455 trace!("dropped participants lock");
456 let p = pid;
457 tokio::spawn(
458 bparticipant
459 .run(participant_channels.b2s_prio_statistic_s)
460 .instrument(info_span!("remote", ?p)),
461 );
462 let (b2s_create_channel_done_s, b2s_create_channel_done_r) =
464 oneshot::channel();
465 s2b_create_channel_s
467 .send((cid, sid, protocol, con_addr, b2s_create_channel_done_s))
468 .unwrap();
469 b2s_create_channel_done_r.await.unwrap();
470 if let Some(pid_oneshot) = s2a_return_pid_s {
471 pid_oneshot.send(Ok(participant)).unwrap();
473 } else {
474 if participant_channels
476 .s2a_connected_s
477 .send(participant)
478 .is_err()
479 {
480 warn!("seems like Network already got closed");
481 };
482 }
483 } else {
484 let pi = &participants[&pid];
485 trace!(
486 ?cid,
487 "2nd+ channel of participant, going to compare security ids"
488 );
489 if pi.secret != secret {
490 warn!(
491 ?cid,
492 ?pid,
493 ?secret,
494 "Detected incompatible Secret!, this is probably an attack!"
495 );
496 error!(?cid, "Just dropping here, TODO handle this correctly!");
497 if let Some(pid_oneshot) = s2a_return_pid_s {
499 pid_oneshot
501 .send(Err(NetworkConnectError::InvalidSecret))
502 .unwrap();
503 }
504 return;
505 }
506 error!(
507 ?cid,
508 "Ufff i cant answer the pid_oneshot. as i need to create the SAME \
509 participant. maybe switch to ARC"
510 );
511 }
512 },
515 Err(e) => {
516 debug!(?cid, ?e, "Handshake from a new connection failed");
517 #[cfg(feature = "metrics")]
518 metrics.failed_handshakes_total.inc();
519 if let Some(pid_oneshot) = s2a_return_pid_s {
520 trace!(?cid, "returning the Err to api who requested the connect");
522 pid_oneshot
523 .send(Err(NetworkConnectError::Handshake(e)))
524 .unwrap();
525 }
526 },
527 }
528 }
529 .instrument(info_span!("")),
530 ); }
532}