use std::sync::Arc;
use std::sync::atomic::{AtomicBool, AtomicU8, Ordering};
use std::thread::JoinHandle;
use std::time::Duration;
use parking_lot::Mutex;
use crate::protocol::FrameType;
use crate::protocol::command::query_payload;
use crate::protocol::opcode::Q_HEALTH;
use super::counters::Counters;
use super::reconcile::DesiredState;
use super::slot::TransportSlot;
use super::write_frame;
const KEEPALIVE_STOP_POLL: Duration = Duration::from_millis(20);
pub(crate) struct KeepaliveCtx {
pub(crate) transport: Arc<TransportSlot>,
pub(crate) write_lock: Arc<Mutex<()>>,
pub(crate) seq: Arc<AtomicU8>,
pub(crate) counters: Arc<Counters>,
pub(crate) desired: Arc<Mutex<DesiredState>>,
pub(crate) stop: Arc<AtomicBool>,
pub(crate) cadence: Duration,
}
pub(crate) fn spawn_keepalive(ctx: KeepaliveCtx) -> JoinHandle<()> {
std::thread::Builder::new()
.name("medius-keepalive".into())
.spawn(move || keepalive_loop(ctx))
.expect("spawn medius-keepalive thread")
}
fn keepalive_loop(ctx: KeepaliveCtx) {
loop {
if sleep_cadence(&ctx.stop, ctx.cadence) {
return;
}
let idle = ctx.desired.lock().is_idle();
if idle {
continue;
}
let seq = ctx.seq.fetch_add(1, Ordering::Relaxed);
let _ = write_frame(
&ctx.transport,
&ctx.write_lock,
&ctx.counters,
seq,
FrameType::Query,
&query_payload(Q_HEALTH),
);
}
}
fn sleep_cadence(stop: &AtomicBool, cadence: Duration) -> bool {
let mut remaining = cadence;
while !remaining.is_zero() {
if stop.load(Ordering::SeqCst) {
return true;
}
let slice = remaining.min(KEEPALIVE_STOP_POLL);
std::thread::sleep(slice);
remaining -= slice;
}
stop.load(Ordering::SeqCst)
}