use std::{net::SocketAddr, sync::Arc};
use tokio::{
sync::{mpsc, watch},
time::Duration,
};
use tracing::{debug, error};
use super::ntp::{DaemonInfo, ExtensionField, Ntp, NtpIoError};
use super::{ClockDisruptionEvent, ControlRequest};
use crate::daemon::{async_ring_buffer, event, selected_clock::SelectedClockSource};
pub const INTERVAL: Duration = Duration::from_secs(16);
pub const SAMPLE_TIMEOUT: Duration = Duration::from_millis(100);
pub type Sender = (SocketAddr, async_ring_buffer::Sender<event::Ntp>);
pub type Receiver = (SocketAddr, async_ring_buffer::Receiver<event::Ntp>);
#[derive(Debug)]
pub struct IpAddrSource {
ntp: Ntp,
}
impl IpAddrSource {
pub fn construct(
address: SocketAddr,
event_sender: async_ring_buffer::Sender<event::Ntp>,
ctrl_receiver: mpsc::Receiver<ControlRequest>,
clock_disruption_receiver: watch::Receiver<ClockDisruptionEvent>,
selected_clock: Arc<SelectedClockSource>,
daemon_info: DaemonInfo,
) -> Self {
let extensions = vec![ExtensionField::Fec2V1(daemon_info)];
let ntp = Ntp::builder()
.event_sender(event_sender)
.ctrl_receiver(ctrl_receiver)
.clock_disruption_receiver(clock_disruption_receiver)
.selected_clock(selected_clock)
.extensions(extensions)
.socket_address(address)
.timeout(SAMPLE_TIMEOUT)
.interval(INTERVAL)
.build();
IpAddrSource { ntp }
}
#[tracing::instrument(level = "info", skip_all, fields(identifier = %self.ntp.socket_address()))]
pub async fn run(&mut self) -> Result<(), NtpIoError> {
debug!("Starting IP addr source sampling loop.");
loop {
let branches = self.ntp.select_branches();
tokio::select! {
biased; val = branches.clock_disruption_receiver.changed() => {
if let Err(e) = val {
error!(?e, "Clock disruption receiver dropped.");
break;
}
debug!("Received clock disruption signal.");
self.ntp.handle_disruption();
}
ctrl_req = branches.ctrl_receiver.recv() => {
match ctrl_req {
None => {
break;
},
Some(ControlRequest::Shutdown) => {
debug!("Received shutdown signal. Exiting.");
break;
},
}
}
_ = branches.interval.tick() => {
self.handle_interval_tick().await;
}
}
}
debug!("IP addr source runner exiting.");
Ok(())
}
async fn handle_interval_tick(&mut self) {
let ntp_event = match self.ntp.sample().await {
Err(e) => {
tracing::debug!(?e, "Failed to sample IP addr source.");
return;
}
Ok(ntp_event) => ntp_event,
};
if let Err(e) = self.ntp.send_event(&ntp_event) {
error!(?e, "channel closed. Not supported on this task");
panic!("unable to communicate with daemon. {e:?}");
}
}
}