use crate::{
implementation::{LockMetadata, SharedGlobalImpl, SharedGlobalProjection, SharedImpl},
synchronizer::{SynchronizerSized, SynchronizerType},
Castable, PoisonPolicy, Shared, SharedMut, SharedReadLock, SharedWriteLock,
};
pub struct SharedGlobalMut<T: Send> {
inner: SharedGlobalImpl<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,
)),
}
}
pub const fn new_lazy(f: fn() -> T) -> Self {
Self {
inner: SharedGlobalImpl::lazy(f, SynchronizerType::RwLock, PoisonPolicy::Panic),
}
}
}
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),
}
}
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> {
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;
use std::sync::Arc;
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_project_const() {
struct Config {
name: String,
port: u16,
}
struct App {
config: Config,
count: usize,
}
static APP: SharedGlobalMut<App> = SharedGlobalMut::new(App {
config: Config {
name: String::new(),
port: 80,
},
count: 0,
});
static NAME: crate::SharedMut<String> = crate::project_global!(mut APP => config.name);
static PORT: crate::Shared<u16> = crate::project_global!(APP => config.port);
*NAME.write() += "server";
assert_eq!(*NAME.read(), "server");
assert_eq!(APP.read().config.name, "server");
assert_eq!(*PORT.read(), 80);
_ = NAME.try_read().expect("Should be able to read");
_ = NAME.try_write().expect("Should be able to write");
_ = PORT.try_read().expect("Should be able to read");
let count = crate::project_global!(mut APP => count);
*count.write() += 1;
assert_eq!(APP.read().count, 1);
}
#[test]
fn test_global_project_const_lazy() {
struct Stats {
hits: usize,
misses: usize,
}
static STATS: SharedGlobalMut<Stats> =
SharedGlobalMut::new_lazy(|| Stats { hits: 1, misses: 2 });
static HITS: crate::SharedMut<usize> = crate::project_global!(mut STATS => hits);
*HITS.write() += 10;
assert_eq!(*HITS.read(), 11);
assert_eq!(STATS.read().misses, 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");
}
}