#![forbid(unsafe_code)]
mod hostfile;
mod ring;
use std::sync::Arc;
use std::time::Duration;
use nuvec_core::distributed::{Group, set_default_group};
use nuvec_core::{Error, Result};
#[derive(Clone, Debug)]
#[non_exhaustive]
pub struct Options {
pub timeout: Duration,
}
impl Default for Options {
fn default() -> Self {
Self {
timeout: Duration::from_secs(30),
}
}
}
impl Options {
pub fn timeout(mut self, timeout: Duration) -> Self {
self.timeout = timeout;
self
}
}
pub fn init(strict: bool) -> Result<Group> {
let (hostfile, rank) = (std::env::var("MLX_HOSTFILE"), std::env::var("MLX_RANK"));
let (Ok(hostfile), Ok(rank)) = (hostfile, rank) else {
if strict {
return Err(Error::InvalidArgument(
"distributed: MLX_HOSTFILE and MLX_RANK are not set".into(),
));
}
return Ok(Group::single());
};
let text = std::fs::read_to_string(&hostfile)
.map_err(|e| Error::Io(format!("distributed: reading {hostfile}: {e}")))?;
let rank = rank
.trim()
.parse()
.map_err(|_| Error::InvalidArgument(format!("distributed: MLX_RANK {rank:?}")))?;
let mut options = Options::default();
if let Ok(secs) = std::env::var("NUVEC_DISTRIBUTED_TIMEOUT") {
let invalid = || {
Error::InvalidArgument(format!(
"distributed: NUVEC_DISTRIBUTED_TIMEOUT {secs:?} (seconds, a positive number)"
))
};
let parsed: f64 = secs.trim().parse().map_err(|_| invalid())?;
options.timeout = Duration::try_from_secs_f64(parsed)
.ok()
.filter(|d| !d.is_zero())
.ok_or_else(invalid)?;
}
let group = init_with(&hostfile::parse(&text)?, rank, &options)?;
set_default_group(group.clone());
Ok(group)
}
pub fn init_with(nodes: &[Vec<String>], rank: usize, options: &Options) -> Result<Group> {
if nodes.len() <= 1 {
return Ok(Group::single());
}
let ring = ring::Ring::connect(nodes, rank, options.timeout)?;
Ok(Group::new(Arc::new(ring)))
}