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 loop {
141 if read.is_some() {
142 return RwLockReadGuard::map(read, |o| o.as_ref().unwrap());
143 }
144 drop(read);
145 let mut write = lock.write();
146 if write.is_none() {
147 *write = Some((self.init)());
148 LocalContext::register_cleanup(Box::new(move |id| self.cleanup(id)));
149 }
150 drop(write);
151 read = lock.read();
152 }
153 }
154
155 fn try_read(&'static self) -> Option<MappedRwLockReadGuard<'static, T>> {
156 let lock = self.app_lock();
157 let mut read = lock.try_read()?;
158 loop {
159 if read.is_some() {
160 return Some(RwLockReadGuard::map(read, |o| o.as_ref().unwrap()));
161 }
162 drop(read);
163 let mut write = lock.try_write()?;
164 if write.is_none() {
165 *write = Some((self.init)());
166 LocalContext::register_cleanup(Box::new(move |id| self.cleanup(id)));
167 }
168 drop(write);
169 read = lock.read();
170 }
171 }
172
173 fn write(&'static self) -> MappedRwLockWriteGuard<'static, T> {
174 let lock = self.app_lock();
175 let mut write = lock.write();
176 if write.is_none() {
177 *write = Some((self.init)());
178 LocalContext::register_cleanup(Box::new(move |id| self.cleanup(id)));
179 }
180 RwLockWriteGuard::map(write, |o| o.as_mut().unwrap())
181 }
182
183 fn try_write(&'static self) -> Option<MappedRwLockWriteGuard<'static, T>> {
184 let lock = self.app_lock();
185 let mut write = lock.try_write()?;
186 if write.is_none() {
187 *write = Some((self.init)());
188 LocalContext::register_cleanup(Box::new(move |id| self.cleanup(id)));
189 }
190 Some(RwLockWriteGuard::map(write, |o| o.as_mut().unwrap()))
191 }
192}
193impl<T: Send + Sync + 'static> AppLocalImpl<T> for AppLocalOption<T> {
194 fn read(&'static self) -> MappedRwLockReadGuard<'static, T> {
195 self.read_impl(self.value.read_recursive())
196 }
197
198 fn try_read(&'static self) -> Option<MappedRwLockReadGuard<'static, T>> {
199 Some(self.read_impl(self.value.try_read_recursive()?))
200 }
201
202 fn write(&'static self) -> MappedRwLockWriteGuard<'static, T> {
203 self.write_impl(self.value.write())
204 }
205
206 fn try_write(&'static self) -> Option<MappedRwLockWriteGuard<'static, T>> {
207 Some(self.write_impl(self.value.try_write()?))
208 }
209}
210impl<T: Send + Sync + 'static> AppLocalImpl<T> for AppLocalConst<T> {
211 fn read(&'static self) -> MappedRwLockReadGuard<'static, T> {
212 RwLockReadGuard::map(self.value.read(), |l| l)
213 }
214
215 fn try_read(&'static self) -> Option<MappedRwLockReadGuard<'static, T>> {
216 Some(RwLockReadGuard::map(self.value.try_read()?, |l| l))
217 }
218
219 fn write(&'static self) -> MappedRwLockWriteGuard<'static, T> {
220 RwLockWriteGuard::map(self.value.write(), |l| l)
221 }
222
223 fn try_write(&'static self) -> Option<MappedRwLockWriteGuard<'static, T>> {
224 Some(RwLockWriteGuard::map(self.value.try_write()?, |l| l))
225 }
226}
227
228pub struct AppLocal<T: Send + Sync + 'static> {
239 inner: fn() -> &'static dyn AppLocalImpl<T>,
240}
241impl<T: Send + Sync + 'static> AppLocal<T> {
242 #[doc(hidden)]
243 pub const fn new(inner: fn() -> &'static dyn AppLocalImpl<T>) -> Self {
244 AppLocal { inner }
245 }
246
247 #[inline]
255 pub fn read(&'static self) -> MappedRwLockReadGuard<'static, T> {
256 (self.inner)().read()
257 }
258
259 #[inline]
269 pub fn try_read(&'static self) -> Option<MappedRwLockReadGuard<'static, T>> {
270 (self.inner)().try_read()
271 }
272
273 #[inline]
281 pub fn write(&'static self) -> MappedRwLockWriteGuard<'static, T> {
282 (self.inner)().write()
283 }
284
285 pub fn try_write(&'static self) -> Option<MappedRwLockWriteGuard<'static, T>> {
295 (self.inner)().try_write()
296 }
297
298 #[inline]
300 pub fn get(&'static self) -> T
301 where
302 T: Clone,
303 {
304 self.read().clone()
305 }
306
307 #[inline]
309 pub fn set(&'static self, value: T) {
310 *self.write() = value;
311 }
312
313 #[inline]
317 pub fn try_get(&'static self) -> Option<T>
318 where
319 T: Clone,
320 {
321 self.try_read().map(|l| l.clone())
322 }
323
324 #[inline]
328 pub fn try_set(&'static self, value: T) -> Result<(), T> {
329 match self.try_write() {
330 Some(mut l) => {
331 *l = value;
332 Ok(())
333 }
334 None => Err(value),
335 }
336 }
337
338 #[inline]
340 pub fn read_map<O>(&'static self, map: impl FnOnce(&T) -> &O) -> MappedRwLockReadGuard<'static, O> {
341 MappedRwLockReadGuard::map(self.read(), map)
342 }
343
344 #[inline]
346 pub fn try_read_map<O>(&'static self, map: impl FnOnce(&T) -> &O) -> Option<MappedRwLockReadGuard<'static, O>> {
347 let lock = self.try_read()?;
348 Some(MappedRwLockReadGuard::map(lock, map))
349 }
350
351 #[inline]
353 pub fn write_map<O>(&'static self, map: impl FnOnce(&mut T) -> &mut O) -> MappedRwLockWriteGuard<'static, O> {
354 MappedRwLockWriteGuard::map(self.write(), map)
355 }
356
357 #[inline]
359 pub fn try_write_map<O>(&'static self, map: impl FnOnce(&mut T) -> &mut O) -> Option<MappedRwLockWriteGuard<'static, O>> {
360 let lock = self.try_write()?;
361 Some(MappedRwLockWriteGuard::map(lock, map))
362 }
363
364 pub fn id(&'static self) -> AppLocalId {
369 AppLocalId((self.inner)() as *const dyn AppLocalImpl<T> as *const () as _)
370 }
371}
372impl<T: Send + Sync + 'static> PartialEq for AppLocal<T> {
373 fn eq(&self, other: &Self) -> bool {
374 let a = AppLocalId((self.inner)() as *const dyn AppLocalImpl<T> as *const () as _);
375 let b = AppLocalId((other.inner)() as *const dyn AppLocalImpl<T> as *const () as _);
376 a == b
377 }
378}
379impl<T: Send + Sync + 'static> Eq for AppLocal<T> {}
380impl<T: Send + Sync + 'static> std::hash::Hash for AppLocal<T> {
381 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
382 let a = AppLocalId((self.inner)() as *const dyn AppLocalImpl<T> as *const () as _);
383 std::hash::Hash::hash(&a, state)
384 }
385}
386
387#[derive(PartialEq, Eq, Hash, Clone, Copy)]
393pub struct AppLocalId(pub(crate) usize);
394impl AppLocalId {
395 pub fn get(self) -> usize {
397 self.0 as _
399 }
400}
401impl fmt::Debug for AppLocalId {
402 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
403 write!(f, "AppLocalId({:#x})", self.0)
404 }
405}
406
407#[macro_export]
459macro_rules! app_local {
460 ($(
461 $(#[$meta:meta])*
462 $vis:vis static $IDENT:ident : $T:ty = $(const { $init_const:expr })? $($init:expr_2021)?;
463 )+) => {$(
464 $crate::app_local_impl! {
465 $(#[$meta])*
466 $vis static $IDENT: $T = $(const { $init_const })? $($init)?;
467 }
468 )+};
469}
470
471#[doc(hidden)]
472#[macro_export]
473macro_rules! app_local_impl_single {
474 (
475 $(#[$meta:meta])*
476 $vis:vis static $IDENT:ident : $T:ty = const { $init:expr };
477 ) => {
478 $(#[$meta])*
479 $vis static $IDENT: $crate::AppLocal<$T> = {
480 fn s() -> &'static dyn $crate::AppLocalImpl<$T> {
481 $crate::hot_static! {
482 static IMPL: $crate::AppLocalConst<$T> = $crate::AppLocalConst::new($init);
483 }
484 $crate::hot_static_ref!(IMPL)
485 }
486 $crate::AppLocal::new(s)
487 };
488 };
489 (
490 $(#[$meta:meta])*
491 $vis:vis static $IDENT:ident : $T:ty = $init:expr_2021;
492 ) => {
493 $(#[$meta])*
494 $vis static $IDENT: $crate::AppLocal<$T> = {
495 fn s() -> &'static dyn $crate::AppLocalImpl<$T> {
496 fn init() -> $T {
497 std::convert::Into::into($init)
498 }
499 $crate::hot_static! {
500 static IMPL: $crate::AppLocalOption<$T> = $crate::AppLocalOption::new(init);
501 }
502 $crate::hot_static_ref!(IMPL)
503 }
504 $crate::AppLocal::new(s)
505 };
506 };
507 (
508 $(#[$meta:meta])*
509 $vis:vis static $IDENT:ident : $T:ty = ($tt:tt)*
510 ) => {
511 std::compile_error!("expected `const { $expr };` or `$expr;`")
512 };
513}
514
515#[doc(hidden)]
516#[macro_export]
517macro_rules! app_local_impl_multi {
518 (
519 $(#[$meta:meta])*
520 $vis:vis static $IDENT:ident : $T:ty = const { $init:expr };
521 ) => {
522 $(#[$meta])*
523 $vis static $IDENT: $crate::AppLocal<$T> = {
524 fn s() -> &'static dyn $crate::AppLocalImpl<$T> {
525 const fn init() -> $T {
526 $init
527 }
528 $crate::hot_static! {
529 static IMPL: $crate::AppLocalVec<$T> = $crate::AppLocalVec::new(init);
530 }
531 $crate::hot_static_ref!(IMPL)
532 }
533 $crate::AppLocal::new(s)
534 };
535 };
536 (
537 $(#[$meta:meta])*
538 $vis:vis static $IDENT:ident : $T:ty = $init:expr_2021;
539 ) => {
540 $(#[$meta])*
541 $vis static $IDENT: $crate::AppLocal<$T> = {
542 fn s() -> &'static dyn $crate::AppLocalImpl<$T> {
543 fn init() -> $T {
544 std::convert::Into::into($init)
545 }
546 $crate::hot_static! {
547 static IMPL: $crate::AppLocalVec<$T> = $crate::AppLocalVec::new(init);
548 }
549 $crate::hot_static_ref!(IMPL)
550 }
551 $crate::AppLocal::new(s)
552 };
553 };
554 (
555 $(#[$meta:meta])*
556 $vis:vis static $IDENT:ident : $T:ty = ($tt:tt)*
557 ) => {
558 std::compile_error!("expected `const { $expr };` or `$expr;`")
559 };
560}
561
562#[cfg(feature = "multi_app")]
563#[doc(hidden)]
564pub use app_local_impl_multi as app_local_impl;
565#[cfg(not(feature = "multi_app"))]
566#[doc(hidden)]
567pub use app_local_impl_single as app_local_impl;