1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
use crate::{
implementation::{
LockMetadata, SharedGlobalImpl, SharedGlobalProjection, SharedImpl, SharedProjection,
},
synchronizer::{SynchronizerSized, SynchronizerType},
Castable, PoisonPolicy, Shared, SharedReadLock,
};
/// A global version of [`Shared`]. Use [`SharedGlobal::shared`] to get a [`Shared`] to access the contents.
pub struct SharedGlobal<T> {
inner: SharedGlobalImpl<T>,
}
impl<T: Send + Sync> SharedGlobal<T> {
/// Create a new [`SharedGlobal`].
pub const fn new(t: T) -> Self {
Self {
inner: SharedGlobalImpl::Value(SynchronizerSized::new(
LockMetadata::poison_ignore(),
SynchronizerType::Arc,
t,
)),
}
}
/// Create a new, lazy [`SharedGlobal`] implementation that will be initialized on the first access.
pub const fn new_lazy(f: fn() -> T) -> Self {
Self {
inner: SharedGlobalImpl::lazy(f, SynchronizerType::Arc, PoisonPolicy::Ignore),
}
}
}
impl<T: Send> SharedGlobal<T> {
/// The [`SharedGlobal::new`] function requires a type that is both `Send + Sync`. If this is not possible, you may call
/// [`SharedGlobal::new_unsync`] to create a [`SharedGlobal`] implementation that uses a [`Mutex`].
///
/// This will fail to compile:
///
/// ```compile_fail
/// # use std::{cell::Cell, marker::PhantomData};
/// # use keepcalm::*;
/// # pub type Unsync = PhantomData<Cell<()>>;
/// // Compiler error:
/// // error[E0277]: (type) cannot be shared between threads safely
/// // required by a bound in `keepcalm::Shared::<T>::new
/// SharedGlobal::new(Unsync {});
/// ```
///
/// This will work:
///
/// ```rust
/// # use std::{cell::Cell, marker::PhantomData};
/// # use keepcalm::*;
/// # pub type Unsync = PhantomData<Cell<()>>;
/// SharedGlobal::new_unsync(Unsync {});
/// ```
pub const fn new_unsync(t: T) -> Self {
Self::new_mutex(t)
}
/// The [`SharedGlobal::new_lazy`] function requires a type that is both `Send + Sync`. If this is not possible, you may call
/// [`SharedGlobal::new_lazy`] to create a [`SharedGlobal`] implementation that uses a [`Mutex`].
pub const fn new_lazy_unsync(f: fn() -> T) -> Self {
Self {
inner: SharedGlobalImpl::lazy(f, SynchronizerType::Mutex, PoisonPolicy::Panic),
}
}
/// Create a new [`SharedGlobal`], backed by a `Mutex` and poisoning on panic.
pub const fn new_mutex(t: T) -> Self {
Self::new_mutex_with_policy(t, PoisonPolicy::Panic)
}
/// Create a new [`SharedGlobal`], backed by a `Mutex` and optionally poisoning on 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,
)),
}
}
/// Create a [`Shared`] reference, backed by this global. Note that the [`Shared`] cannot be unwrapped.
pub const fn shared(&'static self) -> Shared<T> {
let inner: &'static dyn SharedProjection<T> = &self.inner as _;
Shared::from_inner(SharedImpl::ProjectionStaticRO(inner))
}
/// Lock the global [`SharedGlobal`] for reading directly.
pub fn read(&'static self) -> SharedReadLock<'static, T> {
self.inner.read()
}
/// Cast this [`SharedGlobal`] to a new type.
///
/// See [`Castable`] for more information.
pub const fn cast<U>(&'static self) -> Shared<U>
where
T: Castable<U> + 'static,
U: ?Sized + 'static,
{
// We want the vtable from SharedGlobalProjection, not SharedProjection
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);
}
}