use std::any::Any;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, OnceLock};
pub trait ResourceAccessor: Send + Sync {
fn lookup(&self, key: &str) -> Option<Arc<dyn Any + Send + Sync>>;
}
#[derive(Clone, Default)]
pub struct ResourceScope(Arc<Slot>);
#[derive(Default)]
struct Slot {
accessor: OnceLock<Arc<dyn ResourceAccessor>>,
parent: OnceLock<Link>,
}
struct Link {
parent: ResourceScope,
live: AtomicBool,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ScopeJoinError {
Cycle,
}
impl std::fmt::Display for ScopeJoinError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ScopeJoinError::Cycle => write!(
f,
"joining the child's resource scope to its parent's, concurrently with \
another join, would have made a scope delegate to itself"
),
}
}
}
impl std::error::Error for ScopeJoinError {}
impl ResourceScope {
pub fn new() -> Self {
Self::default()
}
pub fn with_accessor(accessor: Arc<dyn ResourceAccessor>) -> Self {
let scope = Self::new();
let _ = scope.0.accessor.set(accessor);
scope
}
pub fn install(
&self,
accessor: Arc<dyn ResourceAccessor>,
) -> Result<(), Arc<dyn ResourceAccessor>> {
self.0.accessor.set(accessor)
}
pub fn is_installed(&self) -> bool {
self.0.accessor.get().is_some()
}
pub fn parent(&self) -> Option<&ResourceScope> {
self.0
.parent
.get()
.filter(|link| link.live.load(Ordering::Acquire))
.map(|link| &link.parent)
}
pub fn join(&self, parent: &ResourceScope) -> Result<(), ScopeJoinError> {
if self.is_installed() || parent.reaches(self) {
return Ok(());
}
let link = Link {
parent: parent.clone(),
live: AtomicBool::new(true),
};
if self.0.parent.set(link).is_err() {
return Ok(());
}
if parent.reaches(self)
&& let Some(link) = self.0.parent.get()
{
link.live.store(false, Ordering::Release);
return Err(ScopeJoinError::Cycle);
}
Ok(())
}
fn reaches(&self, target: &ResourceScope) -> bool {
let mut scope = self;
loop {
if scope.same_scope(target) {
return true;
}
match scope.parent() {
Some(parent) => scope = parent,
None => return false,
}
}
}
pub fn lookup(&self, key: &str) -> Option<Arc<dyn Any + Send + Sync>> {
let mut scope = self;
loop {
if let Some(accessor) = scope.0.accessor.get() {
return accessor.lookup(key);
}
scope = scope.parent()?;
}
}
pub fn same_scope(&self, other: &ResourceScope) -> bool {
Arc::ptr_eq(&self.0, &other.0)
}
}
impl std::fmt::Debug for ResourceScope {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ResourceScope")
.field("installed", &self.is_installed())
.field("joined", &self.parent().is_some())
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
struct Fixed(&'static str, u64);
impl ResourceAccessor for Fixed {
fn lookup(&self, key: &str) -> Option<Arc<dyn Any + Send + Sync>> {
(key == self.0).then(|| Arc::new(self.1) as Arc<dyn Any + Send + Sync>)
}
}
fn read(scope: &ResourceScope) -> Option<u64> {
scope
.lookup("k")
.and_then(|v| v.downcast::<u64>().ok())
.map(|v| *v)
}
#[test]
fn an_empty_scope_answers_none() {
assert!(ResourceScope::new().lookup("k").is_none());
}
#[test]
fn clones_share_one_slot_and_install_once() {
let scope = ResourceScope::new();
let child = scope.clone();
assert!(scope.install(Arc::new(Fixed("k", 7))).is_ok());
assert_eq!(read(&child), Some(7));
assert!(scope.install(Arc::new(Fixed("k", 8))).is_err());
assert!(child.same_scope(&scope));
assert!(!ResourceScope::new().same_scope(&scope));
}
#[test]
fn a_joined_scope_resolves_through_its_parent_until_it_installs_its_own() {
let root = ResourceScope::new();
let child = ResourceScope::new();
let grandchild = ResourceScope::new();
child.join(&root).unwrap();
grandchild.join(&child).unwrap();
assert_eq!(read(&grandchild), None, "no accessor anywhere yet");
root.install(Arc::new(Fixed("k", 1))).ok();
assert_eq!(read(&grandchild), Some(1), "installed once at the root");
child.install(Arc::new(Fixed("k", 2))).ok();
assert_eq!(read(&grandchild), Some(2), "the nearest accessor answers");
assert_eq!(read(&root), Some(1), "a parent never reads its child's");
}
#[test]
fn a_scope_with_its_own_accessor_keeps_it() {
let root = ResourceScope::with_accessor(Arc::new(Fixed("k", 1)));
let own = ResourceScope::with_accessor(Arc::new(Fixed("k", 2)));
own.join(&root).unwrap();
assert!(own.parent().is_none());
assert_eq!(read(&own), Some(2));
}
#[test]
fn a_scope_never_delegates_to_itself_or_a_descendant() {
let root = ResourceScope::new();
root.join(&root).unwrap();
assert!(root.parent().is_none(), "joining itself changes nothing");
let child = ResourceScope::new();
child.join(&root).unwrap();
let grandchild = ResourceScope::new();
grandchild.join(&child).unwrap();
root.join(&grandchild).unwrap();
assert!(
root.parent().is_none(),
"an ancestor bound under its descendant stays a root"
);
}
#[test]
fn the_first_join_wins() {
let a = ResourceScope::with_accessor(Arc::new(Fixed("k", 1)));
let b = ResourceScope::with_accessor(Arc::new(Fixed("k", 2)));
let child = ResourceScope::new();
child.join(&a).unwrap();
child.join(&b).unwrap();
assert!(child.parent().is_some_and(|p| p.same_scope(&a)));
assert_eq!(read(&child), Some(1), "the second join changed nothing");
}
#[test]
fn concurrent_joins_never_close_a_cycle() {
for _ in 0..200 {
let scopes: Vec<ResourceScope> = (0..4).map(|_| ResourceScope::new()).collect();
std::thread::scope(|s| {
for i in 0..4 {
let (from, to) = (scopes[i].clone(), scopes[(i + 1) % 4].clone());
s.spawn(move || from.join(&to));
}
});
let live = scopes.iter().filter(|s| s.parent().is_some()).count();
assert!(live < 4, "a cycle of joins stood");
scopes[3].install(Arc::new(Fixed("k", 9))).ok();
for s in &scopes {
let _ = read(s);
}
}
}
}