use super::*;
#[allow(clippy::too_many_arguments)]
pub(super) fn inbound_loop(
rank: usize,
stream: &mut TcpStream,
salt: &SessionSalt,
shutdown: &Arc<AtomicBool>,
control_tx: &mpsc::Sender<ControlMsg>,
local_dead_ranks: &Arc<crate::distributed::controller::DeadRanks>,
nccl_session_mailbox: &Arc<std::sync::Mutex<Option<PendingNcclSession>>>,
timing_tx: &mpsc::Sender<TimingMsg>,
coord_liveness_timeout_secs: u64,
) {
let inject_shutdown = || {
let _ = control_tx.send(ControlMsg::Shutdown);
};
let poison_peers = || {
for r in 0..local_dead_ranks.world_size() {
if r != rank {
local_dead_ranks.declare_dead(r);
}
}
};
let mut clean_shutdown_seen = false;
let coord_liveness_deadline = Duration::from_secs(coord_liveness_timeout_secs);
let mut last_inbound = std::time::Instant::now();
loop {
if shutdown.load(Ordering::SeqCst) {
return;
}
match try_read_len_framed(stream) {
Ok(LenFramedRead::Blob(blob)) => {
let frame = match ControlFrame::read_from(&mut blob.as_slice(), salt) {
Ok(Some(f)) => f,
Ok(None) => {
eprintln!("cluster_worker: inbound r{rank} truncated ControlFrame");
if !clean_shutdown_seen {
poison_peers();
}
inject_shutdown();
return;
}
Err(e) => {
eprintln!(
"cluster_worker: inbound r{rank} ControlFrame parse: {e}"
);
if !clean_shutdown_seen {
poison_peers();
}
inject_shutdown();
return;
}
};
last_inbound = std::time::Instant::now();
match frame.kind {
MsgKind::Control => match frame.decode::<ControlMsgWire>() {
Ok(wire) => match wire {
ControlMsgWire::CoordHeartbeat => {}
ControlMsgWire::DeclareDead { rank: dead_r } => {
local_dead_ranks.declare_dead(dead_r as usize);
}
ControlMsgWire::NewNcclSession {
uid_bytes,
new_rank,
new_world_size,
} => {
let pending = PendingNcclSession {
uid_bytes,
new_rank: new_rank as usize,
new_world_size: new_world_size as usize,
};
if let Ok(mut slot) = nccl_session_mailbox.lock() {
*slot = Some(pending);
}
}
ControlMsgWire::RequestNewNcclId => {
match crate::distributed::nccl::NcclUniqueId::new() {
Ok(uid) => {
let uid_bytes = uid.as_bytes().to_vec();
let _ = timing_tx.send(
TimingMsg::NewNcclIdGenerated {
rank,
uid_bytes,
},
);
}
Err(e) => {
eprintln!(
"cluster_worker: inbound r{rank} \
NcclUniqueId::new failed: {e}"
);
}
}
}
ControlMsgWire::Update { next_plan, .. } => {
if let Some(plan) = next_plan {
let msg = ControlMsg::StartEpoch(EpochPlan {
epoch: plan.epoch as usize,
partition_offset: plan.partition_offset as usize,
partition_size: plan.partition_size as usize,
});
if control_tx.send(msg).is_err() {
return;
}
}
}
other => match control_wire_to_msg(other) {
Ok(Some(msg)) => {
if matches!(msg, ControlMsg::Shutdown) {
clean_shutdown_seen = true;
}
if control_tx.send(msg).is_err() {
return;
}
}
Ok(None) => {
}
Err(e) => {
eprintln!(
"cluster_worker: inbound r{rank} control_wire_to_msg: {e}"
);
if !clean_shutdown_seen {
poison_peers();
}
inject_shutdown();
return;
}
},
},
Err(e) => {
eprintln!(
"cluster_worker: inbound r{rank} decode ControlMsgWire: {e}"
);
if !clean_shutdown_seen {
poison_peers();
}
inject_shutdown();
return;
}
},
other => {
eprintln!(
"cluster_worker: inbound r{rank} unexpected MsgKind {other:?} \
on coord→rank channel; dropping"
);
}
}
}
Ok(LenFramedRead::WouldBlock) => {
if last_inbound.elapsed() >= coord_liveness_deadline {
eprintln!(
"cluster_worker: inbound r{rank} coordinator silent for \
{coord_liveness_timeout_secs}s (presumed wedged); \
declaring peers dead and shutting down"
);
if !clean_shutdown_seen {
poison_peers();
}
inject_shutdown();
return;
}
continue;
}
Ok(LenFramedRead::Eof) => {
if !clean_shutdown_seen {
poison_peers();
}
inject_shutdown();
return;
}
Err(e) => {
crate::verbose!("cluster_worker: inbound r{rank} wire error: {e}");
if !clean_shutdown_seen {
poison_peers();
}
inject_shutdown();
return;
}
}
}
}
const HEARTBEAT_CADENCE_MS: u64 = 1_000;
pub(super) fn heartbeat_loop(
rank: usize,
timing_tx: mpsc::Sender<TimingMsg>,
shutdown: Arc<AtomicBool>,
) {
let mut step_count: usize = 0;
while !shutdown.load(Ordering::SeqCst) {
step_count = step_count.saturating_add(1);
if timing_tx
.send(TimingMsg::Heartbeat { rank, step_count })
.is_err()
{
return;
}
thread::sleep(Duration::from_millis(HEARTBEAT_CADENCE_MS));
}
}
const NCCL_WATCHDOG_POLL_MS: u64 = 100;
pub(super) fn nccl_watchdog_loop(
rank: usize,
abort_slot: crate::distributed::ddp_run::NcclAbortSlot,
local_dead_ranks: Arc<crate::distributed::controller::DeadRanks>,
shutdown: Arc<AtomicBool>,
) {
let mut last_dead_count = 0usize;
while !shutdown.load(Ordering::SeqCst) {
let now_dead = local_dead_ranks.dead_count();
if now_dead > last_dead_count {
crate::verbose!(
" cluster_worker: rank {} NCCL watchdog: dead_count {} -> {}, \
aborting NCCL comm",
rank,
last_dead_count,
now_dead,
);
let handle = abort_slot
.lock()
.expect("nccl abort slot poisoned")
.clone();
match handle {
Some(h) => {
if let Err(e) = h.abort() {
eprintln!(
"cluster_worker: rank {} NCCL watchdog abort error: {}",
rank, e,
);
}
}
None => {
}
}
last_dead_count = now_dead;
}
thread::sleep(Duration::from_millis(NCCL_WATCHDOG_POLL_MS));
}
}
pub(super) fn outbound_loop(
rank: usize,
stream: &mut TcpStream,
salt: &SessionSalt,
shutdown: &Arc<AtomicBool>,
timing_rx: mpsc::Receiver<TimingMsg>,
metrics_rx: mpsc::Receiver<crate::distributed::ddp_run::MetricsMsg>,
) {
let pending = crate::distributed::cluster_dashboard_emit::drain();
let envelope = crate::distributed::LocalCluster::from_env().ok().flatten();
let assigned_device_idx: Option<u8> = envelope
.as_ref()
.and_then(|c| c.my_rank().ok())
.and_then(|(_, dev)| match dev {
crate::tensor::Device::CUDA(idx) => Some(idx),
_ => None,
});
let has_setup_payload = pending.port.is_some()
|| pending.svg.is_some()
|| pending.metadata_json.is_some()
|| pending.hardware.is_some();
if has_setup_payload {
emit_dashboard_setup(stream, salt, rank, &pending, assigned_device_idx);
}
let want_resources = pending.port.is_some()
|| envelope.as_ref().is_some_and(|c| c.rank_resources);
let resource_sampler: Option<std::sync::Mutex<crate::monitor::ResourceSampler>> =
if want_resources {
Some(std::sync::Mutex::new(
crate::monitor::ResourceSampler::new(),
))
} else {
None
};
let mut last_resource_emit: Option<std::time::Instant> = None;
loop {
if shutdown.load(Ordering::SeqCst) {
while let Ok(msg) = timing_rx.try_recv() {
let _ = write_timing(stream, salt, msg);
}
while let Ok(msg) = metrics_rx.try_recv() {
let _ = write_metrics(stream, salt, msg, resource_sampler.as_ref(), assigned_device_idx);
}
return;
}
match metrics_rx.try_recv() {
Ok(msg) => {
if let Err(e) = write_metrics(stream, salt, msg, resource_sampler.as_ref(), assigned_device_idx) {
crate::verbose!(
"cluster_worker: outbound r{rank} metrics write error: {e}"
);
return;
}
continue;
}
Err(mpsc::TryRecvError::Empty) => {}
Err(mpsc::TryRecvError::Disconnected) => {
}
}
if let Some(sampler) = resource_sampler.as_ref() {
let due = last_resource_emit.is_none_or(|t| {
t.elapsed() >= Duration::from_millis(super::RESOURCE_SAMPLE_INTERVAL_MS)
});
if due {
let sample = {
let mut s = sampler.lock().unwrap();
let mut sample = s.sample();
trim_sample_to_assigned_device(&mut sample, assigned_device_idx);
sample
};
last_resource_emit = Some(std::time::Instant::now());
if let Err(e) = write_timing_wire(
stream,
salt,
&crate::distributed::wire::TimingMsgWire::ResourceSample {
rank: rank as u64,
sample: sample.into(),
},
) {
crate::verbose!(
"cluster_worker: outbound r{rank} resource write error: {e}"
);
return;
}
}
}
match timing_rx.recv_timeout(Duration::from_millis(250)) {
Ok(msg) => {
if let Err(e) = write_timing(stream, salt, msg) {
crate::verbose!("cluster_worker: outbound r{rank} write error: {e}");
return;
}
}
Err(mpsc::RecvTimeoutError::Timeout) => continue,
Err(mpsc::RecvTimeoutError::Disconnected) => {
return;
}
}
}
}
pub(super) fn write_framed_control<W: std::io::Write>(stream: &mut W, frame: &ControlFrame) -> Result<()> {
let mut buf = Vec::new();
frame.write_to(&mut buf)?;
write_len_framed(stream, &buf)
}
pub(super) fn write_timing(
stream: &mut TcpStream,
salt: &SessionSalt,
msg: TimingMsg,
) -> Result<()> {
write_timing_wire(stream, salt, &timing_msg_to_wire(msg))
}
pub(super) fn write_metrics(
stream: &mut TcpStream,
salt: &SessionSalt,
msg: crate::distributed::ddp_run::MetricsMsg,
resource_sampler: Option<&std::sync::Mutex<crate::monitor::ResourceSampler>>,
assigned_device_idx: Option<u8>,
) -> Result<()> {
let mut wire = metrics_msg_to_wire(msg);
if let Some(sampler) = resource_sampler {
let mut s = sampler.lock().unwrap();
let mut sample = s.sample();
trim_sample_to_assigned_device(&mut sample, assigned_device_idx);
wire.resources = Some(sample.into());
}
let frame = ControlFrame::encode(salt, MsgKind::Metrics, &wire)?;
write_framed_control(stream, &frame)
}
pub(super) fn trim_sample_to_assigned_device(
sample: &mut crate::monitor::ResourceSample,
assigned_device_idx: Option<u8>,
) {
if sample.gpus.len() > 1 {
if let Some(target) = assigned_device_idx {
sample.gpus.retain(|g| g.device_index == target);
}
}
}
pub(super) fn emit_dashboard_setup(
stream: &mut TcpStream,
salt: &SessionSalt,
rank: usize,
pending: &crate::distributed::cluster_dashboard_emit::PendingDashboardConfig,
assigned_device_idx: Option<u8>,
) {
use crate::distributed::wire::TimingMsgWire;
let rank_u64 = rank as u64;
let mut emit = |msg: TimingMsgWire| {
if let Err(e) = write_timing_wire(stream, salt, &msg) {
crate::verbose!(
"cluster_worker: outbound r{rank} dashboard emit failed: {e}",
);
}
};
if let Some(port) = pending.port {
emit(TimingMsgWire::DashboardRegister { rank: rank_u64, port });
}
if let Some(ref svg) = pending.svg {
emit(TimingMsgWire::DashboardSetSvg {
rank: rank_u64,
svg: svg.clone(),
label: pending.label.clone(),
hash: pending.hash.clone(),
});
}
if let Some(ref json) = pending.metadata_json {
emit(TimingMsgWire::DashboardSetMetadata {
rank: rank_u64,
json: json.clone(),
});
}
if let Some(ref hw) = pending.hardware {
let trimmed = trim_hardware_to_assigned(hw, assigned_device_idx);
emit(TimingMsgWire::DashboardSetHardware {
rank: rank_u64,
summary: trimmed,
});
}
}
pub(super) fn trim_hardware_to_assigned(
full: &str,
assigned_device_idx: Option<u8>,
) -> String {
let Some(target) = assigned_device_idx else {
return full.to_string();
};
let parts: Vec<&str> = full.split(" | ").collect();
if parts.len() < 2 {
return full.to_string();
}
let cpu = parts[0];
let gpu_idx = target as usize + 1;
match parts.get(gpu_idx) {
Some(gpu) => format!("{cpu} | {gpu}"),
None => cpu.to_string(),
}
}
pub(super) fn write_timing_wire(
stream: &mut TcpStream,
salt: &SessionSalt,
msg: &crate::distributed::wire::TimingMsgWire,
) -> Result<()> {
let frame = ControlFrame::encode(salt, MsgKind::Timing, msg)?;
write_framed_control(stream, &frame)
}