1use parking_lot::{MappedRwLockReadGuard, MappedRwLockWriteGuard, RwLock, RwLockReadGuard, RwLockWriteGuard};
2
3#[cfg(feature = "multi_app")]
4use crate::{AppId, LocalContext};
5
6use std::fmt;
7
8#[doc(hidden)]
9pub struct AppLocalConst<T: Send + Sync + 'static> {
10 value: RwLock<T>,
11}
12impl<T: Send + Sync + 'static> AppLocalConst<T> {
13 pub const fn new(init: T) -> Self {
14 Self { value: RwLock::new(init) }
15 }
16}
17#[doc(hidden)]
18pub struct AppLocalOption<T: Send + Sync + 'static> {
19 value: RwLock<Option<T>>,
20 init: fn() -> T,
21}
22impl<T: Send + Sync + 'static> AppLocalOption<T> {
23 pub const fn new(init: fn() -> T) -> Self {
24 Self {
25 value: RwLock::new(None),
26 init,
27 }
28 }
29
30 fn read_impl(&'static self, read: RwLockReadGuard<'static, Option<T>>) -> MappedRwLockReadGuard<'static, T> {
31 if read.is_some() {
32 return RwLockReadGuard::map(read, |v| v.as_ref().unwrap());
33 }
34 drop(read);
35
36 let mut write = self.value.write();
37 if write.is_some() {
38 drop(write);
39 return self.read();
40 }
41
42 let value = (self.init)();
43 *write = Some(value);
44
45 let read = RwLockWriteGuard::downgrade(write);
46
47 RwLockReadGuard::map(read, |v| v.as_ref().unwrap())
48 }
49
50 fn write_impl(&'static self, mut write: RwLockWriteGuard<'static, Option<T>>) -> MappedRwLockWriteGuard<'static, T> {
51 if write.is_some() {
52 return RwLockWriteGuard::map(write, |v| v.as_mut().unwrap());
53 }
54
55 let value = (self.init)();
56 *write = Some(value);
57
58 RwLockWriteGuard::map(write, |v| v.as_mut().unwrap())
59 }
60}
61
62#[doc(hidden)]
63pub struct AppLocalVec<T: Send + Sync + 'static> {
64 #[cfg(feature = "multi_app")]
67 value: append_only_vec::AppendOnlyVec<(AppId, RwLock<Option<T>>)>,
68 #[cfg(feature = "multi_app")]
69 init: fn() -> T,
70 #[cfg(not(feature = "multi_app"))]
71 _f: std::marker::PhantomData<T>,
72}
73#[cfg(feature = "multi_app")]
74impl<T: Send + Sync + 'static> AppLocalVec<T> {
75 pub const fn new(init: fn() -> T) -> Self {
76 Self {
77 value: append_only_vec::AppendOnlyVec::new(),
78 init,
79 }
80 }
81
82 fn cleanup(&'static self, id: AppId) {
83 self.try_cleanup(id, 0);
84 }
85 fn try_cleanup(&'static self, id: AppId, tries: u8) {
86 for (app, data) in self.value.iter() {
87 if *app == id {
88 let timeout = if tries == 0 {
89 std::time::Duration::from_millis(50)
90 } else {
91 std::time::Duration::from_millis(500)
92 };
93 if let Some(mut w) = data.try_write_for(timeout) {
94 let _ = w.take();
96 } else if tries > 5 {
97 tracing::error!("failed to cleanup `app_local` for {id:?}, was locked after app drop");
98 } else {
99 std::thread::Builder::new()
100 .name("app_local_cleanup".into())
101 .spawn(move || {
102 self.try_cleanup(id, tries + 1);
103 })
104 .expect("failed to spawn thread");
105 }
106 }
107 }
108 }
109
110 fn app_lock(&'static self) -> &'static RwLock<Option<T>> {
111 let id = LocalContext::current_app().expect("no app running, `app_local` can only be accessed inside apps");
112 for (app, lock) in self.value.iter() {
113 if *app == id {
114 return lock;
115 }
116 }
117 self.value.push((id, RwLock::new(None)));
118 for (app, lock) in self.value.iter() {
120 if *app == id {
121 return lock;
122 }
123 }
124 unreachable!()
125 }
126}
127#[doc(hidden)]
128pub trait AppLocalImpl<T: Send + Sync + 'static>: Send + Sync + 'static {
129 fn read(&'static self) -> MappedRwLockReadGuard<'static, T>;
130 fn try_read(&'static self) -> Option<MappedRwLockReadGuard<'static, T>>;
131 fn write(&'static self) -> MappedRwLockWriteGuard<'static, T>;
132 fn try_write(&'static self) -> Option<MappedRwLockWriteGuard<'static, T>>;
133}
134
135#[cfg(feature = "multi_app")]
136impl<T: Send + Sync + 'static> AppLocalImpl<T> for AppLocalVec<T> {
137 fn read(&'static self) -> MappedRwLockReadGuard<'static, T> {
138 let lock = self.app_lock();
139 let mut read = lock.read();
140 if read.is_none() {
141 drop(read);
142 let mut write = lock.write();
143 if write.is_none() {
144 *write = Some((self.init)());
145 LocalContext::register_cleanup(Box::new(move |id| self.cleanup(id)));
146 }
147 drop(write);
148 read = lock.read();
149 }
150 RwLockReadGuard::map(read, |o| o.as_ref().unwrap())
151 }
152
153 fn try_read(&'static self) -> Option<MappedRwLockReadGuard<'static, T>> {
154 let lock = self.app_lock();
155 let mut read = lock.try_read()?;
156 if read.is_none() {
157 drop(read);
158 let mut write = lock.try_write()?;
159 if write.is_none() {
160 *write = Some((self.init)());
161 LocalContext::register_cleanup(Box::new(move |id| self.cleanup(id)));
162 }
163 drop(write);
164 read = lock.read();
165 }
166 Some(RwLockReadGuard::map(read, |o| o.as_ref().unwrap()))
167 }
168
169 fn write(&'static self) -> MappedRwLockWriteGuard<'static, T> {
170 let lock = self.app_lock();
171 let mut write = lock.write();
172 if write.is_none() {
173 *write = Some((self.init)());
174 LocalContext::register_cleanup(Box::new(move |id| self.cleanup(id)));
175 }
176 RwLockWriteGuard::map(write, |o| o.as_mut().unwrap())
177 }
178
179 fn try_write(&'static self) -> Option<MappedRwLockWriteGuard<'static, T>> {
180 let lock = self.app_lock();
181 let mut write = lock.try_write()?;
182 if write.is_none() {
183 *write = Some((self.init)());
184 LocalContext::register_cleanup(Box::new(move |id| self.cleanup(id)));
185 }
186 Some(RwLockWriteGuard::map(write, |o| o.as_mut().unwrap()))
187 }
188}
189impl<T: Send + Sync + 'static> AppLocalImpl<T> for AppLocalOption<T> {
190 fn read(&'static self) -> MappedRwLockReadGuard<'static, T> {
191 self.read_impl(self.value.read_recursive())
192 }
193
194 fn try_read(&'static self) -> Option<MappedRwLockReadGuard<'static, T>> {
195 Some(self.read_impl(self.value.try_read_recursive()?))
196 }
197
198 fn write(&'static self) -> MappedRwLockWriteGuard<'static, T> {
199 self.write_impl(self.value.write())
200 }
201
202 fn try_write(&'static self) -> Option<MappedRwLockWriteGuard<'static, T>> {
203 Some(self.write_impl(self.value.try_write()?))
204 }
205}
206impl<T: Send + Sync + 'static> AppLocalImpl<T> for AppLocalConst<T> {
207 fn read(&'static self) -> MappedRwLockReadGuard<'static, T> {
208 RwLockReadGuard::map(self.value.read(), |l| l)
209 }
210
211 fn try_read(&'static self) -> Option<MappedRwLockReadGuard<'static, T>> {
212 Some(RwLockReadGuard::map(self.value.try_read()?, |l| l))
213 }
214
215 fn write(&'static self) -> MappedRwLockWriteGuard<'static, T> {
216 RwLockWriteGuard::map(self.value.write(), |l| l)
217 }
218
219 fn try_write(&'static self) -> Option<MappedRwLockWriteGuard<'static, T>> {
220 Some(RwLockWriteGuard::map(self.value.try_write()?, |l| l))
221 }
222}
223
224pub struct AppLocal<T: Send + Sync + 'static> {
235 inner: fn() -> &'static dyn AppLocalImpl<T>,
236}
237impl<T: Send + Sync + 'static> AppLocal<T> {
238 #[doc(hidden)]
239 pub const fn new(inner: fn() -> &'static dyn AppLocalImpl<T>) -> Self {
240 AppLocal { inner }
241 }
242
243 #[inline]
251 pub fn read(&'static self) -> MappedRwLockReadGuard<'static, T> {
252 (self.inner)().read()
253 }
254
255 #[inline]
265 pub fn try_read(&'static self) -> Option<MappedRwLockReadGuard<'static, T>> {
266 (self.inner)().try_read()
267 }
268
269 #[inline]
277 pub fn write(&'static self) -> MappedRwLockWriteGuard<'static, T> {
278 (self.inner)().write()
279 }
280
281 pub fn try_write(&'static self) -> Option<MappedRwLockWriteGuard<'static, T>> {
291 (self.inner)().try_write()
292 }
293
294 #[inline]
296 pub fn get(&'static self) -> T
297 where
298 T: Clone,
299 {
300 self.read().clone()
301 }
302
303 #[inline]
305 pub fn set(&'static self, value: T) {
306 *self.write() = value;
307 }
308
309 #[inline]
313 pub fn try_get(&'static self) -> Option<T>
314 where
315 T: Clone,
316 {
317 self.try_read().map(|l| l.clone())
318 }
319
320 #[inline]
324 pub fn try_set(&'static self, value: T) -> Result<(), T> {
325 match self.try_write() {
326 Some(mut l) => {
327 *l = value;
328 Ok(())
329 }
330 None => Err(value),
331 }
332 }
333
334 #[inline]
336 pub fn read_map<O>(&'static self, map: impl FnOnce(&T) -> &O) -> MappedRwLockReadGuard<'static, O> {
337 MappedRwLockReadGuard::map(self.read(), map)
338 }
339
340 #[inline]
342 pub fn try_read_map<O>(&'static self, map: impl FnOnce(&T) -> &O) -> Option<MappedRwLockReadGuard<'static, O>> {
343 let lock = self.try_read()?;
344 Some(MappedRwLockReadGuard::map(lock, map))
345 }
346
347 #[inline]
349 pub fn write_map<O>(&'static self, map: impl FnOnce(&mut T) -> &mut O) -> MappedRwLockWriteGuard<'static, O> {
350 MappedRwLockWriteGuard::map(self.write(), map)
351 }
352
353 #[inline]
355 pub fn try_write_map<O>(&'static self, map: impl FnOnce(&mut T) -> &mut O) -> Option<MappedRwLockWriteGuard<'static, O>> {
356 let lock = self.try_write()?;
357 Some(MappedRwLockWriteGuard::map(lock, map))
358 }
359
360 pub fn id(&'static self) -> AppLocalId {
365 AppLocalId((self.inner)() as *const dyn AppLocalImpl<T> as *const () as _)
366 }
367}
368impl<T: Send + Sync + 'static> PartialEq for AppLocal<T> {
369 fn eq(&self, other: &Self) -> bool {
370 let a = AppLocalId((self.inner)() as *const dyn AppLocalImpl<T> as *const () as _);
371 let b = AppLocalId((other.inner)() as *const dyn AppLocalImpl<T> as *const () as _);
372 a == b
373 }
374}
375impl<T: Send + Sync + 'static> Eq for AppLocal<T> {}
376impl<T: Send + Sync + 'static> std::hash::Hash for AppLocal<T> {
377 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
378 let a = AppLocalId((self.inner)() as *const dyn AppLocalImpl<T> as *const () as _);
379 std::hash::Hash::hash(&a, state)
380 }
381}
382
383#[derive(PartialEq, Eq, Hash, Clone, Copy)]
389pub struct AppLocalId(pub(crate) usize);
390impl AppLocalId {
391 pub fn get(self) -> usize {
393 self.0 as _
395 }
396}
397impl fmt::Debug for AppLocalId {
398 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
399 write!(f, "AppLocalId({:#x})", self.0)
400 }
401}
402
403#[macro_export]
455macro_rules! app_local {
456 ($(
457 $(#[$meta:meta])*
458 $vis:vis static $IDENT:ident : $T:ty = $(const { $init_const:expr })? $($init:expr_2021)?;
459 )+) => {$(
460 $crate::app_local_impl! {
461 $(#[$meta])*
462 $vis static $IDENT: $T = $(const { $init_const })? $($init)?;
463 }
464 )+};
465}
466
467#[doc(hidden)]
468#[macro_export]
469macro_rules! app_local_impl_single {
470 (
471 $(#[$meta:meta])*
472 $vis:vis static $IDENT:ident : $T:ty = const { $init:expr };
473 ) => {
474 $(#[$meta])*
475 $vis static $IDENT: $crate::AppLocal<$T> = {
476 fn s() -> &'static dyn $crate::AppLocalImpl<$T> {
477 $crate::hot_static! {
478 static IMPL: $crate::AppLocalConst<$T> = $crate::AppLocalConst::new($init);
479 }
480 $crate::hot_static_ref!(IMPL)
481 }
482 $crate::AppLocal::new(s)
483 };
484 };
485 (
486 $(#[$meta:meta])*
487 $vis:vis static $IDENT:ident : $T:ty = $init:expr_2021;
488 ) => {
489 $(#[$meta])*
490 $vis static $IDENT: $crate::AppLocal<$T> = {
491 fn s() -> &'static dyn $crate::AppLocalImpl<$T> {
492 fn init() -> $T {
493 std::convert::Into::into($init)
494 }
495 $crate::hot_static! {
496 static IMPL: $crate::AppLocalOption<$T> = $crate::AppLocalOption::new(init);
497 }
498 $crate::hot_static_ref!(IMPL)
499 }
500 $crate::AppLocal::new(s)
501 };
502 };
503 (
504 $(#[$meta:meta])*
505 $vis:vis static $IDENT:ident : $T:ty = ($tt:tt)*
506 ) => {
507 std::compile_error!("expected `const { $expr };` or `$expr;`")
508 };
509}
510
511#[doc(hidden)]
512#[macro_export]
513macro_rules! app_local_impl_multi {
514 (
515 $(#[$meta:meta])*
516 $vis:vis static $IDENT:ident : $T:ty = const { $init:expr };
517 ) => {
518 $(#[$meta])*
519 $vis static $IDENT: $crate::AppLocal<$T> = {
520 fn s() -> &'static dyn $crate::AppLocalImpl<$T> {
521 const fn init() -> $T {
522 $init
523 }
524 $crate::hot_static! {
525 static IMPL: $crate::AppLocalVec<$T> = $crate::AppLocalVec::new(init);
526 }
527 $crate::hot_static_ref!(IMPL)
528 }
529 $crate::AppLocal::new(s)
530 };
531 };
532 (
533 $(#[$meta:meta])*
534 $vis:vis static $IDENT:ident : $T:ty = $init:expr_2021;
535 ) => {
536 $(#[$meta])*
537 $vis static $IDENT: $crate::AppLocal<$T> = {
538 fn s() -> &'static dyn $crate::AppLocalImpl<$T> {
539 fn init() -> $T {
540 std::convert::Into::into($init)
541 }
542 $crate::hot_static! {
543 static IMPL: $crate::AppLocalVec<$T> = $crate::AppLocalVec::new(init);
544 }
545 $crate::hot_static_ref!(IMPL)
546 }
547 $crate::AppLocal::new(s)
548 };
549 };
550 (
551 $(#[$meta:meta])*
552 $vis:vis static $IDENT:ident : $T:ty = ($tt:tt)*
553 ) => {
554 std::compile_error!("expected `const { $expr };` or `$expr;`")
555 };
556}
557
558#[cfg(feature = "multi_app")]
559#[doc(hidden)]
560pub use app_local_impl_multi as app_local_impl;
561#[cfg(not(feature = "multi_app"))]
562#[doc(hidden)]
563pub use app_local_impl_single as app_local_impl;