use std::collections::HashMap;
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::{LazyLock, Mutex};
static NEXT_TRANSPORT_GENERATION: AtomicU64 = AtomicU64::new(1);
static LIVE_TRANSPORT_GENERATIONS: LazyLock<Mutex<HashMap<usize, u64>>> =
LazyLock::new(|| Mutex::new(HashMap::new()));
fn next_generation() -> u64 {
let generation = NEXT_TRANSPORT_GENERATION.fetch_add(1, Ordering::Relaxed);
assert_ne!(
generation, 0,
"OpenRTC transport generation counter exhausted"
);
generation
}
pub(crate) fn for_runtime_connection(raw_stable_id: usize) -> u64 {
let mut generations = LIVE_TRANSPORT_GENERATIONS
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner());
*generations
.entry(raw_stable_id)
.or_insert_with(next_generation)
}
pub(crate) fn for_connection(connection: &iroh::endpoint::Connection) -> u64 {
let raw_stable_id = connection.stable_id();
let mut generations = LIVE_TRANSPORT_GENERATIONS
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner());
if let Some(generation) = generations.get(&raw_stable_id) {
return *generation;
}
let generation = next_generation();
if connection.close_reason().is_none() {
generations.insert(raw_stable_id, generation);
}
generation
}
pub(crate) fn forget_runtime_connection(raw_stable_id: usize) {
LIVE_TRANSPORT_GENERATIONS
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner())
.remove(&raw_stable_id);
}
pub(crate) fn forget_connection(connection: &iroh::endpoint::Connection) {
forget_runtime_connection(connection.stable_id());
}
#[cfg(test)]
mod tests {
use super::{for_runtime_connection, forget_runtime_connection};
#[test]
fn reused_runtime_identifier_receives_a_fresh_openrtc_generation() {
let raw = usize::MAX - 17;
let first = for_runtime_connection(raw);
assert_eq!(for_runtime_connection(raw), first);
forget_runtime_connection(raw);
let replacement = for_runtime_connection(raw);
assert_ne!(replacement, first);
forget_runtime_connection(raw);
}
}