use std::{
collections::HashMap,
sync::{
Arc,
atomic::{AtomicUsize, Ordering},
},
};
use arc_swap::ArcSwap;
use super::{Session, SessionId, id::ClientId};
pub struct SessionRegistry {
sessions: ArcSwap<HashMap<SessionId, Arc<Session>>>,
next_client_id: AtomicUsize,
}
impl SessionRegistry {
#[must_use]
pub fn new() -> Self {
Self {
sessions: ArcSwap::from_pointee(HashMap::new()),
next_client_id: AtomicUsize::new(1),
}
}
#[must_use]
pub fn get(&self, id: &SessionId) -> Option<Arc<Session>> {
self.sessions.load().get(id).cloned()
}
pub fn insert(&self, session: &Arc<Session>) {
let session = Arc::clone(session);
self.sessions.rcu(|current| {
let mut new = (**current).clone();
new.insert(session.id().clone(), Arc::clone(&session));
new
});
}
pub fn remove(&self, id: &SessionId) -> Option<Arc<Session>> {
let mut removed = None;
self.sessions.rcu(|current| {
let mut new = (**current).clone();
removed = new.remove(id);
new
});
removed
}
#[must_use]
pub fn list(&self) -> Vec<SessionId> {
self.sessions.load().keys().cloned().collect()
}
pub fn next_client_id(&self) -> ClientId {
ClientId::new(self.next_client_id.fetch_add(1, Ordering::Relaxed))
}
#[must_use]
pub fn len(&self) -> usize {
self.sessions.load().len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.sessions.load().is_empty()
}
}
impl Default for SessionRegistry {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
#[path = "registry_tests.rs"]
mod tests;