use std::net::IpAddr;
use std::sync::Mutex;
use kevy_rt::ReplicaInboxSender;
use crate::replica_runner::ReplicaRunner;
static REPLICA_SENDERS: Mutex<Vec<ReplicaInboxSender>> = Mutex::new(Vec::new());
static REPLICA_RUNNERS: Mutex<Vec<ReplicaRunner>> = Mutex::new(Vec::new());
static REPLICA_UPSTREAM: Mutex<Option<(IpAddr, u16)>> = Mutex::new(None);
pub(crate) fn install_senders(senders: Vec<ReplicaInboxSender>) {
let mut guard = REPLICA_SENDERS.lock().expect("REPLICA_SENDERS poisoned");
*guard = senders;
}
pub(crate) fn senders_clone() -> Vec<ReplicaInboxSender> {
REPLICA_SENDERS
.lock()
.expect("REPLICA_SENDERS poisoned")
.clone()
}
pub(crate) fn stop_runners() {
let mut guard = REPLICA_RUNNERS.lock().expect("REPLICA_RUNNERS poisoned");
let runners = std::mem::take(&mut *guard);
drop(guard); for r in runners {
r.shutdown();
}
*REPLICA_UPSTREAM.lock().expect("REPLICA_UPSTREAM poisoned") = None;
}
pub(crate) fn start_runners(upstream: (IpAddr, u16)) -> Result<(), &'static str> {
let senders = senders_clone();
if senders.is_empty() {
return Err("replica senders not installed (kevy::serve required)");
}
stop_runners();
let mut new_runners = Vec::with_capacity(senders.len());
let (host, port_base) = upstream;
for (shard_id, sender) in senders.into_iter().enumerate() {
let port = port_base.saturating_add(u16::try_from(shard_id).unwrap_or(u16::MAX));
let replica_id = format!("kevy-replica-{shard_id}");
new_runners.push(ReplicaRunner::spawn((host, port), replica_id, sender));
}
*REPLICA_RUNNERS.lock().expect("REPLICA_RUNNERS poisoned") = new_runners;
*REPLICA_UPSTREAM.lock().expect("REPLICA_UPSTREAM poisoned") = Some(upstream);
Ok(())
}
pub(crate) fn current_upstream() -> Option<(IpAddr, u16)> {
*REPLICA_UPSTREAM
.lock()
.expect("REPLICA_UPSTREAM poisoned")
}
#[cfg(test)]
pub(crate) static TEST_STATE_GUARD: std::sync::Mutex<()> = std::sync::Mutex::new(());
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn defaults_are_empty() {
let _g = TEST_STATE_GUARD.lock().unwrap_or_else(std::sync::PoisonError::into_inner);
stop_runners();
install_senders(Vec::new());
assert!(senders_clone().is_empty());
assert!(current_upstream().is_none());
}
#[test]
fn start_runners_without_senders_errors() {
let _g = TEST_STATE_GUARD.lock().unwrap_or_else(std::sync::PoisonError::into_inner);
install_senders(Vec::new());
let result = start_runners((IpAddr::V4(std::net::Ipv4Addr::LOCALHOST), 6400));
assert!(result.is_err());
}
}