use crate::{
implementation::{
LockMetadata, SharedGlobalImpl, SharedGlobalProjection, SharedImpl, SharedProjection,
},
synchronizer::{SynchronizerSized, SynchronizerType},
Castable, PoisonPolicy, Shared, SharedReadLock,
};
pub struct SharedGlobal<T> {
inner: SharedGlobalImpl<T>,
}
impl<T: Send + Sync> SharedGlobal<T> {
pub const fn new(t: T) -> Self {
Self {
inner: SharedGlobalImpl::Value(SynchronizerSized::new(
LockMetadata::poison_ignore(),
SynchronizerType::Arc,
t,
)),
}
}
pub const fn new_lazy(f: fn() -> T) -> Self {
Self {
inner: SharedGlobalImpl::lazy(f, SynchronizerType::Arc, PoisonPolicy::Ignore),
}
}
}
impl<T: Send> SharedGlobal<T> {
pub const fn new_unsync(t: T) -> Self {
Self::new_mutex(t)
}
pub const fn new_lazy_unsync(f: fn() -> T) -> Self {
Self {
inner: SharedGlobalImpl::lazy(f, SynchronizerType::Mutex, PoisonPolicy::Panic),
}
}
pub const fn new_mutex(t: T) -> Self {
Self::new_mutex_with_policy(t, PoisonPolicy::Panic)
}
pub const fn new_mutex_with_policy(t: T, policy: PoisonPolicy) -> Self {
Self {
inner: SharedGlobalImpl::Value(SynchronizerSized::new(
LockMetadata::poison_policy(policy),
SynchronizerType::Mutex,
t,
)),
}
}
pub const fn shared(&'static self) -> Shared<T> {
let inner: &'static dyn SharedProjection<T> = &self.inner as _;
Shared::from_inner(SharedImpl::ProjectionStaticRO(inner))
}
pub fn read(&'static self) -> SharedReadLock<'static, T> {
self.inner.read()
}
pub fn try_read(&'static self) -> Option<SharedReadLock<'static, T>> {
self.inner.try_lock_read()
}
pub const fn cast<U>(&'static self) -> Shared<U>
where
T: Castable<U> + 'static,
U: ?Sized + 'static,
{
let inner = std::ptr::from_ref(&self.inner).cast::<SharedGlobalProjection<T>>();
Shared::from_inner(SharedImpl::ProjectionStaticRO(inner))
}
}
#[cfg(test)]
mod test {
use super::SharedGlobal;
use std::{collections::HashMap, sync::Arc};
pub type Unsync = std::cell::Cell<()>;
static GLOBAL: SharedGlobal<usize> = SharedGlobal::new(1);
static GLOBAL_UNSYNC: SharedGlobal<Unsync> = SharedGlobal::new_unsync(std::cell::Cell::new(()));
static GLOBAL_LAZY: SharedGlobal<HashMap<&str, usize>> =
SharedGlobal::new_lazy(|| HashMap::from_iter([("a", 1), ("b", 2)]));
#[test]
fn test_global() {
let shared = GLOBAL.shared();
assert_eq!(shared.read(), 1);
let shared = GLOBAL_UNSYNC.shared();
assert_eq!(shared.read().get(), ());
}
#[test]
fn test_global_direct() {
assert_eq!(GLOBAL.read(), 1);
assert_eq!(GLOBAL_UNSYNC.read().get(), ());
}
#[test]
fn test_global_lazy() {
let shared = GLOBAL_LAZY.shared();
assert_eq!(shared.read().len(), 2);
}
#[test]
fn test_global_lazy_thread_race() {
static GLOBAL: SharedGlobal<HashMap<&str, usize>> =
SharedGlobal::new_lazy(|| HashMap::from_iter([("a", 1), ("b", 2)]));
let shared = GLOBAL.shared();
let mut handles = Vec::new();
let barrier = Arc::new(std::sync::Barrier::new(100));
for _ in 0..100 {
let barrier = barrier.clone();
handles.push(std::thread::spawn(move || {
barrier.wait();
let shared = GLOBAL.read();
assert_eq!(shared.len(), 2);
}));
}
for handle in handles {
handle.join().unwrap();
}
assert_eq!(shared.read().len(), 2);
}
#[test]
fn test_global_lazy_direct() {
assert_eq!(GLOBAL_LAZY.read().len(), 2);
}
#[test]
fn test_global_project_const() {
static PAIR: SharedGlobal<(u32, u32)> = SharedGlobal::new((1, 2));
static SECOND: crate::Shared<u32> = crate::project_global!(PAIR => 1);
assert_eq!(*SECOND.read(), 2);
_ = SECOND.try_read().expect("Should be able to read");
}
}