mod table;
use core::hash::Hash;
#[cfg(loom)]
use loom::sync::atomic::{AtomicU64, Ordering};
#[cfg(loom)]
use loom::sync::{Arc, RwLock};
#[cfg(not(loom))]
use parking_lot::RwLock;
#[cfg(not(loom))]
use std::sync::Arc;
#[cfg(not(loom))]
use std::sync::atomic::{AtomicU64, Ordering};
use table::OpenTable;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AddressInUse<A>(pub A);
struct Inner<A, E> {
next_generation: AtomicU64,
entries: RwLock<OpenTable<A, E>>,
}
impl<A, E> Inner<A, E> {
#[cfg(loom)]
fn read_entries(&self) -> loom::sync::RwLockReadGuard<'_, OpenTable<A, E>> {
self.entries.read().unwrap()
}
#[cfg(loom)]
fn write_entries(&self) -> loom::sync::RwLockWriteGuard<'_, OpenTable<A, E>> {
self.entries.write().unwrap()
}
#[cfg(not(loom))]
fn read_entries(&self) -> parking_lot::RwLockReadGuard<'_, OpenTable<A, E>> {
self.entries.read()
}
#[cfg(not(loom))]
fn write_entries(&self) -> parking_lot::RwLockWriteGuard<'_, OpenTable<A, E>> {
self.entries.write()
}
}
pub struct AddressSpace<A, E> {
inner: Arc<Inner<A, E>>,
}
impl<A, E> Clone for AddressSpace<A, E> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
}
}
}
impl<A, E> Default for AddressSpace<A, E> {
fn default() -> Self {
Self::new()
}
}
impl<A, E> AddressSpace<A, E> {
#[must_use]
pub fn new() -> Self {
Self {
inner: Arc::new(Inner {
next_generation: AtomicU64::new(1),
entries: RwLock::new(OpenTable::new()),
}),
}
}
#[must_use]
pub fn len(&self) -> usize {
self.inner.read_entries().len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
impl<A: Eq + Hash, E> AddressSpace<A, E> {
pub fn shrink_to_fit(&self) {
self.inner.write_entries().shrink_to_fit();
}
}
impl<A, E> AddressSpace<A, E>
where
A: Eq + Hash + Clone,
{
pub fn claim(&self, address: A, endpoint: E) -> Result<Lease<A, E>, AddressInUse<A>> {
let key = address.clone();
let mut entries = self.inner.write_entries();
if entries.get(&address).is_some() {
return Err(AddressInUse(address));
}
let generation = self.inner.next_generation.fetch_add(1, Ordering::Relaxed);
entries.insert(key, generation, endpoint);
Ok(Lease {
inner: self.inner.clone(),
address,
generation,
released: false,
})
}
}
impl<A, E> AddressSpace<A, E>
where
A: Eq + Hash,
E: Clone,
{
#[must_use]
pub fn resolve(&self, address: &A) -> Option<E> {
let endpoint = {
let guard = self.inner.read_entries();
guard.get(address).map(|entry| Arc::clone(&entry.endpoint))
};
endpoint.as_deref().cloned()
}
}
pub struct Lease<A, E>
where
A: Eq + Hash,
{
inner: Arc<Inner<A, E>>,
address: A,
generation: u64,
released: bool,
}
impl<A, E> Lease<A, E>
where
A: Eq + Hash,
{
#[must_use]
pub fn address(&self) -> &A {
&self.address
}
pub fn release(mut self) {
self.release_inner();
}
fn release_inner(&mut self) {
if self.released {
return;
}
self.released = true;
let removed = {
let mut entries = self.inner.write_entries();
entries.remove_if(&self.address, self.generation)
};
drop(removed);
}
}
impl<A, E> Drop for Lease<A, E>
where
A: Eq + Hash,
{
fn drop(&mut self) {
self.release_inner();
}
}
#[cfg(test)]
mod tests {
use super::{AddressInUse, AddressSpace};
#[test]
fn lease_owns_and_releases_one_registration() {
let space = AddressSpace::new();
let lease = space.claim(7, "first").unwrap();
assert_eq!(space.resolve(&7), Some("first"));
assert!(matches!(space.claim(7, "second"), Err(AddressInUse(7))));
drop(lease);
assert_eq!(space.resolve(&7), None);
let replacement = space.claim(7, "second").unwrap();
assert_eq!(space.resolve(&7), Some("second"));
drop(replacement);
}
}