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::Builder::new()
88 .name("app_local_cleanup".into())
89 .spawn(move || {
90 self.try_cleanup(id, tries + 1);
91 })
92 .expect("failed to spawn thread");
93 }
94 }
95
96 fn read_impl(&'static self, read: RwLockReadGuard<'static, Vec<(AppId, T)>>) -> MappedRwLockReadGuard<'static, T> {
97 let id = LocalContext::current_app().expect("no app running, `app_local` can only be accessed inside apps");
98
99 if let Some(i) = read.iter().position(|(s, _)| *s == id) {
100 return RwLockReadGuard::map(read, |v| &v[i].1);
101 }
102 drop(read);
103
104 let mut write = self.value.write();
105 if write.iter().any(|(s, _)| *s == id) {
106 drop(write);
107 return self.read();
108 }
109
110 let value = (self.init)();
111 let i = write.len();
112 write.push((id, value));
113
114 LocalContext::register_cleanup(Box::new(move |id| self.cleanup(id)));
115
116 let read = RwLockWriteGuard::downgrade(write);
117
118 RwLockReadGuard::map(read, |v| &v[i].1)
119 }
120
121 fn write_impl(&'static self, mut write: RwLockWriteGuard<'static, Vec<(AppId, T)>>) -> MappedRwLockWriteGuard<'static, T> {
122 let id = LocalContext::current_app().expect("no app running, `app_local` can only be accessed inside apps");
123
124 if let Some(i) = write.iter().position(|(s, _)| *s == id) {
125 return RwLockWriteGuard::map(write, |v| &mut v[i].1);
126 }
127
128 let value = (self.init)();
129 let i = write.len();
130 write.push((id, value));
131
132 LocalContext::register_cleanup(move |id| self.cleanup(id));
133
134 RwLockWriteGuard::map(write, |v| &mut v[i].1)
135 }
136}
137#[doc(hidden)]
138pub trait AppLocalImpl<T: Send + Sync + 'static>: Send + Sync + 'static {
139 fn read(&'static self) -> MappedRwLockReadGuard<'static, T>;
140 fn try_read(&'static self) -> Option<MappedRwLockReadGuard<'static, T>>;
141 fn write(&'static self) -> MappedRwLockWriteGuard<'static, T>;
142 fn try_write(&'static self) -> Option<MappedRwLockWriteGuard<'static, T>>;
143}
144
145impl<T: Send + Sync + 'static> AppLocalImpl<T> for AppLocalVec<T> {
146 fn read(&'static self) -> MappedRwLockReadGuard<'static, T> {
147 self.read_impl(self.value.read_recursive())
148 }
149
150 fn try_read(&'static self) -> Option<MappedRwLockReadGuard<'static, T>> {
151 Some(self.read_impl(self.value.try_read_recursive()?))
152 }
153
154 fn write(&'static self) -> MappedRwLockWriteGuard<'static, T> {
155 self.write_impl(self.value.write())
156 }
157
158 fn try_write(&'static self) -> Option<MappedRwLockWriteGuard<'static, T>> {
159 Some(self.write_impl(self.value.try_write()?))
160 }
161}
162impl<T: Send + Sync + 'static> AppLocalImpl<T> for AppLocalOption<T> {
163 fn read(&'static self) -> MappedRwLockReadGuard<'static, T> {
164 self.read_impl(self.value.read_recursive())
165 }
166
167 fn try_read(&'static self) -> Option<MappedRwLockReadGuard<'static, T>> {
168 Some(self.read_impl(self.value.try_read_recursive()?))
169 }
170
171 fn write(&'static self) -> MappedRwLockWriteGuard<'static, T> {
172 self.write_impl(self.value.write())
173 }
174
175 fn try_write(&'static self) -> Option<MappedRwLockWriteGuard<'static, T>> {
176 Some(self.write_impl(self.value.try_write()?))
177 }
178}
179impl<T: Send + Sync + 'static> AppLocalImpl<T> for AppLocalConst<T> {
180 fn read(&'static self) -> MappedRwLockReadGuard<'static, T> {
181 RwLockReadGuard::map(self.value.read(), |l| l)
182 }
183
184 fn try_read(&'static self) -> Option<MappedRwLockReadGuard<'static, T>> {
185 Some(RwLockReadGuard::map(self.value.try_read()?, |l| l))
186 }
187
188 fn write(&'static self) -> MappedRwLockWriteGuard<'static, T> {
189 RwLockWriteGuard::map(self.value.write(), |l| l)
190 }
191
192 fn try_write(&'static self) -> Option<MappedRwLockWriteGuard<'static, T>> {
193 Some(RwLockWriteGuard::map(self.value.try_write()?, |l| l))
194 }
195}
196
197pub struct AppLocal<T: Send + Sync + 'static> {
208 inner: fn() -> &'static dyn AppLocalImpl<T>,
209}
210impl<T: Send + Sync + 'static> AppLocal<T> {
211 #[doc(hidden)]
212 pub const fn new(inner: fn() -> &'static dyn AppLocalImpl<T>) -> Self {
213 AppLocal { inner }
214 }
215
216 #[inline]
224 pub fn read(&'static self) -> MappedRwLockReadGuard<'static, T> {
225 (self.inner)().read()
226 }
227
228 #[inline]
238 pub fn try_read(&'static self) -> Option<MappedRwLockReadGuard<'static, T>> {
239 (self.inner)().try_read()
240 }
241
242 #[inline]
250 pub fn write(&'static self) -> MappedRwLockWriteGuard<'static, T> {
251 (self.inner)().write()
252 }
253
254 pub fn try_write(&'static self) -> Option<MappedRwLockWriteGuard<'static, T>> {
264 (self.inner)().try_write()
265 }
266
267 #[inline]
269 pub fn get(&'static self) -> T
270 where
271 T: Clone,
272 {
273 self.read().clone()
274 }
275
276 #[inline]
278 pub fn set(&'static self, value: T) {
279 *self.write() = value;
280 }
281
282 #[inline]
286 pub fn try_get(&'static self) -> Option<T>
287 where
288 T: Clone,
289 {
290 self.try_read().map(|l| l.clone())
291 }
292
293 #[inline]
297 pub fn try_set(&'static self, value: T) -> Result<(), T> {
298 match self.try_write() {
299 Some(mut l) => {
300 *l = value;
301 Ok(())
302 }
303 None => Err(value),
304 }
305 }
306
307 #[inline]
309 pub fn read_map<O>(&'static self, map: impl FnOnce(&T) -> &O) -> MappedRwLockReadGuard<'static, O> {
310 MappedRwLockReadGuard::map(self.read(), map)
311 }
312
313 #[inline]
315 pub fn try_read_map<O>(&'static self, map: impl FnOnce(&T) -> &O) -> Option<MappedRwLockReadGuard<'static, O>> {
316 let lock = self.try_read()?;
317 Some(MappedRwLockReadGuard::map(lock, map))
318 }
319
320 #[inline]
322 pub fn write_map<O>(&'static self, map: impl FnOnce(&mut T) -> &mut O) -> MappedRwLockWriteGuard<'static, O> {
323 MappedRwLockWriteGuard::map(self.write(), map)
324 }
325
326 #[inline]
328 pub fn try_write_map<O>(&'static self, map: impl FnOnce(&mut T) -> &mut O) -> Option<MappedRwLockWriteGuard<'static, O>> {
329 let lock = self.try_write()?;
330 Some(MappedRwLockWriteGuard::map(lock, map))
331 }
332
333 pub fn id(&'static self) -> AppLocalId {
338 AppLocalId((self.inner)() as *const dyn AppLocalImpl<T> as *const () as _)
339 }
340}
341impl<T: Send + Sync + 'static> PartialEq for AppLocal<T> {
342 fn eq(&self, other: &Self) -> bool {
343 let a = AppLocalId((self.inner)() as *const dyn AppLocalImpl<T> as *const () as _);
344 let b = AppLocalId((other.inner)() as *const dyn AppLocalImpl<T> as *const () as _);
345 a == b
346 }
347}
348impl<T: Send + Sync + 'static> Eq for AppLocal<T> {}
349impl<T: Send + Sync + 'static> std::hash::Hash for AppLocal<T> {
350 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
351 let a = AppLocalId((self.inner)() as *const dyn AppLocalImpl<T> as *const () as _);
352 std::hash::Hash::hash(&a, state)
353 }
354}
355
356#[derive(PartialEq, Eq, Hash, Clone, Copy)]
362pub struct AppLocalId(pub(crate) usize);
363impl AppLocalId {
364 pub fn get(self) -> usize {
366 self.0 as _
368 }
369}
370impl fmt::Debug for AppLocalId {
371 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
372 write!(f, "AppLocalId({:#x})", self.0)
373 }
374}
375
376#[macro_export]
428macro_rules! app_local {
429 ($(
430 $(#[$meta:meta])*
431 $vis:vis static $IDENT:ident : $T:ty = $(const { $init_const:expr })? $($init:expr_2021)?;
432 )+) => {$(
433 $crate::app_local_impl! {
434 $(#[$meta])*
435 $vis static $IDENT: $T = $(const { $init_const })? $($init)?;
436 }
437 )+};
438}
439
440#[doc(hidden)]
441#[macro_export]
442macro_rules! app_local_impl_single {
443 (
444 $(#[$meta:meta])*
445 $vis:vis static $IDENT:ident : $T:ty = const { $init:expr };
446 ) => {
447 $(#[$meta])*
448 $vis static $IDENT: $crate::AppLocal<$T> = {
449 fn s() -> &'static dyn $crate::AppLocalImpl<$T> {
450 $crate::hot_static! {
451 static IMPL: $crate::AppLocalConst<$T> = $crate::AppLocalConst::new($init);
452 }
453 $crate::hot_static_ref!(IMPL)
454 }
455 $crate::AppLocal::new(s)
456 };
457 };
458 (
459 $(#[$meta:meta])*
460 $vis:vis static $IDENT:ident : $T:ty = $init:expr_2021;
461 ) => {
462 $(#[$meta])*
463 $vis static $IDENT: $crate::AppLocal<$T> = {
464 fn s() -> &'static dyn $crate::AppLocalImpl<$T> {
465 fn init() -> $T {
466 std::convert::Into::into($init)
467 }
468 $crate::hot_static! {
469 static IMPL: $crate::AppLocalOption<$T> = $crate::AppLocalOption::new(init);
470 }
471 $crate::hot_static_ref!(IMPL)
472 }
473 $crate::AppLocal::new(s)
474 };
475 };
476 (
477 $(#[$meta:meta])*
478 $vis:vis static $IDENT:ident : $T:ty = ($tt:tt)*
479 ) => {
480 std::compile_error!("expected `const { $expr };` or `$expr;`")
481 };
482}
483
484#[doc(hidden)]
485#[macro_export]
486macro_rules! app_local_impl_multi {
487 (
488 $(#[$meta:meta])*
489 $vis:vis static $IDENT:ident : $T:ty = const { $init:expr };
490 ) => {
491 $(#[$meta])*
492 $vis static $IDENT: $crate::AppLocal<$T> = {
493 fn s() -> &'static dyn $crate::AppLocalImpl<$T> {
494 const fn init() -> $T {
495 $init
496 }
497 $crate::hot_static! {
498 static IMPL: $crate::AppLocalVec<$T> = $crate::AppLocalVec::new(init);
499 }
500 $crate::hot_static_ref!(IMPL)
501 }
502 $crate::AppLocal::new(s)
503 };
504 };
505 (
506 $(#[$meta:meta])*
507 $vis:vis static $IDENT:ident : $T:ty = $init:expr_2021;
508 ) => {
509 $(#[$meta])*
510 $vis static $IDENT: $crate::AppLocal<$T> = {
511 fn s() -> &'static dyn $crate::AppLocalImpl<$T> {
512 fn init() -> $T {
513 std::convert::Into::into($init)
514 }
515 $crate::hot_static! {
516 static IMPL: $crate::AppLocalVec<$T> = $crate::AppLocalVec::new(init);
517 }
518 $crate::hot_static_ref!(IMPL)
519 }
520 $crate::AppLocal::new(s)
521 };
522 };
523 (
524 $(#[$meta:meta])*
525 $vis:vis static $IDENT:ident : $T:ty = ($tt:tt)*
526 ) => {
527 std::compile_error!("expected `const { $expr };` or `$expr;`")
528 };
529}
530
531use std::{fmt, time::Duration};
532
533#[cfg(feature = "multi_app")]
534#[doc(hidden)]
535pub use app_local_impl_multi as app_local_impl;
536#[cfg(not(feature = "multi_app"))]
537#[doc(hidden)]
538pub use app_local_impl_single as app_local_impl;