1use core::{
2 cell::UnsafeCell,
3 ptr::{
4 self,
5 NonNull,
6 },
7 sync::atomic::{
8 AtomicBool,
9 AtomicPtr,
10 Ordering,
11 },
12};
13
14pub trait Cache {
20 fn resolve(&self, _: impl FnOnce() -> NonNull<()>) -> NonNull<()>;
21}
22
23pub struct StaticCache {
28 value: UnsafeCell<Option<NonNull<()>>>,
29}
30
31unsafe impl Sync for StaticCache {}
34
35impl StaticCache {
36 pub const fn new() -> Self {
37 Self {
38 value: UnsafeCell::new(None),
39 }
40 }
41}
42
43impl Default for StaticCache {
44 fn default() -> Self {
45 Self::new()
46 }
47}
48
49impl Cache for StaticCache {
50 fn resolve(&self, resolver: impl FnOnce() -> NonNull<()>) -> NonNull<()> {
51 let value = unsafe { &mut *self.value.get() };
52 *value.get_or_insert_with(resolver)
53 }
54}
55
56pub struct StaticAtomicCache {
62 value: AtomicPtr<()>,
63 resolve_lock: AtomicBool,
64}
65
66impl StaticAtomicCache {
67 pub const fn new() -> Self {
68 Self {
69 value: AtomicPtr::new(ptr::null_mut()),
70 resolve_lock: AtomicBool::new(false),
71 }
72 }
73}
74
75impl Default for StaticAtomicCache {
76 fn default() -> Self {
77 Self::new()
78 }
79}
80
81impl Cache for StaticAtomicCache {
82 fn resolve(&self, resolver: impl FnOnce() -> NonNull<()>) -> NonNull<()> {
83 loop {
84 let value = self.value.load(Ordering::Relaxed);
85 if let Some(value) = NonNull::new(value) {
86 return value;
87 }
88
89 if self
90 .resolve_lock
91 .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed)
92 .is_err()
93 {
94 continue;
96 }
97
98 let result = resolver();
99 self.value.store(result.as_ptr(), Ordering::Relaxed);
100 return result;
101 }
102 }
103}
104
105pub struct NoCache;
110
111impl NoCache {
112 pub const fn new() -> Self {
113 Self
114 }
115}
116
117impl Default for NoCache {
118 fn default() -> Self {
119 Self::new()
120 }
121}
122
123impl Cache for NoCache {
124 fn resolve(&self, resolver: impl FnOnce() -> NonNull<()>) -> NonNull<()> {
125 resolver()
126 }
127}