1use std::{
4 any::{Any, TypeId},
5 fmt,
6 sync::{Arc, Weak},
7};
8
9use parking_lot::{Mutex, RwLock};
10use smallbox::{SmallBox, smallbox};
11use zng_app_context::context_local;
12
13use crate::{AnyVar, DynAnyVar, DynWeakAnyVar, Var, VarHandle, VarImpl, VarInstanceTag, VarValue, WeakVarImpl};
14
15use super::VarCapability;
16
17pub fn any_contextual_var(context_init: impl FnMut() -> AnyVar + Send + 'static, value_type: TypeId) -> AnyVar {
25 any_contextual_var_impl(smallbox!(ContextInitFnMut(context_init)), value_type)
26}
27pub(super) fn any_contextual_var_impl(context_init: ContextInitFn, value_type: TypeId) -> AnyVar {
28 AnyVar(DynAnyVar::Contextual(ContextualVar::new(context_init, value_type)))
29}
30
31pub fn contextual_var<T: VarValue>(mut context_init: impl FnMut() -> Var<T> + Send + 'static) -> Var<T> {
70 Var::new_any(any_contextual_var(move || context_init().into(), TypeId::of::<T>()))
71}
72
73pub(super) type ContextInitFn = SmallBox<dyn ContextInitFnImpl, smallbox::space::S8>;
74pub(crate) trait ContextInitFnImpl: Send + Any {
76 fn init(&mut self) -> AnyVar;
77}
78struct ContextInitFnMut<F>(F);
79impl<F: FnMut() -> AnyVar + Send + 'static> ContextInitFnImpl for ContextInitFnMut<F> {
80 fn init(&mut self) -> AnyVar {
81 self.0()
82 }
83}
84
85pub(super) struct ContextualVarData {
86 pub(super) init: Arc<Mutex<ContextInitFn>>,
87 ctx: RwLock<(AnyVar, ContextInitHandle)>,
88}
89
90pub(crate) struct ContextualVar(pub(super) Box<ContextualVarData>);
91impl Clone for ContextualVar {
92 fn clone(&self) -> Self {
93 Self(Box::new(ContextualVarData {
94 init: self.0.init.clone(),
95 ctx: RwLock::new((no_ctx_var(self.value_type()), ContextInitHandle::no_context())),
96 }))
97 }
98}
99impl fmt::Debug for ContextualVar {
100 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101 let mut b = f.debug_struct("ContextualVar");
102 b.field("init", &Arc::as_ptr(&self.0.init));
103 if let Some(ctx) = self.0.ctx.try_read() {
104 if ctx.1.is_no_context() {
105 b.field("ctx", &"<no context>");
106 } else {
107 b.field("ctx.handle", &ctx.1);
108 b.field("ctx.var", &ctx.0.0);
109 }
110 } else {
111 b.field("ctx", &"<locked>");
112 }
113
114 b.finish()
115 }
116}
117impl PartialEq for ContextualVar {
118 fn eq(&self, other: &Self) -> bool {
119 Arc::ptr_eq(&self.0.init, &other.0.init) && {
120 let a = self.0.ctx.read_recursive();
121 let b = other.0.ctx.read_recursive();
122 a.1 == b.1 && a.0.var_eq(&b.0)
123 }
124 }
125}
126impl ContextualVar {
127 pub fn new(init: ContextInitFn, value_type: TypeId) -> Self {
128 Self(Box::new(ContextualVarData {
129 init: Arc::new(Mutex::new(init)),
130 ctx: RwLock::new((no_ctx_var(value_type), ContextInitHandle::no_context())),
131 }))
132 }
133
134 fn load(&self) -> parking_lot::MappedRwLockReadGuard<'_, AnyVar> {
135 let ctx = self.0.ctx.read();
136 let id = ContextInitHandle::current();
137 if ctx.1 == id {
138 parking_lot::RwLockReadGuard::map(ctx, |f| &f.0)
139 } else {
140 drop(ctx);
141 let mut ctx = self.0.ctx.write();
142 if ctx.1 != id {
143 ctx.0 = self.0.init.lock().init();
144 if ctx.0.capabilities().is_contextual() {
145 ctx.0 = ctx.0.current_context();
146 }
147 ctx.1 = id;
148 }
149 let ctx = parking_lot::RwLockWriteGuard::downgrade(ctx);
150 parking_lot::RwLockReadGuard::map(ctx, |f| &f.0)
151 }
152 }
153}
154impl VarImpl for ContextualVar {
155 fn clone_dyn(&self) -> DynAnyVar {
156 DynAnyVar::Contextual(self.clone())
157 }
158
159 fn current_context(&self) -> DynAnyVar {
160 self.load().0.current_context()
161 }
162
163 fn value_type(&self) -> std::any::TypeId {
164 let ctx = self.0.ctx.read();
165 let (var, ctx) = &*ctx;
166 if ctx.is_no_context() {
167 var.with(|v| v.downcast_ref::<NoContext>().unwrap().value_type)
168 } else {
169 var.value_type()
170 }
171 }
172
173 #[cfg(feature = "type_names")]
174 fn value_type_name(&self) -> &'static str {
175 self.load().0.value_type_name()
176 }
177
178 fn strong_count(&self) -> usize {
179 self.load().0.strong_count()
180 }
181
182 fn var_eq(&self, other: &DynAnyVar) -> bool {
183 match other {
184 DynAnyVar::Contextual(o) => self == o,
185 _ => false,
186 }
187 }
188
189 fn var_instance_tag(&self) -> VarInstanceTag {
190 self.load().0.var_instance_tag()
191 }
192
193 fn downgrade(&self) -> DynWeakAnyVar {
194 DynWeakAnyVar::Contextual(WeakContextualVar(Box::new(WeakContextualVarData {
195 init: Arc::downgrade(&self.0.init),
196 value_type: self.value_type(),
197 })))
198 }
199
200 fn capabilities(&self) -> VarCapability {
201 let mut caps = VarCapability::CONTEXT | VarCapability::MODIFY_CHANGES;
202 let ctx = self.0.ctx.read();
203 if ctx.1 == ContextInitHandle::current() {
204 let mut inner = ctx.0.capabilities();
205 inner.remove(VarCapability::CONTEXT_CHANGES);
206 caps |= inner;
207 }
208 caps
209 }
210
211 fn with(&self, visitor: &mut dyn FnMut(&dyn crate::AnyVarValue)) {
212 self.load().0.with(visitor);
213 }
214
215 fn get(&self) -> crate::BoxAnyVarValue {
216 self.load().0.get()
217 }
218
219 fn set(&self, new_value: crate::BoxAnyVarValue) -> bool {
220 self.load().0.set(new_value)
221 }
222
223 fn update(&self) -> bool {
224 self.load().0.update()
225 }
226
227 fn modify(&self, modify: SmallBox<dyn FnMut(&mut super::AnyVarModify) + Send + 'static, smallbox::space::S4>) -> bool {
228 self.load().0.modify(modify)
229 }
230
231 fn hook(&self, on_new: SmallBox<dyn FnMut(&crate::AnyVarHookArgs) -> bool + Send + 'static, smallbox::space::S4>) -> super::VarHandle {
232 self.load().0.hook(on_new)
233 }
234
235 fn last_update(&self) -> crate::VarUpdateId {
236 self.load().0.last_update()
237 }
238
239 fn modify_info(&self) -> crate::animation::ModifyInfo {
240 self.load().0.modify_info()
241 }
242
243 fn modify_importance(&self) -> usize {
244 self.load().0.modify_importance()
245 }
246
247 fn is_animating(&self) -> bool {
248 self.load().0.is_animating()
249 }
250
251 fn hook_animation_stop(&self, handler: crate::animation::AnimationStopFn) -> VarHandle {
252 self.load().0.hook_animation_stop(handler)
253 }
254}
255
256#[derive(Clone)]
257
258struct WeakContextualVarData {
259 init: Weak<Mutex<ContextInitFn>>,
260 value_type: TypeId,
261}
262
263#[derive(Clone)]
264pub(crate) struct WeakContextualVar(Box<WeakContextualVarData>);
265impl fmt::Debug for WeakContextualVar {
266 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
267 f.debug_struct("WeakContextualVar").field("init", &self.0.init.as_ptr()).finish()
268 }
269}
270impl WeakVarImpl for WeakContextualVar {
271 fn clone_dyn(&self) -> DynWeakAnyVar {
272 DynWeakAnyVar::Contextual(self.clone())
273 }
274
275 fn strong_count(&self) -> usize {
276 self.0.init.strong_count()
277 }
278
279 fn upgrade(&self) -> Option<DynAnyVar> {
280 self.0.init.upgrade().map(|init| {
281 DynAnyVar::Contextual(ContextualVar(Box::new(ContextualVarData {
282 init,
283 ctx: RwLock::new((no_ctx_var(self.0.value_type), ContextInitHandle::no_context())),
284 })))
285 })
286 }
287}
288
289fn no_ctx_var(value_type: TypeId) -> AnyVar {
290 crate::const_var(NoContext { value_type }).into()
291}
292
293#[derive(Debug, PartialEq, Clone)]
294struct NoContext {
295 value_type: TypeId,
296}
297
298#[derive(Default)]
299struct ContextInitHandleMarker;
300
301#[derive(Clone, Default)]
307pub struct ContextInitHandle(Option<Arc<ContextInitHandleMarker>>);
308context_local! {
309 static CONTEXT_INIT_ID: ContextInitHandleMarker = ContextInitHandleMarker;
310}
311impl ContextInitHandle {
312 pub fn new() -> Self {
314 Self(Some(Arc::new(ContextInitHandleMarker)))
315 }
316
317 pub const fn no_context() -> Self {
321 Self(None)
322 }
323
324 pub fn current() -> Self {
332 Self(Some(CONTEXT_INIT_ID.get()))
333 }
334
335 pub fn is_no_context(&self) -> bool {
337 self.0.is_none()
338 }
339
340 #[inline(always)]
351 pub fn with_context<R>(&self, action: impl FnOnce() -> R) -> R {
352 let mut opt = self.0.clone();
353 CONTEXT_INIT_ID.with_context(&mut opt, action)
354 }
355
356 pub fn downgrade(&self) -> WeakContextInitHandle {
358 match &self.0 {
359 Some(a) => WeakContextInitHandle(Arc::downgrade(a)),
360 None => WeakContextInitHandle(std::sync::Weak::new()),
361 }
362 }
363}
364impl fmt::Debug for ContextInitHandle {
365 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
366 f.debug_tuple("WeakContextInitHandle")
367 .field(&match &self.0 {
368 Some(a) => std::sync::Arc::as_ptr(a),
369 None => std::ptr::null(),
370 })
371 .finish()
372 }
373}
374impl PartialEq for ContextInitHandle {
375 fn eq(&self, other: &Self) -> bool {
376 match (&self.0, &other.0) {
377 (Some(a), Some(b)) => Arc::ptr_eq(a, b),
378 (None, None) => true,
379 _ => false,
380 }
381 }
382}
383impl Eq for ContextInitHandle {}
384
385#[derive(Clone, Default)]
387pub struct WeakContextInitHandle(std::sync::Weak<ContextInitHandleMarker>);
388impl WeakContextInitHandle {
389 pub fn is_alive(&self) -> bool {
391 self.0.strong_count() > 0
392 }
393}
394impl fmt::Debug for WeakContextInitHandle {
395 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
396 f.debug_tuple("WeakContextInitHandle")
397 .field(&std::sync::Weak::as_ptr(&self.0))
398 .finish()
399 }
400}
401impl PartialEq for WeakContextInitHandle {
402 fn eq(&self, other: &Self) -> bool {
403 std::sync::Weak::ptr_eq(&self.0, &other.0)
404 }
405}
406impl Eq for WeakContextInitHandle {}
407impl std::hash::Hash for WeakContextInitHandle {
408 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
409 let i = std::sync::Weak::as_ptr(&self.0) as usize;
410 std::hash::Hash::hash(&i, state)
411 }
412}