use std::sync::{Arc, OnceLock};
use crate::{
implementation::{
LockMetadata, SharedGlobalImpl, SharedGlobalProjection, SharedImpl, SharedMutProjection,
},
synchronizer::{SynchronizerSized, SynchronizerType},
Castable, PoisonPolicy, Shared, SharedMut, SharedReadLock, SharedWriteLock,
};
pub struct SharedGlobalMut<T: Send> {
inner: SharedGlobalImpl<T>,
projection: OnceLock<Arc<dyn SharedMutProjection<T>>>,
}
impl<T: Send + Sync> SharedGlobalMut<T> {
pub const fn new(t: T) -> Self {
Self {
inner: SharedGlobalImpl::Value(SynchronizerSized::new(
LockMetadata::poison_policy(PoisonPolicy::Panic),
SynchronizerType::RwLock,
t,
)),
projection: OnceLock::new(),
}
}
pub const fn new_lazy(f: fn() -> T) -> Self {
Self {
inner: SharedGlobalImpl::lazy(f, SynchronizerType::RwLock, PoisonPolicy::Panic),
projection: OnceLock::new(),
}
}
}
impl<T: Send> SharedGlobalMut<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),
projection: OnceLock::new(),
}
}
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,
)),
projection: OnceLock::new(),
}
}
pub const fn shared(&'static self) -> Shared<T> {
Shared::from_inner(SharedImpl::ProjectionStaticRO(&self.inner))
}
pub const fn shared_mut(&'static self) -> SharedMut<T> {
SharedMut::from_inner(SharedImpl::ProjectionStatic(&self.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 fn write(&'static self) -> SharedWriteLock<'static, T> {
self.inner.write()
}
pub fn try_write(&'static self) -> Option<SharedWriteLock<'static, T>> {
self.inner.try_lock_write()
}
pub const fn cast<U>(&'static self) -> SharedMut<U>
where
T: Castable<U> + 'static,
U: ?Sized + 'static,
{
let inner = std::ptr::from_ref(&self.inner).cast::<SharedGlobalProjection<T>>();
SharedMut::from_inner(SharedImpl::ProjectionStatic(inner))
}
}
#[cfg(test)]
mod test {
use super::*;
use std::collections::HashMap;
pub type Unsync = std::cell::Cell<()>;
#[test]
fn test_global() {
static GLOBAL: SharedGlobalMut<usize> = SharedGlobalMut::new(1);
static GLOBAL_UNSYNC: SharedGlobalMut<Unsync> =
SharedGlobalMut::new_unsync(std::cell::Cell::new(()));
let shared = GLOBAL.shared_mut();
let shared2 = GLOBAL.shared();
assert_eq!(shared.read(), 1);
*shared.write() = 2;
assert_eq!(shared.read(), 2);
assert_eq!(shared2.read(), 2);
let shared = GLOBAL_UNSYNC.shared_mut();
assert_eq!(shared.read().get(), ());
shared.write().set(());
assert_eq!(shared.read().get(), ());
}
#[test]
fn test_global_direct() {
static GLOBAL: SharedGlobalMut<usize> = SharedGlobalMut::new(1);
static GLOBAL_UNSYNC: SharedGlobalMut<Unsync> =
SharedGlobalMut::new_unsync(std::cell::Cell::new(()));
assert_eq!(GLOBAL.read(), 1);
*GLOBAL.write() = 2;
assert_eq!(GLOBAL.read(), 2);
assert_eq!(GLOBAL_UNSYNC.read().get(), ());
GLOBAL_UNSYNC.write().set(());
assert_eq!(GLOBAL_UNSYNC.read().get(), ());
}
#[test]
fn test_global_lazy() {
static GLOBAL_LAZY: SharedGlobalMut<HashMap<&str, usize>> =
SharedGlobalMut::new_lazy(|| HashMap::from_iter([("a", 1), ("b", 2)]));
assert_eq!(GLOBAL_LAZY.read().len(), 2);
let shared = GLOBAL_LAZY.shared_mut();
assert_eq!(shared.read().len(), 2);
shared.write().insert("c", 3);
GLOBAL_LAZY.write().insert("d", 4);
assert_eq!(shared.read().len(), 4);
assert_eq!(GLOBAL_LAZY.read().len(), 4);
}
#[test]
fn test_global_lazy_thread_race() {
static GLOBAL: SharedGlobalMut<HashMap<&str, usize>> =
SharedGlobalMut::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_cast() {
static GLOBAL: SharedGlobalMut<[i32; 2]> = SharedGlobalMut::new_lazy(|| [1, 2]);
let shared = GLOBAL.cast::<[i32]>();
assert_eq!(shared.read().len(), 2);
assert_eq!(GLOBAL.read().len(), 2);
_ = shared.try_read().expect("Should be able to read");
_ = shared.try_write().expect("Should be able to write");
}
}