use std::cell::{Cell, RefCell};
use std::net::SocketAddr;
use crate::ring::{Location, Ring};
thread_local! {
static SCATTER_DISABLED: Cell<bool> = const { Cell::new(false) };
static OP_TRACES: RefCell<Vec<OpTraceRecord>> = const { RefCell::new(Vec::new()) };
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ProbeOpKind {
Put,
Get,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ProbeStopReason {
Found,
HtlZero,
RoutingExhausted,
TerminusGuard,
NoNextHop,
}
#[derive(Clone, Debug)]
pub struct OpTraceRecord {
pub kind: ProbeOpKind,
pub tx: String,
pub own_loc: f64,
pub target_loc: f64,
pub own_dist: f64,
pub htl_remaining: usize,
pub hop_count: usize,
pub stop_reason: ProbeStopReason,
pub closer_neighbor_loc: Option<f64>,
pub closer_neighbor_dist: Option<f64>,
pub closer_neighbor_visited: Option<bool>,
pub num_neighbors: usize,
}
pub fn set_scatter_disabled(enabled: bool) {
SCATTER_DISABLED.with(|c| c.set(enabled));
}
pub fn scatter_disabled() -> bool {
SCATTER_DISABLED.with(|c| c.get())
}
pub fn clear_op_traces() {
OP_TRACES.with(|t| t.borrow_mut().clear());
}
pub fn take_op_traces() -> Vec<OpTraceRecord> {
OP_TRACES.with(|t| std::mem::take(&mut *t.borrow_mut()))
}
fn push_trace(record: OpTraceRecord) {
OP_TRACES.with(|t| t.borrow_mut().push(record));
}
fn ring_dist(a: f64, b: f64) -> f64 {
let d = (a - b).abs();
d.min(1.0 - d)
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn record_terminus(
ring: &Ring,
kind: ProbeOpKind,
tx: &crate::message::Transaction,
target: Location,
htl_remaining: usize,
hop_count: usize,
stop_reason: ProbeStopReason,
is_visited: impl Fn(SocketAddr) -> bool,
) {
let cm = &ring.connection_manager;
let Some(own_loc) = cm.own_location().location() else {
return;
};
let own_f = own_loc.as_f64();
let target_f = target.as_f64();
let own_dist = ring_dist(own_f, target_f);
let mut num_neighbors = 0usize;
let mut closest_closer: Option<(f64, f64, bool)> = None;
for (loc, conns) in cm.get_connections_by_location().iter() {
let nb_f = loc.as_f64();
let nb_dist = ring_dist(nb_f, target_f);
for conn in conns {
num_neighbors += 1;
if nb_dist < own_dist {
let visited = conn
.location
.socket_addr()
.map(&is_visited)
.unwrap_or(false);
let better = match &closest_closer {
Some((d, _, _)) => nb_dist < *d,
None => true,
};
if better {
closest_closer = Some((nb_dist, nb_f, visited));
}
}
}
}
let (closer_neighbor_loc, closer_neighbor_dist, closer_neighbor_visited) = match closest_closer
{
Some((d, l, v)) => (Some(l), Some(d), Some(v)),
None => (None, None, None),
};
push_trace(OpTraceRecord {
kind,
tx: tx.to_string(),
own_loc: own_f,
target_loc: target_f,
own_dist,
htl_remaining,
hop_count,
stop_reason,
closer_neighbor_loc,
closer_neighbor_dist,
closer_neighbor_visited,
num_neighbors,
});
}