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 PartialEq for WeakContextualVar {
271 fn eq(&self, other: &Self) -> bool {
272 self.0.init.ptr_eq(&other.0.init)
273 }
274}
275impl WeakVarImpl for WeakContextualVar {
276 fn clone_dyn(&self) -> DynWeakAnyVar {
277 DynWeakAnyVar::Contextual(self.clone())
278 }
279
280 fn strong_count(&self) -> usize {
281 self.0.init.strong_count()
282 }
283
284 fn upgrade(&self) -> Option<DynAnyVar> {
285 self.0.init.upgrade().map(|init| {
286 DynAnyVar::Contextual(ContextualVar(Box::new(ContextualVarData {
287 init,
288 ctx: RwLock::new((no_ctx_var(self.0.value_type), ContextInitHandle::no_context())),
289 })))
290 })
291 }
292
293 fn var_eq(&self, other: &DynWeakAnyVar) -> bool {
294 match other {
295 DynWeakAnyVar::Contextual(b) => self == b,
296 _ => false,
297 }
298 }
299}
300
301fn no_ctx_var(value_type: TypeId) -> AnyVar {
302 crate::const_var(NoContext { value_type }).into()
303}
304
305#[derive(Debug, PartialEq, Clone)]
306struct NoContext {
307 value_type: TypeId,
308}
309
310#[derive(Default)]
311struct ContextInitHandleMarker;
312
313#[derive(Clone, Default)]
319pub struct ContextInitHandle(Option<Arc<ContextInitHandleMarker>>);
320context_local! {
321 static CONTEXT_INIT_ID: ContextInitHandleMarker = ContextInitHandleMarker;
322}
323impl ContextInitHandle {
324 pub fn new() -> Self {
326 Self(Some(Arc::new(ContextInitHandleMarker)))
327 }
328
329 pub const fn no_context() -> Self {
333 Self(None)
334 }
335
336 pub fn current() -> Self {
344 Self(Some(CONTEXT_INIT_ID.get()))
345 }
346
347 pub fn is_no_context(&self) -> bool {
349 self.0.is_none()
350 }
351
352 #[inline(always)]
363 pub fn with_context<R>(&self, action: impl FnOnce() -> R) -> R {
364 let mut opt = self.0.clone();
365 CONTEXT_INIT_ID.with_context(&mut opt, action)
366 }
367
368 pub fn downgrade(&self) -> WeakContextInitHandle {
370 match &self.0 {
371 Some(a) => WeakContextInitHandle(Arc::downgrade(a)),
372 None => WeakContextInitHandle(std::sync::Weak::new()),
373 }
374 }
375}
376impl fmt::Debug for ContextInitHandle {
377 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
378 f.debug_tuple("WeakContextInitHandle")
379 .field(&match &self.0 {
380 Some(a) => std::sync::Arc::as_ptr(a),
381 None => std::ptr::null(),
382 })
383 .finish()
384 }
385}
386impl PartialEq for ContextInitHandle {
387 fn eq(&self, other: &Self) -> bool {
388 match (&self.0, &other.0) {
389 (Some(a), Some(b)) => Arc::ptr_eq(a, b),
390 (None, None) => true,
391 _ => false,
392 }
393 }
394}
395impl Eq for ContextInitHandle {}
396
397#[derive(Clone, Default)]
399pub struct WeakContextInitHandle(std::sync::Weak<ContextInitHandleMarker>);
400impl WeakContextInitHandle {
401 pub fn is_alive(&self) -> bool {
403 self.0.strong_count() > 0
404 }
405}
406impl fmt::Debug for WeakContextInitHandle {
407 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
408 f.debug_tuple("WeakContextInitHandle")
409 .field(&std::sync::Weak::as_ptr(&self.0))
410 .finish()
411 }
412}
413impl PartialEq for WeakContextInitHandle {
414 fn eq(&self, other: &Self) -> bool {
415 std::sync::Weak::ptr_eq(&self.0, &other.0)
416 }
417}
418impl Eq for WeakContextInitHandle {}
419impl std::hash::Hash for WeakContextInitHandle {
420 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
421 let i = std::sync::Weak::as_ptr(&self.0) as usize;
422 std::hash::Hash::hash(&i, state)
423 }
424}