1use parking_lot::{MappedRwLockReadGuard, MappedRwLockWriteGuard, RwLock, RwLockReadGuard, RwLockWriteGuard};
2
3use crate::{AppId, LocalContext};
4
5#[doc(hidden)]
6pub struct AppLocalConst<T: Send + Sync + 'static> {
7 value: RwLock<T>,
8}
9impl<T: Send + Sync + 'static> AppLocalConst<T> {
10 pub const fn new(init: T) -> Self {
11 Self { value: RwLock::new(init) }
12 }
13}
14#[doc(hidden)]
15pub struct AppLocalOption<T: Send + Sync + 'static> {
16 value: RwLock<Option<T>>,
17 init: fn() -> T,
18}
19impl<T: Send + Sync + 'static> AppLocalOption<T> {
20 pub const fn new(init: fn() -> T) -> Self {
21 Self {
22 value: RwLock::new(None),
23 init,
24 }
25 }
26
27 fn read_impl(&'static self, read: RwLockReadGuard<'static, Option<T>>) -> MappedRwLockReadGuard<'static, T> {
28 if read.is_some() {
29 return RwLockReadGuard::map(read, |v| v.as_ref().unwrap());
30 }
31 drop(read);
32
33 let mut write = self.value.write();
34 if write.is_some() {
35 drop(write);
36 return self.read();
37 }
38
39 let value = (self.init)();
40 *write = Some(value);
41
42 let read = RwLockWriteGuard::downgrade(write);
43
44 RwLockReadGuard::map(read, |v| v.as_ref().unwrap())
45 }
46
47 fn write_impl(&'static self, mut write: RwLockWriteGuard<'static, Option<T>>) -> MappedRwLockWriteGuard<'static, T> {
48 if write.is_some() {
49 return RwLockWriteGuard::map(write, |v| v.as_mut().unwrap());
50 }
51
52 let value = (self.init)();
53 *write = Some(value);
54
55 RwLockWriteGuard::map(write, |v| v.as_mut().unwrap())
56 }
57}
58
59#[doc(hidden)]
60pub struct AppLocalVec<T: Send + Sync + 'static> {
61 value: RwLock<Vec<(AppId, T)>>,
62 init: fn() -> T,
63}
64impl<T: Send + Sync + 'static> AppLocalVec<T> {
65 pub const fn new(init: fn() -> T) -> Self {
66 Self {
67 value: RwLock::new(vec![]),
68 init,
69 }
70 }
71
72 fn cleanup(&'static self, id: AppId) {
73 self.try_cleanup(id, 0);
74 }
75 fn try_cleanup(&'static self, id: AppId, tries: u8) {
76 if let Some(mut w) = self.value.try_write_for(if tries == 0 {
77 Duration::from_millis(50)
78 } else {
79 Duration::from_millis(500)
80 }) {
81 if let Some(i) = w.iter().position(|(s, _)| *s == id) {
82 w.swap_remove(i);
83 }
84 } else if tries > 5 {
85 tracing::error!("failed to cleanup `app_local` for {id:?}, was locked after app drop");
86 } else {
87 std::thread::spawn(move || {
88 self.try_cleanup(id, tries + 1);
89 });
90 }
91 }
92
93 fn read_impl(&'static self, read: RwLockReadGuard<'static, Vec<(AppId, T)>>) -> MappedRwLockReadGuard<'static, T> {
94 let id = LocalContext::current_app().expect("no app running, `app_local` can only be accessed inside apps");
95
96 if let Some(i) = read.iter().position(|(s, _)| *s == id) {
97 return RwLockReadGuard::map(read, |v| &v[i].1);
98 }
99 drop(read);
100
101 let mut write = self.value.write();
102 if write.iter().any(|(s, _)| *s == id) {
103 drop(write);
104 return self.read();
105 }
106
107 let value = (self.init)();
108 let i = write.len();
109 write.push((id, value));
110
111 LocalContext::register_cleanup(Box::new(move |id| self.cleanup(id)));
112
113 let read = RwLockWriteGuard::downgrade(write);
114
115 RwLockReadGuard::map(read, |v| &v[i].1)
116 }
117
118 fn write_impl(&'static self, mut write: RwLockWriteGuard<'static, Vec<(AppId, T)>>) -> MappedRwLockWriteGuard<'static, T> {
119 let id = LocalContext::current_app().expect("no app running, `app_local` can only be accessed inside apps");
120
121 if let Some(i) = write.iter().position(|(s, _)| *s == id) {
122 return RwLockWriteGuard::map(write, |v| &mut v[i].1);
123 }
124
125 let value = (self.init)();
126 let i = write.len();
127 write.push((id, value));
128
129 LocalContext::register_cleanup(move |id| self.cleanup(id));
130
131 RwLockWriteGuard::map(write, |v| &mut v[i].1)
132 }
133}
134#[doc(hidden)]
135pub trait AppLocalImpl<T: Send + Sync + 'static>: Send + Sync + 'static {
136 fn read(&'static self) -> MappedRwLockReadGuard<'static, T>;
137 fn try_read(&'static self) -> Option<MappedRwLockReadGuard<'static, T>>;
138 fn write(&'static self) -> MappedRwLockWriteGuard<'static, T>;
139 fn try_write(&'static self) -> Option<MappedRwLockWriteGuard<'static, T>>;
140}
141
142impl<T: Send + Sync + 'static> AppLocalImpl<T> for AppLocalVec<T> {
143 fn read(&'static self) -> MappedRwLockReadGuard<'static, T> {
144 self.read_impl(self.value.read_recursive())
145 }
146
147 fn try_read(&'static self) -> Option<MappedRwLockReadGuard<'static, T>> {
148 Some(self.read_impl(self.value.try_read_recursive()?))
149 }
150
151 fn write(&'static self) -> MappedRwLockWriteGuard<'static, T> {
152 self.write_impl(self.value.write())
153 }
154
155 fn try_write(&'static self) -> Option<MappedRwLockWriteGuard<'static, T>> {
156 Some(self.write_impl(self.value.try_write()?))
157 }
158}
159impl<T: Send + Sync + 'static> AppLocalImpl<T> for AppLocalOption<T> {
160 fn read(&'static self) -> MappedRwLockReadGuard<'static, T> {
161 self.read_impl(self.value.read_recursive())
162 }
163
164 fn try_read(&'static self) -> Option<MappedRwLockReadGuard<'static, T>> {
165 Some(self.read_impl(self.value.try_read_recursive()?))
166 }
167
168 fn write(&'static self) -> MappedRwLockWriteGuard<'static, T> {
169 self.write_impl(self.value.write())
170 }
171
172 fn try_write(&'static self) -> Option<MappedRwLockWriteGuard<'static, T>> {
173 Some(self.write_impl(self.value.try_write()?))
174 }
175}
176impl<T: Send + Sync + 'static> AppLocalImpl<T> for AppLocalConst<T> {
177 fn read(&'static self) -> MappedRwLockReadGuard<'static, T> {
178 RwLockReadGuard::map(self.value.read(), |l| l)
179 }
180
181 fn try_read(&'static self) -> Option<MappedRwLockReadGuard<'static, T>> {
182 Some(RwLockReadGuard::map(self.value.try_read()?, |l| l))
183 }
184
185 fn write(&'static self) -> MappedRwLockWriteGuard<'static, T> {
186 RwLockWriteGuard::map(self.value.write(), |l| l)
187 }
188
189 fn try_write(&'static self) -> Option<MappedRwLockWriteGuard<'static, T>> {
190 Some(RwLockWriteGuard::map(self.value.try_write()?, |l| l))
191 }
192}
193
194pub struct AppLocal<T: Send + Sync + 'static> {
205 inner: fn() -> &'static dyn AppLocalImpl<T>,
206}
207impl<T: Send + Sync + 'static> AppLocal<T> {
208 #[doc(hidden)]
209 pub const fn new(inner: fn() -> &'static dyn AppLocalImpl<T>) -> Self {
210 AppLocal { inner }
211 }
212
213 #[inline]
221 pub fn read(&'static self) -> MappedRwLockReadGuard<'static, T> {
222 (self.inner)().read()
223 }
224
225 #[inline]
235 pub fn try_read(&'static self) -> Option<MappedRwLockReadGuard<'static, T>> {
236 (self.inner)().try_read()
237 }
238
239 #[inline]
247 pub fn write(&'static self) -> MappedRwLockWriteGuard<'static, T> {
248 (self.inner)().write()
249 }
250
251 pub fn try_write(&'static self) -> Option<MappedRwLockWriteGuard<'static, T>> {
261 (self.inner)().try_write()
262 }
263
264 #[inline]
266 pub fn get(&'static self) -> T
267 where
268 T: Clone,
269 {
270 self.read().clone()
271 }
272
273 #[inline]
275 pub fn set(&'static self, value: T) {
276 *self.write() = value;
277 }
278
279 #[inline]
283 pub fn try_get(&'static self) -> Option<T>
284 where
285 T: Clone,
286 {
287 self.try_read().map(|l| l.clone())
288 }
289
290 #[inline]
294 pub fn try_set(&'static self, value: T) -> Result<(), T> {
295 match self.try_write() {
296 Some(mut l) => {
297 *l = value;
298 Ok(())
299 }
300 None => Err(value),
301 }
302 }
303
304 #[inline]
306 pub fn read_map<O>(&'static self, map: impl FnOnce(&T) -> &O) -> MappedRwLockReadGuard<'static, O> {
307 MappedRwLockReadGuard::map(self.read(), map)
308 }
309
310 #[inline]
312 pub fn try_read_map<O>(&'static self, map: impl FnOnce(&T) -> &O) -> Option<MappedRwLockReadGuard<'static, O>> {
313 let lock = self.try_read()?;
314 Some(MappedRwLockReadGuard::map(lock, map))
315 }
316
317 #[inline]
319 pub fn write_map<O>(&'static self, map: impl FnOnce(&mut T) -> &mut O) -> MappedRwLockWriteGuard<'static, O> {
320 MappedRwLockWriteGuard::map(self.write(), map)
321 }
322
323 #[inline]
325 pub fn try_write_map<O>(&'static self, map: impl FnOnce(&mut T) -> &mut O) -> Option<MappedRwLockWriteGuard<'static, O>> {
326 let lock = self.try_write()?;
327 Some(MappedRwLockWriteGuard::map(lock, map))
328 }
329
330 pub fn id(&'static self) -> AppLocalId {
335 AppLocalId((self.inner)() as *const dyn AppLocalImpl<T> as *const () as _)
336 }
337}
338impl<T: Send + Sync + 'static> PartialEq for AppLocal<T> {
339 fn eq(&self, other: &Self) -> bool {
340 let a = AppLocalId((self.inner)() as *const dyn AppLocalImpl<T> as *const () as _);
341 let b = AppLocalId((other.inner)() as *const dyn AppLocalImpl<T> as *const () as _);
342 a == b
343 }
344}
345impl<T: Send + Sync + 'static> Eq for AppLocal<T> {}
346impl<T: Send + Sync + 'static> std::hash::Hash for AppLocal<T> {
347 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
348 let a = AppLocalId((self.inner)() as *const dyn AppLocalImpl<T> as *const () as _);
349 std::hash::Hash::hash(&a, state)
350 }
351}
352
353#[derive(PartialEq, Eq, Hash, Clone, Copy)]
359pub struct AppLocalId(pub(crate) usize);
360impl AppLocalId {
361 pub fn get(self) -> usize {
363 self.0 as _
365 }
366}
367impl fmt::Debug for AppLocalId {
368 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
369 write!(f, "AppLocalId({:#x})", self.0)
370 }
371}
372
373#[macro_export]
425macro_rules! app_local {
426 ($(
427 $(#[$meta:meta])*
428 $vis:vis static $IDENT:ident : $T:ty = $(const { $init_const:expr })? $($init:expr_2021)?;
429 )+) => {$(
430 $crate::app_local_impl! {
431 $(#[$meta])*
432 $vis static $IDENT: $T = $(const { $init_const })? $($init)?;
433 }
434 )+};
435}
436
437#[doc(hidden)]
438#[macro_export]
439macro_rules! app_local_impl_single {
440 (
441 $(#[$meta:meta])*
442 $vis:vis static $IDENT:ident : $T:ty = const { $init:expr };
443 ) => {
444 $(#[$meta])*
445 $vis static $IDENT: $crate::AppLocal<$T> = {
446 fn s() -> &'static dyn $crate::AppLocalImpl<$T> {
447 $crate::hot_static! {
448 static IMPL: $crate::AppLocalConst<$T> = $crate::AppLocalConst::new($init);
449 }
450 $crate::hot_static_ref!(IMPL)
451 }
452 $crate::AppLocal::new(s)
453 };
454 };
455 (
456 $(#[$meta:meta])*
457 $vis:vis static $IDENT:ident : $T:ty = $init:expr_2021;
458 ) => {
459 $(#[$meta])*
460 $vis static $IDENT: $crate::AppLocal<$T> = {
461 fn s() -> &'static dyn $crate::AppLocalImpl<$T> {
462 fn init() -> $T {
463 std::convert::Into::into($init)
464 }
465 $crate::hot_static! {
466 static IMPL: $crate::AppLocalOption<$T> = $crate::AppLocalOption::new(init);
467 }
468 $crate::hot_static_ref!(IMPL)
469 }
470 $crate::AppLocal::new(s)
471 };
472 };
473 (
474 $(#[$meta:meta])*
475 $vis:vis static $IDENT:ident : $T:ty = ($tt:tt)*
476 ) => {
477 std::compile_error!("expected `const { $expr };` or `$expr;`")
478 };
479}
480
481#[doc(hidden)]
482#[macro_export]
483macro_rules! app_local_impl_multi {
484 (
485 $(#[$meta:meta])*
486 $vis:vis static $IDENT:ident : $T:ty = const { $init:expr };
487 ) => {
488 $(#[$meta])*
489 $vis static $IDENT: $crate::AppLocal<$T> = {
490 fn s() -> &'static dyn $crate::AppLocalImpl<$T> {
491 const fn init() -> $T {
492 $init
493 }
494 $crate::hot_static! {
495 static IMPL: $crate::AppLocalVec<$T> = $crate::AppLocalVec::new(init);
496 }
497 $crate::hot_static_ref!(IMPL)
498 }
499 $crate::AppLocal::new(s)
500 };
501 };
502 (
503 $(#[$meta:meta])*
504 $vis:vis static $IDENT:ident : $T:ty = $init:expr_2021;
505 ) => {
506 $(#[$meta])*
507 $vis static $IDENT: $crate::AppLocal<$T> = {
508 fn s() -> &'static dyn $crate::AppLocalImpl<$T> {
509 fn init() -> $T {
510 std::convert::Into::into($init)
511 }
512 $crate::hot_static! {
513 static IMPL: $crate::AppLocalVec<$T> = $crate::AppLocalVec::new(init);
514 }
515 $crate::hot_static_ref!(IMPL)
516 }
517 $crate::AppLocal::new(s)
518 };
519 };
520 (
521 $(#[$meta:meta])*
522 $vis:vis static $IDENT:ident : $T:ty = ($tt:tt)*
523 ) => {
524 std::compile_error!("expected `const { $expr };` or `$expr;`")
525 };
526}
527
528use std::{fmt, time::Duration};
529
530#[cfg(feature = "multi_app")]
531#[doc(hidden)]
532pub use app_local_impl_multi as app_local_impl;
533#[cfg(not(feature = "multi_app"))]
534#[doc(hidden)]
535pub use app_local_impl_single as app_local_impl;