use std::sync::{Mutex, OnceLock};
use slotmap::{Key, KeyData, SlotMap};
use crate::endpoint::IrohEndpoint;
slotmap::new_key_type! { struct EndpointKey; }
fn endpoint_map() -> &'static Mutex<SlotMap<EndpointKey, IrohEndpoint>> {
static S: OnceLock<Mutex<SlotMap<EndpointKey, IrohEndpoint>>> = OnceLock::new();
S.get_or_init(|| Mutex::new(SlotMap::with_key()))
}
fn key_to_handle(k: EndpointKey) -> u64 {
k.data().as_ffi()
}
fn handle_to_key(h: u64) -> EndpointKey {
EndpointKey::from(KeyData::from_ffi(h))
}
pub fn insert_endpoint(ep: IrohEndpoint) -> u64 {
let key = endpoint_map()
.lock()
.unwrap_or_else(|e| e.into_inner())
.insert(ep);
key_to_handle(key)
}
pub fn get_endpoint(handle: u64) -> Option<IrohEndpoint> {
endpoint_map()
.lock()
.unwrap_or_else(|e| e.into_inner())
.get(handle_to_key(handle))
.cloned()
}
pub fn remove_endpoint(handle: u64) -> Option<IrohEndpoint> {
endpoint_map()
.lock()
.unwrap_or_else(|e| e.into_inner())
.remove(handle_to_key(handle))
}
pub fn close_all_endpoints() {
let endpoints: Vec<IrohEndpoint> = {
let mut map = endpoint_map().lock().unwrap_or_else(|e| e.into_inner());
let keys: Vec<EndpointKey> = map.keys().collect();
keys.into_iter().filter_map(|k| map.remove(k)).collect()
};
if endpoints.is_empty() {
return;
}
std::thread::spawn(move || {
if let Ok(rt) = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
{
rt.block_on(async move {
for ep in endpoints {
ep.close_force().await;
}
});
}
});
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn close_all_endpoints_is_idempotent_on_empty_registry() {
close_all_endpoints();
}
#[test]
fn handle_round_trip_changes_after_reuse() {
let mut map: SlotMap<EndpointKey, u8> = SlotMap::with_key();
let k1 = map.insert(1u8);
let h1 = key_to_handle(k1);
map.remove(k1);
let k2 = map.insert(2u8);
let h2 = key_to_handle(k2);
assert_ne!(
h1, h2,
"slotmap must bump generation on reuse; got identical handles {h1:#x}",
);
assert_eq!(
h1 as u32, h2 as u32,
"slot index repeats on reuse; truncating handle to u32 would alias h1 and h2",
);
assert_eq!(handle_to_key(h2), k2);
assert_ne!(handle_to_key(h1), k2);
}
}