Skip to main content

windows_reactor/core/
component.rs

1use super::arena::NodeId;
2use super::runtime::WindowToken;
3use super::scope::{ScopeArena, ScopeError, ScopeId, ScopeState};
4use crate::element::{
5    Callback, CallbackSource, ColorScheme, IntoPayloadCallback, View, WindowSize, WindowVisuals,
6};
7use crate::reference::{HostRequest, WindowEndpoint, WindowRef};
8use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet};
9use std::any::{Any, TypeId};
10use std::cell::RefCell;
11use std::collections::VecDeque;
12use std::fmt;
13use std::marker::PhantomData;
14use std::mem::size_of;
15use std::rc::Rc;
16use std::sync::atomic::{AtomicU64, Ordering};
17use std::sync::{Arc, Mutex, Weak};
18
19pub(crate) const BACKGROUND_MESSAGE_QUEUE_CAPACITY: usize = 4_096;
20pub(crate) const BACKGROUND_TASK_CAPACITY: usize = 64;
21pub(crate) const LOCAL_MESSAGE_QUEUE_CAPACITY: usize = 4_096;
22/// Identifies an effect within one component publication.
23///
24/// Keys must be unique among the effects declared by one call to [`Component::view`].
25#[derive(Clone, Debug, Eq, Hash, PartialEq)]
26pub struct EffectKey(EffectKeyKind);
27
28#[derive(Clone, Debug, Eq, Hash, PartialEq)]
29enum EffectKeyKind {
30    Integer(u64),
31    String(Rc<str>),
32}
33
34impl From<u64> for EffectKey {
35    fn from(value: u64) -> Self {
36        Self(EffectKeyKind::Integer(value))
37    }
38}
39
40impl From<u32> for EffectKey {
41    fn from(value: u32) -> Self {
42        Self(EffectKeyKind::Integer(value.into()))
43    }
44}
45
46impl From<usize> for EffectKey {
47    fn from(value: usize) -> Self {
48        Self(EffectKeyKind::Integer(u64::try_from(value).unwrap()))
49    }
50}
51
52impl From<String> for EffectKey {
53    fn from(value: String) -> Self {
54        Self(EffectKeyKind::String(value.into()))
55    }
56}
57
58impl From<&str> for EffectKey {
59    fn from(value: &str) -> Self {
60        Self(EffectKeyKind::String(value.into()))
61    }
62}
63
64#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
65pub(crate) struct ContextId(u64);
66
67static NEXT_CONTEXT_ID: AtomicU64 = AtomicU64::new(1);
68/// A typed context channel with a fallback value.
69///
70/// Each value created by [`new`](Self::new) has its own identity, even when defaults are equal.
71#[derive(Clone, Debug)]
72pub struct Context<T> {
73    default: T,
74    id: ContextId,
75}
76
77impl<T> Context<T> {
78    /// Creates a context that returns `default` when no ancestor provides a value.
79    pub fn new(default: T) -> Self {
80        Self {
81            default,
82            id: ContextId(NEXT_CONTEXT_ID.fetch_add(1, Ordering::Relaxed)),
83        }
84    }
85
86    #[cfg(test)]
87    pub(crate) fn id(&self) -> ContextId {
88        self.id
89    }
90}
91
92impl<T> PartialEq for Context<T> {
93    fn eq(&self, other: &Self) -> bool {
94        self.id == other.id
95    }
96}
97
98impl<T> Eq for Context<T> {}
99
100#[derive(Clone)]
101pub(crate) struct ContextProvision {
102    pub(crate) id: ContextId,
103    pub(crate) value: Rc<dyn Any>,
104    value_type: TypeId,
105    equals: fn(&dyn Any, &dyn Any) -> bool,
106}
107
108impl ContextProvision {
109    pub(crate) fn new<T: Clone + PartialEq + 'static>(context: &Context<T>, value: T) -> Self {
110        Self {
111            id: context.id,
112            value: Rc::new(value),
113            value_type: TypeId::of::<T>(),
114            equals: |left, right| {
115                left.downcast_ref::<T>()
116                    .zip(right.downcast_ref::<T>())
117                    .is_some_and(|(left, right)| left == right)
118            },
119        }
120    }
121}
122
123impl fmt::Debug for ContextProvision {
124    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
125        formatter
126            .debug_struct("ContextProvision")
127            .field("id", &self.id)
128            .field("value_type", &self.value_type)
129            .finish()
130    }
131}
132
133impl PartialEq for ContextProvision {
134    fn eq(&self, other: &Self) -> bool {
135        self.id == other.id
136            && self.value_type == other.value_type
137            && (Rc::ptr_eq(&self.value, &other.value)
138                || (self.equals)(self.value.as_ref(), other.value.as_ref()))
139    }
140}
141
142#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
143pub(crate) struct ContextDependency {
144    pub(crate) id: ContextId,
145    pub(crate) provider: Option<NodeId>,
146}
147
148#[derive(Clone, Default)]
149pub(crate) enum ContextDependencies {
150    #[default]
151    Empty,
152    One(ContextDependency),
153    Many(HashSet<ContextDependency>),
154}
155
156impl ContextDependencies {
157    pub(crate) fn contains(&self, dependency: &ContextDependency) -> bool {
158        match self {
159            Self::Empty => false,
160            Self::One(value) => value == dependency,
161            Self::Many(values) => values.contains(dependency),
162        }
163    }
164
165    fn insert(&mut self, dependency: ContextDependency) {
166        match self {
167            Self::Empty => *self = Self::One(dependency),
168            Self::One(value) if *value == dependency => {}
169            Self::One(value) => {
170                let mut values = HashSet::default();
171                values.insert(*value);
172                values.insert(dependency);
173                *self = Self::Many(values);
174            }
175            Self::Many(values) => {
176                values.insert(dependency);
177            }
178        }
179    }
180
181    fn is_empty(&self) -> bool {
182        matches!(self, Self::Empty)
183    }
184
185    pub(crate) fn iter(&self) -> ContextDependencyIter<'_> {
186        match self {
187            Self::Empty => ContextDependencyIter::Empty,
188            Self::One(value) => ContextDependencyIter::One(Some(value)),
189            Self::Many(values) => ContextDependencyIter::Many(values.iter()),
190        }
191    }
192
193    fn len(&self) -> usize {
194        match self {
195            Self::Empty => 0,
196            Self::One(_) => 1,
197            Self::Many(values) => values.len(),
198        }
199    }
200}
201
202impl PartialEq for ContextDependencies {
203    fn eq(&self, other: &Self) -> bool {
204        self.len() == other.len() && self.iter().all(|value| other.contains(value))
205    }
206}
207
208impl Eq for ContextDependencies {}
209
210pub(crate) enum ContextDependencyIter<'a> {
211    Empty,
212    One(Option<&'a ContextDependency>),
213    Many(std::collections::hash_set::Iter<'a, ContextDependency>),
214}
215
216impl<'a> Iterator for ContextDependencyIter<'a> {
217    type Item = &'a ContextDependency;
218
219    fn next(&mut self) -> Option<Self::Item> {
220        match self {
221            Self::Empty => None,
222            Self::One(value) => value.take(),
223            Self::Many(values) => values.next(),
224        }
225    }
226}
227
228#[derive(Clone, Default)]
229enum ContextValues {
230    #[default]
231    Empty,
232    One {
233        id: ContextId,
234        provider: NodeId,
235        value_type: TypeId,
236        value: Rc<dyn Any>,
237    },
238    Many(HashMap<ContextId, (NodeId, TypeId, Rc<dyn Any>)>),
239}
240
241#[derive(Clone, Default)]
242pub(crate) struct ContextSnapshot {
243    values: ContextValues,
244}
245
246impl ContextSnapshot {
247    pub(crate) fn insert(&mut self, provider: NodeId, provision: &ContextProvision) {
248        match &mut self.values {
249            ContextValues::Empty => {
250                self.values = ContextValues::One {
251                    id: provision.id,
252                    provider,
253                    value_type: provision.value_type,
254                    value: Rc::clone(&provision.value),
255                };
256            }
257            ContextValues::One { id, .. } if *id == provision.id => {}
258            ContextValues::One { .. } => {
259                let ContextValues::One {
260                    id,
261                    provider: previous_provider,
262                    value_type,
263                    value,
264                } = std::mem::take(&mut self.values)
265                else {
266                    unreachable!()
267                };
268                let mut values = HashMap::default();
269                values.insert(id, (previous_provider, value_type, value));
270                values.insert(
271                    provision.id,
272                    (provider, provision.value_type, Rc::clone(&provision.value)),
273                );
274                self.values = ContextValues::Many(values);
275            }
276            ContextValues::Many(values) => {
277                values.entry(provision.id).or_insert_with(|| {
278                    (provider, provision.value_type, Rc::clone(&provision.value))
279                });
280            }
281        }
282    }
283
284    fn get<T: Clone + 'static>(&self, context: &Context<T>) -> Option<(NodeId, T)> {
285        let (provider, value_type, value) = match &self.values {
286            ContextValues::Empty => return None,
287            ContextValues::One {
288                id,
289                provider,
290                value_type,
291                value,
292            } if *id == context.id => (provider, value_type, value),
293            ContextValues::One { .. } => return None,
294            ContextValues::Many(values) => {
295                let (provider, value_type, value) = values.get(&context.id)?;
296                (provider, value_type, value)
297            }
298        };
299        assert_eq!(*value_type, TypeId::of::<T>(), "context type mismatch");
300        Some((*provider, value.downcast_ref::<T>().unwrap().clone()))
301    }
302}
303
304#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
305pub(crate) struct ComponentToken {
306    window: WindowToken,
307    scope: ScopeId,
308}
309
310impl ComponentToken {
311    pub(crate) fn scope(self) -> ScopeId {
312        self.scope
313    }
314}
315
316/// An invalid set of declarations made during [`Component::view`].
317#[derive(Clone, Debug, Eq, PartialEq)]
318pub enum ComponentDeclarationError {
319    /// The same effect key was declared more than once.
320    EffectKey(EffectKey),
321    /// More than one color-scheme observation was declared.
322    ColorSchemeObservation,
323    /// More than one window-size observation was declared.
324    WindowSizeObservation,
325    /// More than one window title was declared.
326    WindowTitle,
327    /// More than one set of window visuals was declared.
328    WindowVisuals,
329}
330
331struct MessageEnvelope {
332    control: Option<Arc<TaskControl>>,
333    token: ComponentToken,
334    payload: Box<dyn Any>,
335}
336
337struct BackgroundEnvelope {
338    control: Arc<TaskControl>,
339    delivery: BackgroundDelivery,
340    payload: Box<dyn Any + Send>,
341    rejection: Option<Box<dyn Any + Send>>,
342    token: ComponentToken,
343}
344
345#[derive(Clone, Copy, Eq, PartialEq)]
346enum BackgroundDelivery {
347    Completion,
348    Rejection,
349}
350
351impl BackgroundEnvelope {
352    fn reject(mut self) -> Option<Self> {
353        if self.delivery == BackgroundDelivery::Rejection {
354            return Some(self);
355        }
356        if !self.control.reject() {
357            return None;
358        }
359        self.payload = self.rejection.take()?;
360        self.delivery = BackgroundDelivery::Rejection;
361        Some(self)
362    }
363}
364
365enum PendingEnvelope {
366    Background(BackgroundEnvelope),
367    Local(MessageEnvelope),
368}
369
370impl PendingEnvelope {
371    fn control(&self) -> Option<&Arc<TaskControl>> {
372        match self {
373            Self::Background(envelope) => Some(&envelope.control),
374            Self::Local(envelope) => envelope.control.as_ref(),
375        }
376    }
377
378    fn token(&self) -> ComponentToken {
379        match self {
380            Self::Background(envelope) => envelope.token,
381            Self::Local(envelope) => envelope.token,
382        }
383    }
384}
385
386struct BackgroundQueue {
387    envelopes: VecDeque<BackgroundEnvelope>,
388    open: bool,
389    tasks: HashMap<ScopeId, Vec<Weak<TaskControl>>>,
390    wake: Option<Arc<dyn Fn() -> bool + Send + Sync>>,
391    wake_pending: bool,
392}
393
394#[derive(Clone)]
395struct TaskSpawner {
396    limiter: Arc<TaskLimiter>,
397    queue: Arc<Mutex<BackgroundQueue>>,
398    token: ComponentToken,
399}
400
401#[derive(Default)]
402struct TaskLimiter {
403    active: std::sync::atomic::AtomicUsize,
404}
405
406impl TaskLimiter {
407    fn acquire(self: &Arc<Self>) -> Option<TaskSlot> {
408        self.active
409            .try_update(Ordering::AcqRel, Ordering::Acquire, |active| {
410                (active < BACKGROUND_TASK_CAPACITY).then_some(active + 1)
411            })
412            .ok()
413            .map(|_| TaskSlot(Arc::clone(self)))
414    }
415}
416
417struct TaskSlot(Arc<TaskLimiter>);
418
419impl Drop for TaskSlot {
420    fn drop(&mut self) {
421        self.0.active.fetch_sub(1, Ordering::AcqRel);
422    }
423}
424
425/// A cooperative cancellation signal passed to background work.
426#[derive(Clone, Debug)]
427pub struct CancellationToken {
428    control: Arc<TaskControl>,
429}
430
431impl CancellationToken {
432    /// Returns whether the component scope or task has been cancelled.
433    pub fn is_cancelled(&self) -> bool {
434        self.control.status() == ComponentTaskStatus::Cancelled
435    }
436}
437
438/// The delivery state of a component background task.
439#[derive(Clone, Copy, Debug, Eq, PartialEq)]
440#[repr(u8)]
441pub enum ComponentTaskStatus {
442    /// The task is running on the thread pool.
443    Running,
444    /// Its result is waiting in the component message queue.
445    Queued,
446    /// Its result was delivered to the component.
447    Delivered,
448    /// Delivery was cancelled.
449    Cancelled,
450    /// Bounded scheduling or delivery rejected the result.
451    Rejected,
452}
453
454#[derive(Debug)]
455struct TaskControl {
456    status: std::sync::atomic::AtomicU8,
457}
458
459impl TaskControl {
460    fn new() -> Self {
461        Self {
462            status: std::sync::atomic::AtomicU8::new(ComponentTaskStatus::Running as u8),
463        }
464    }
465
466    fn queue(&self) -> bool {
467        self.status
468            .compare_exchange(
469                ComponentTaskStatus::Running as u8,
470                ComponentTaskStatus::Queued as u8,
471                Ordering::AcqRel,
472                Ordering::Acquire,
473            )
474            .is_ok()
475    }
476
477    fn deliver(&self) -> bool {
478        self.status
479            .compare_exchange(
480                ComponentTaskStatus::Queued as u8,
481                ComponentTaskStatus::Delivered as u8,
482                Ordering::AcqRel,
483                Ordering::Acquire,
484            )
485            .is_ok()
486    }
487
488    fn reject(&self) -> bool {
489        self.finish(ComponentTaskStatus::Rejected)
490    }
491
492    fn finish(&self, status: ComponentTaskStatus) -> bool {
493        let mut current = self.status.load(Ordering::Acquire);
494        while current == ComponentTaskStatus::Running as u8
495            || current == ComponentTaskStatus::Queued as u8
496        {
497            match self.status.compare_exchange_weak(
498                current,
499                status as u8,
500                Ordering::AcqRel,
501                Ordering::Acquire,
502            ) {
503                Ok(_) => return true,
504                Err(actual) => current = actual,
505            }
506        }
507        false
508    }
509
510    fn status(&self) -> ComponentTaskStatus {
511        match self.status.load(Ordering::Acquire) {
512            0 => ComponentTaskStatus::Running,
513            1 => ComponentTaskStatus::Queued,
514            2 => ComponentTaskStatus::Delivered,
515            3 => ComponentTaskStatus::Cancelled,
516            4 => ComponentTaskStatus::Rejected,
517            _ => unreachable!(),
518        }
519    }
520
521    fn cancel(&self) {
522        self.finish(ComponentTaskStatus::Cancelled);
523    }
524}
525
526/// A handle for observing or cancelling background work.
527///
528/// Dropping the handle does not cancel the task. Scope retirement, Pump shutdown, or an explicit
529/// [`cancel`](Self::cancel) call cancels it.
530#[derive(Clone)]
531pub struct ComponentTask {
532    control: Arc<TaskControl>,
533    queue: Arc<Mutex<BackgroundQueue>>,
534    token: ComponentToken,
535}
536
537impl ComponentTask {
538    /// Cancels delivery and removes a queued result when possible.
539    pub fn cancel(&self) {
540        self.control.cancel();
541        let mut queue = self.queue.lock().unwrap();
542        queue
543            .envelopes
544            .retain(|envelope| !Arc::ptr_eq(&envelope.control, &self.control));
545    }
546
547    /// Returns whether this task has been cancelled.
548    pub fn is_cancelled(&self) -> bool {
549        self.status() == ComponentTaskStatus::Cancelled
550    }
551
552    /// Returns the current scheduling and delivery state.
553    pub fn status(&self) -> ComponentTaskStatus {
554        self.control.status()
555    }
556}
557
558impl fmt::Debug for ComponentTask {
559    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
560        formatter
561            .debug_struct("ComponentTask")
562            .field("status", &self.status())
563            .field("token", &self.token)
564            .finish()
565    }
566}
567
568impl TaskSpawner {
569    fn spawn<M, F>(&self, work: F) -> ComponentTask
570    where
571        M: Send + 'static,
572        F: FnOnce(CancellationToken) -> M + Send + 'static,
573    {
574        self.spawn_inner(work, None)
575    }
576
577    fn spawn_with_rejection<M, F>(&self, work: F, rejection: M) -> ComponentTask
578    where
579        M: Send + 'static,
580        F: FnOnce(CancellationToken) -> M + Send + 'static,
581    {
582        self.spawn_inner(work, Some(Box::new(rejection)))
583    }
584
585    fn spawn_inner<M, F>(&self, work: F, rejection: Option<Box<dyn Any + Send>>) -> ComponentTask
586    where
587        M: Send + 'static,
588        F: FnOnce(CancellationToken) -> M + Send + 'static,
589    {
590        let control = Arc::new(TaskControl::new());
591        let rejection = Arc::new(Mutex::new(rejection));
592        let task = ComponentTask {
593            control: Arc::clone(&control),
594            queue: Arc::clone(&self.queue),
595            token: self.token,
596        };
597        let Some(slot) = self.limiter.acquire() else {
598            self.queue_rejection(&control, rejection.lock().unwrap().take());
599            return task;
600        };
601        {
602            let mut queue = self.queue.lock().unwrap();
603            if !queue.open {
604                control.reject();
605                return task;
606            }
607            queue
608                .tasks
609                .entry(self.token.scope)
610                .or_default()
611                .push(Arc::downgrade(&control));
612        }
613        let queue = Arc::clone(&self.queue);
614        let token = self.token;
615        let thread_control = Arc::clone(&control);
616        let thread_rejection = Arc::clone(&rejection);
617        windows_threading::submit(move || {
618            let _slot = slot;
619            let message = work(CancellationToken {
620                control: Arc::clone(&thread_control),
621            });
622            let wake = {
623                let mut background = queue.lock().unwrap();
624                let registered = background.tasks.get_mut(&token.scope).is_some_and(|tasks| {
625                    let before = tasks.len();
626                    tasks.retain(|task| {
627                        task.upgrade()
628                            .is_some_and(|task| !Arc::ptr_eq(&task, &thread_control))
629                    });
630                    tasks.len() != before
631                });
632                if background
633                    .tasks
634                    .get(&token.scope)
635                    .is_some_and(Vec::is_empty)
636                {
637                    background.tasks.remove(&token.scope);
638                }
639                if !registered
640                    || thread_control.status() == ComponentTaskStatus::Cancelled
641                    || !background.open
642                {
643                    thread_control.cancel();
644                    return;
645                }
646                if background.envelopes.len() >= BACKGROUND_MESSAGE_QUEUE_CAPACITY {
647                    drop(background);
648                    Self::queue_rejection_shared(
649                        &queue,
650                        &thread_control,
651                        token,
652                        thread_rejection.lock().unwrap().take(),
653                    );
654                    return;
655                }
656                if !thread_control.queue() {
657                    return;
658                }
659                background.envelopes.push_back(BackgroundEnvelope {
660                    control: Arc::clone(&thread_control),
661                    delivery: BackgroundDelivery::Completion,
662                    payload: Box::new(message),
663                    rejection: thread_rejection.lock().unwrap().take(),
664                    token,
665                });
666                Self::background_wake(&mut background)
667            };
668            Self::wake_or_reject(&queue, wake);
669        });
670        task
671    }
672
673    fn background_wake(queue: &mut BackgroundQueue) -> Option<Arc<dyn Fn() -> bool + Send + Sync>> {
674        let wake = (!queue.wake_pending).then(|| queue.wake.clone()).flatten();
675        queue.wake_pending |= wake.is_some();
676        wake
677    }
678
679    fn queue_rejection(&self, control: &Arc<TaskControl>, rejection: Option<Box<dyn Any + Send>>) {
680        Self::queue_rejection_shared(&self.queue, control, self.token, rejection);
681    }
682
683    fn queue_rejection_shared(
684        queue: &Arc<Mutex<BackgroundQueue>>,
685        control: &Arc<TaskControl>,
686        token: ComponentToken,
687        rejection: Option<Box<dyn Any + Send>>,
688    ) {
689        let Some(payload) = rejection else {
690            control.reject();
691            return;
692        };
693        let mut background = queue.lock().unwrap();
694        if !background.open {
695            control.reject();
696            return;
697        }
698        if !control.reject() {
699            return;
700        }
701        background.envelopes.push_back(BackgroundEnvelope {
702            control: Arc::clone(control),
703            delivery: BackgroundDelivery::Rejection,
704            payload,
705            rejection: None,
706            token,
707        });
708        let wake = Self::background_wake(&mut background);
709        drop(background);
710        Self::wake_or_reject(queue, wake);
711    }
712
713    fn wake_or_reject(
714        queue: &Arc<Mutex<BackgroundQueue>>,
715        wake: Option<Arc<dyn Fn() -> bool + Send + Sync>>,
716    ) {
717        if wake.is_some_and(|wake| !wake()) {
718            let mut queue = queue.lock().unwrap();
719            queue.wake_pending = false;
720            queue.envelopes = queue
721                .envelopes
722                .drain(..)
723                .filter_map(BackgroundEnvelope::reject)
724                .collect();
725        }
726    }
727}
728
729struct ComponentQueue {
730    active: HashSet<ScopeId>,
731    envelopes: VecDeque<MessageEnvelope>,
732    open: bool,
733    wake: Option<Rc<dyn Fn()>>,
734}
735
736/// Sends typed messages to a component's queued update loop.
737///
738/// Delivery never calls [`Component::update`] inline. A send returns `false` when the component
739/// is retired, the queue is closed, or the bounded local queue is full.
740pub struct LocalSender<M> {
741    queue: Rc<RefCell<ComponentQueue>>,
742    token: ComponentToken,
743    marker: PhantomData<fn(M)>,
744}
745
746impl<M> Clone for LocalSender<M> {
747    fn clone(&self) -> Self {
748        Self {
749            queue: Rc::clone(&self.queue),
750            token: self.token,
751            marker: PhantomData,
752        }
753    }
754}
755
756impl<M: 'static> LocalSender<M> {
757    /// Queues a message for later delivery.
758    pub fn send(&self, message: M) -> bool {
759        let wake = {
760            let mut queue = self.queue.borrow_mut();
761            if !queue.open
762                || !queue.active.contains(&self.token.scope)
763                || queue.envelopes.len() >= LOCAL_MESSAGE_QUEUE_CAPACITY
764            {
765                return false;
766            }
767            let wake = queue
768                .envelopes
769                .is_empty()
770                .then(|| queue.wake.clone())
771                .flatten();
772            queue.envelopes.push_back(MessageEnvelope {
773                control: None,
774                token: self.token,
775                payload: Box::new(message),
776            });
777            wake
778        };
779        if let Some(wake) = wake {
780            wake();
781        }
782        true
783    }
784
785    /// Adapts values into queued component messages.
786    ///
787    /// Captureless mapper functions retain callback identity across publications. Capturing
788    /// closures have identity only through clones of the returned callback.
789    pub fn callback<T, F>(&self, map: F) -> Callback<T>
790    where
791        F: Fn(T) -> M + 'static,
792    {
793        let sender = self.clone();
794        if size_of::<F>() == 0 {
795            let source = CallbackSource::new(Rc::as_ptr(&self.queue) as usize, self.token);
796            Callback::new_identified(source, TypeId::of::<F>(), move |value| {
797                sender.send(map(value))
798            })
799        } else {
800            Callback::new_with_acceptance(move |value| sender.send(map(value)))
801        }
802    }
803
804    /// Creates a callback that queues a clone of `message` each time it is called.
805    pub fn message(&self, message: M) -> Callback<()>
806    where
807        M: Clone,
808    {
809        self.callback(move |()| message.clone())
810    }
811}
812
813/// Capabilities available while creating or updating a component.
814///
815/// Messages sent through this context are queued and delivered after the current lifecycle call.
816pub struct ComponentContext<C: Component> {
817    sender: LocalSender<C::Message>,
818    tasks: TaskSpawner,
819    window: WindowRef,
820}
821
822impl<C: Component> ComponentContext<C> {
823    /// Requests a new application-owned window with an independent Pump.
824    #[must_use = "false means there is no active component publication"]
825    pub fn open_window(&self, root: View) -> bool {
826        self.window.request_open(root)
827    }
828
829    /// Returns a sender bound to this component instance.
830    pub fn sender(&self) -> LocalSender<C::Message> {
831        self.sender.clone()
832    }
833
834    /// Starts scope-owned work on the Windows thread pool.
835    ///
836    /// Expected application failures should be represented in the returned message. A panic in
837    /// `work` or failure to submit to the Windows thread pool is fatal.
838    pub fn spawn_background<F>(&self, work: F) -> ComponentTask
839    where
840        C::Message: Send,
841        F: FnOnce(CancellationToken) -> C::Message + Send + 'static,
842    {
843        self.tasks.spawn(work)
844    }
845
846    /// Starts scope-owned work and dispatches `rejected` if bounded delivery rejects it.
847    ///
848    /// Cancellation and scope retirement do not dispatch `rejected`. A panic in `work` or failure
849    /// to submit to the Windows thread pool is fatal.
850    pub fn spawn_background_with_rejection<F>(&self, work: F, rejected: C::Message) -> ComponentTask
851    where
852        C::Message: Send,
853        F: FnOnce(CancellationToken) -> C::Message + Send + 'static,
854    {
855        self.tasks.spawn_with_rejection(work, rejected)
856    }
857
858    /// Returns a token-bound capability for the component's owning window.
859    pub fn window(&self) -> WindowRef {
860        self.window.clone()
861    }
862}
863
864#[derive(Default)]
865enum SingleDeclaration<T> {
866    #[default]
867    Empty,
868    Value(T),
869    Duplicate,
870}
871
872impl<T> SingleDeclaration<T> {
873    fn declare(&mut self, value: T) {
874        *self = if matches!(self, Self::Empty) {
875            Self::Value(value)
876        } else {
877            Self::Duplicate
878        };
879    }
880
881    fn resolve<E>(self, duplicate: E) -> Result<Option<T>, E> {
882        match self {
883            Self::Empty => Ok(None),
884            Self::Value(value) => Ok(Some(value)),
885            Self::Duplicate => Err(duplicate),
886        }
887    }
888}
889
890/// Records view-time declarations for a component publication.
891///
892/// Context reads subscribe the component to the resolved provider. Callbacks queue messages rather
893/// than updating the component during view construction.
894pub struct ViewContext<C: Component> {
895    contexts: ContextSnapshot,
896    effects: ComponentEffects,
897    reads: ContextDependencies,
898    sender: LocalSender<C::Message>,
899    color_scheme_observation: SingleDeclaration<Callback<ColorScheme>>,
900    window_size_observation: SingleDeclaration<Callback<WindowSize>>,
901    window_title: SingleDeclaration<String>,
902    window_visuals: SingleDeclaration<WindowVisuals>,
903}
904
905impl<C: Component> ViewContext<C> {
906    /// Declares a queued color-scheme observation for the owning window.
907    pub fn on_color_scheme(&mut self, callback: impl IntoPayloadCallback<ColorScheme>) {
908        self.color_scheme_observation
909            .declare(callback.into_payload_callback());
910    }
911
912    /// Declares a queued client-size observation for the owning window.
913    pub fn on_window_size(&mut self, callback: impl IntoPayloadCallback<WindowSize>) {
914        self.window_size_observation
915            .declare(callback.into_payload_callback());
916    }
917
918    /// Declares the owning window's title for this component publication.
919    pub fn window_title(&mut self, title: impl Into<String>) {
920        self.window_title.declare(title.into());
921    }
922
923    /// Declares the owning window's visual environment for this publication.
924    pub fn window_visuals(&mut self, visuals: WindowVisuals) {
925        self.window_visuals.declare(visuals);
926    }
927
928    /// Returns a sender bound to this component instance.
929    pub fn sender(&self) -> LocalSender<C::Message> {
930        self.sender.clone()
931    }
932
933    /// Creates a callback that maps its argument to a queued component message.
934    pub fn callback<T>(&self, map: impl Fn(T) -> C::Message + 'static) -> Callback<T> {
935        self.sender.callback(map)
936    }
937
938    /// Creates a callback that forwards its argument as a queued component message.
939    pub fn forward(&self) -> Callback<C::Message> {
940        self.sender.callback(std::convert::identity)
941    }
942
943    /// Creates a callback that queues a clone of `message`.
944    pub fn message(&self, message: C::Message) -> Callback<()>
945    where
946        C::Message: Clone,
947    {
948        self.sender.message(message)
949    }
950
951    /// Reads the nearest provided value, or the context's fallback when no provider exists.
952    pub fn use_context<T: Clone + 'static>(&mut self, context: &Context<T>) -> T {
953        let resolved = self.contexts.get(context);
954        self.reads.insert(ContextDependency {
955            id: context.id,
956            provider: resolved.as_ref().map(|(provider, _)| *provider),
957        });
958        resolved.map_or_else(|| context.default.clone(), |(_, value)| value)
959    }
960
961    /// Declares an effect identified by `key` and `dependency`.
962    ///
963    /// The setup runs after a successful publication when the dependency is new or changed. Its
964    /// cleanup runs before replacement, when the effect is omitted, or when the component is
965    /// removed. Cleanup functions run in reverse order when the component is removed.
966    pub fn use_effect<D>(
967        &mut self,
968        key: impl Into<EffectKey>,
969        dependency: D,
970        setup: impl FnOnce() -> Option<Box<dyn FnOnce()>> + 'static,
971    ) where
972        D: PartialEq + 'static,
973    {
974        self.effects.use_effect(key.into(), dependency, setup);
975    }
976}
977
978/// A stateful unit that turns input and queued messages into a [`View`].
979///
980/// Reactor calls [`create`](Self::create) once, [`input_changed`](Self::input_changed) when unequal
981/// input is applied, and [`update`](Self::update) for accepted queued messages. It then calls
982/// [`view`](Self::view) to publish the current tree and view-time declarations.
983pub trait Component: Sized + 'static {
984    /// Immutable parent-supplied data used to reconcile the component.
985    type Input: Clone + PartialEq + 'static;
986    /// A message delivered to [`update`](Self::update).
987    type Message: 'static;
988
989    /// Creates component state from its initial input.
990    fn create(input: &Self::Input, context: &ComponentContext<Self>) -> Self;
991    /// Responds to a new input value that differs from the current input.
992    fn input_changed(&mut self, _input: &Self::Input, _context: &ComponentContext<Self>) {}
993    /// Handles one queued message.
994    fn update(&mut self, _message: Self::Message, _context: &ComponentContext<Self>) {}
995    /// Builds the current view and records context, effect, and window declarations.
996    fn view(&self, input: &Self::Input, context: &mut ViewContext<Self>) -> View;
997}
998
999trait ErasedComponentFactory {
1000    fn apply_input(&self, store: &mut ComponentStore, token: ComponentToken) -> bool;
1001    fn as_any(&self) -> &dyn Any;
1002    fn component_type(&self) -> TypeId;
1003    fn equals(&self, other: &dyn ErasedComponentFactory) -> bool;
1004    fn reserve(&self, store: &mut ComponentStore) -> ComponentToken;
1005    fn type_name(&self) -> &'static str;
1006}
1007
1008struct TypedComponentFactory<C: Component> {
1009    input: C::Input,
1010}
1011
1012impl<C: Component> ErasedComponentFactory for TypedComponentFactory<C> {
1013    fn apply_input(&self, store: &mut ComponentStore, token: ComponentToken) -> bool {
1014        store.apply_input(token, &self.input)
1015    }
1016
1017    fn as_any(&self) -> &dyn Any {
1018        self
1019    }
1020
1021    fn component_type(&self) -> TypeId {
1022        TypeId::of::<C>()
1023    }
1024
1025    fn equals(&self, other: &dyn ErasedComponentFactory) -> bool {
1026        other.component_type() == TypeId::of::<C>()
1027            && other
1028                .as_any()
1029                .downcast_ref::<Self>()
1030                .is_some_and(|other| self.input == other.input)
1031    }
1032
1033    fn reserve(&self, store: &mut ComponentStore) -> ComponentToken {
1034        store.reserve_component::<C>(self.input.clone())
1035    }
1036
1037    fn type_name(&self) -> &'static str {
1038        std::any::type_name::<C>()
1039    }
1040}
1041
1042#[derive(Clone)]
1043pub(crate) struct ComponentView {
1044    factory: Rc<dyn ErasedComponentFactory>,
1045}
1046
1047impl ComponentView {
1048    pub(crate) fn new<C: Component>(input: C::Input) -> Self {
1049        Self {
1050            factory: Rc::new(TypedComponentFactory::<C> { input }),
1051        }
1052    }
1053
1054    pub(crate) fn component_type(&self) -> TypeId {
1055        self.factory.component_type()
1056    }
1057
1058    pub(crate) fn apply_input(&self, store: &mut ComponentStore, token: ComponentToken) -> bool {
1059        self.factory.apply_input(store, token)
1060    }
1061
1062    pub(crate) fn reserve(&self, store: &mut ComponentStore) -> ComponentToken {
1063        self.factory.reserve(store)
1064    }
1065}
1066
1067impl fmt::Debug for ComponentView {
1068    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1069        formatter
1070            .debug_tuple("Component")
1071            .field(&self.factory.type_name())
1072            .finish()
1073    }
1074}
1075
1076impl PartialEq for ComponentView {
1077    fn eq(&self, other: &Self) -> bool {
1078        self.factory.equals(&*other.factory)
1079    }
1080}
1081
1082trait ErasedScope {
1083    fn apply_input(&mut self, input: &dyn Any, tasks: TaskSpawner) -> bool;
1084    #[cfg(test)]
1085    fn component(&self) -> &dyn Any;
1086    fn dispatch(&mut self, message: Box<dyn Any>, tasks: TaskSpawner);
1087    #[cfg(test)]
1088    fn message_type(&self) -> TypeId;
1089    fn input_type(&self) -> TypeId;
1090    fn type_name(&self) -> &'static str;
1091    fn context_dependencies(&self) -> Option<&ContextDependencies>;
1092    fn set_context_dependencies(&mut self, dependencies: ContextDependencies);
1093    fn view(&self, contexts: ContextSnapshot)
1094    -> Result<ComponentRender, ComponentDeclarationError>;
1095    fn cleanup_effects(&self);
1096    fn commit_effects(&self);
1097    fn prepare_effects(&self);
1098}
1099
1100pub(crate) struct ComponentRender {
1101    pub(crate) color_scheme_observation: Option<Callback<ColorScheme>>,
1102    pub(crate) dependencies: ContextDependencies,
1103    pub(crate) view: View,
1104    pub(crate) window_size_observation: Option<Callback<WindowSize>>,
1105    pub(crate) window_title: Option<String>,
1106    pub(crate) window_visuals: Option<WindowVisuals>,
1107}
1108
1109// Boxing the successful render would add an allocation to every component view.
1110#[allow(clippy::large_enum_variant)]
1111enum ComponentViewOutcome {
1112    Complete(Result<ComponentRender, ComponentDeclarationError>),
1113    Panicked(Box<dyn Any + Send>),
1114}
1115
1116type EffectCleanup = Box<dyn FnOnce()>;
1117type EffectSetup = Box<dyn FnOnce() -> Option<EffectCleanup>>;
1118
1119struct EffectSlot {
1120    cleanup: Option<EffectCleanup>,
1121    dependency: Box<dyn Any>,
1122    key: EffectKey,
1123}
1124
1125enum EffectRegistration {
1126    Retain {
1127        key: EffectKey,
1128    },
1129    Replace {
1130        dependency: Box<dyn Any>,
1131        key: EffectKey,
1132        setup: EffectSetup,
1133    },
1134}
1135
1136impl EffectRegistration {
1137    fn key(&self) -> &EffectKey {
1138        match self {
1139            Self::Retain { key } | Self::Replace { key, .. } => key,
1140        }
1141    }
1142}
1143
1144#[derive(Default)]
1145struct ComponentEffectState {
1146    registrations: Vec<EffectRegistration>,
1147    slots: Vec<EffectSlot>,
1148}
1149
1150impl ComponentEffectState {
1151    fn begin_view(&mut self) {
1152        self.registrations.clear();
1153    }
1154
1155    fn duplicate_key(&self) -> Option<&EffectKey> {
1156        self.registrations
1157            .windows(2)
1158            .next_back()
1159            .filter(|pair| pair[0].key() == pair[1].key())
1160            .map(|pair| pair[0].key())
1161    }
1162
1163    fn use_effect<D>(
1164        &mut self,
1165        key: EffectKey,
1166        dependency: D,
1167        setup: impl FnOnce() -> Option<EffectCleanup> + 'static,
1168    ) where
1169        D: PartialEq + 'static,
1170    {
1171        if self.duplicate_key().is_some() {
1172            return;
1173        }
1174        if self
1175            .registrations
1176            .iter()
1177            .any(|registration| registration.key() == &key)
1178        {
1179            // A repeated tail pair records the first duplicate without enlarging every state.
1180            self.registrations
1181                .push(EffectRegistration::Retain { key: key.clone() });
1182            self.registrations.push(EffectRegistration::Retain { key });
1183            return;
1184        }
1185        let changed = self
1186            .slots
1187            .iter()
1188            .find(|slot| slot.key == key)
1189            .and_then(|slot| slot.dependency.downcast_ref::<D>())
1190            != Some(&dependency);
1191        self.registrations.push(if changed {
1192            EffectRegistration::Replace {
1193                dependency: Box::new(dependency),
1194                key,
1195                setup: Box::new(setup),
1196            }
1197        } else {
1198            EffectRegistration::Retain { key }
1199        });
1200    }
1201
1202    fn finish_view(&self) -> Result<(), ComponentDeclarationError> {
1203        if let Some(key) = self.duplicate_key() {
1204            Err(ComponentDeclarationError::EffectKey(key.clone()))
1205        } else {
1206            Ok(())
1207        }
1208    }
1209
1210    fn prepare(&mut self) {
1211        for slot in &mut self.slots {
1212            let cleanup_required = self
1213                .registrations
1214                .iter()
1215                .find(|registration| registration.key() == &slot.key)
1216                .is_none_or(|registration| {
1217                    matches!(registration, EffectRegistration::Replace { .. })
1218                });
1219            if cleanup_required && let Some(cleanup) = slot.cleanup.take() {
1220                cleanup();
1221            }
1222        }
1223    }
1224
1225    fn commit(&mut self) {
1226        debug_assert!(self.duplicate_key().is_none());
1227        let mut published = std::mem::take(&mut self.slots);
1228        for registration in self.registrations.drain(..) {
1229            let slot = match registration {
1230                EffectRegistration::Replace {
1231                    dependency,
1232                    key,
1233                    setup,
1234                } => EffectSlot {
1235                    cleanup: setup(),
1236                    dependency,
1237                    key,
1238                },
1239                EffectRegistration::Retain { key } => {
1240                    let index = published.iter().position(|slot| slot.key == key).unwrap();
1241                    published.remove(index)
1242                }
1243            };
1244            self.slots.push(slot);
1245        }
1246    }
1247
1248    fn cleanup(&mut self) {
1249        for slot in self.slots.iter_mut().rev() {
1250            if let Some(cleanup) = slot.cleanup.take() {
1251                cleanup();
1252            }
1253        }
1254        self.slots.clear();
1255        self.registrations.clear();
1256    }
1257
1258    fn is_empty(&self) -> bool {
1259        self.registrations.is_empty() && self.slots.is_empty()
1260    }
1261}
1262
1263#[derive(Default)]
1264pub(crate) struct ComponentEffects(Option<Box<ComponentEffectState>>);
1265
1266impl ComponentEffects {
1267    fn begin_view(&mut self) {
1268        if let Some(state) = self.0.as_deref_mut() {
1269            state.begin_view();
1270        }
1271    }
1272
1273    fn use_effect<D>(
1274        &mut self,
1275        key: EffectKey,
1276        dependency: D,
1277        setup: impl FnOnce() -> Option<EffectCleanup> + 'static,
1278    ) where
1279        D: PartialEq + 'static,
1280    {
1281        self.0
1282            .get_or_insert_with(Box::default)
1283            .use_effect(key, dependency, setup);
1284    }
1285
1286    fn finish_view(&self) -> Result<(), ComponentDeclarationError> {
1287        self.0
1288            .as_deref()
1289            .map_or(Ok(()), ComponentEffectState::finish_view)
1290    }
1291
1292    fn prepare(&mut self) {
1293        if let Some(state) = self.0.as_deref_mut() {
1294            state.prepare();
1295        }
1296    }
1297
1298    fn commit(&mut self) {
1299        let clear = self.0.as_deref_mut().is_some_and(|state| {
1300            state.commit();
1301            state.is_empty()
1302        });
1303        if clear {
1304            self.0 = None;
1305        }
1306    }
1307
1308    fn cleanup(&mut self) {
1309        if let Some(state) = self.0.as_deref_mut() {
1310            state.cleanup();
1311        }
1312        self.0 = None;
1313    }
1314}
1315
1316struct TypedScope<C, I, M> {
1317    component: C,
1318    context_dependencies: Option<Rc<ContextDependencies>>,
1319    effects: RefCell<ComponentEffects>,
1320    input: I,
1321    input_changed: fn(&mut C, &I, LocalSender<M>, TaskSpawner, WindowRef),
1322    sender: LocalSender<M>,
1323    update: fn(&mut C, M, LocalSender<M>, TaskSpawner, WindowRef),
1324    view: fn(
1325        &C,
1326        &I,
1327        LocalSender<M>,
1328        ComponentEffects,
1329        ContextSnapshot,
1330    ) -> (ComponentViewOutcome, ComponentEffects),
1331    window: WindowEndpoint,
1332}
1333
1334impl<C, I, M> Drop for TypedScope<C, I, M> {
1335    fn drop(&mut self) {
1336        self.effects.borrow_mut().cleanup();
1337    }
1338}
1339
1340impl<C, I, M> ErasedScope for TypedScope<C, I, M>
1341where
1342    C: 'static,
1343    I: Clone + PartialEq + 'static,
1344    M: 'static,
1345{
1346    fn apply_input(&mut self, input: &dyn Any, tasks: TaskSpawner) -> bool {
1347        let input = input.downcast_ref::<I>().unwrap();
1348        if self.input == *input {
1349            return false;
1350        }
1351        self.input = input.clone();
1352        self.window.begin();
1353        (self.input_changed)(
1354            &mut self.component,
1355            &self.input,
1356            self.sender.clone(),
1357            tasks,
1358            self.window.reference(),
1359        );
1360        self.window.finish();
1361        true
1362    }
1363
1364    #[cfg(test)]
1365    fn component(&self) -> &dyn Any {
1366        &self.component
1367    }
1368
1369    fn context_dependencies(&self) -> Option<&ContextDependencies> {
1370        self.context_dependencies.as_deref()
1371    }
1372
1373    fn set_context_dependencies(&mut self, dependencies: ContextDependencies) {
1374        self.context_dependencies = (!dependencies.is_empty()).then(|| Rc::new(dependencies));
1375    }
1376
1377    fn dispatch(&mut self, message: Box<dyn Any>, tasks: TaskSpawner) {
1378        let message = message.downcast::<M>().unwrap();
1379        self.window.begin();
1380        (self.update)(
1381            &mut self.component,
1382            *message,
1383            self.sender.clone(),
1384            tasks,
1385            self.window.reference(),
1386        );
1387        self.window.finish();
1388    }
1389
1390    #[cfg(test)]
1391    fn message_type(&self) -> TypeId {
1392        TypeId::of::<M>()
1393    }
1394
1395    fn input_type(&self) -> TypeId {
1396        TypeId::of::<I>()
1397    }
1398
1399    fn type_name(&self) -> &'static str {
1400        std::any::type_name::<C>()
1401    }
1402
1403    fn view(
1404        &self,
1405        contexts: ContextSnapshot,
1406    ) -> Result<ComponentRender, ComponentDeclarationError> {
1407        let mut effects = self.effects.take();
1408        effects.begin_view();
1409        let (outcome, effects) = (self.view)(
1410            &self.component,
1411            &self.input,
1412            self.sender.clone(),
1413            effects,
1414            contexts,
1415        );
1416        self.effects.replace(effects);
1417        match outcome {
1418            ComponentViewOutcome::Complete(render) => render,
1419            ComponentViewOutcome::Panicked(payload) => std::panic::resume_unwind(payload),
1420        }
1421    }
1422
1423    fn cleanup_effects(&self) {
1424        self.effects.borrow_mut().cleanup();
1425    }
1426
1427    fn commit_effects(&self) {
1428        self.effects.borrow_mut().commit();
1429    }
1430
1431    fn prepare_effects(&self) {
1432        self.effects.borrow_mut().prepare();
1433    }
1434}
1435
1436#[derive(Clone, Debug, Default, Eq, PartialEq)]
1437pub struct DrainReport {
1438    pub blocked: bool,
1439    pub dispatched: usize,
1440    pub dropped: usize,
1441    pub(crate) dirty: Vec<ComponentToken>,
1442}
1443
1444pub struct ComponentStore {
1445    background: Arc<Mutex<BackgroundQueue>>,
1446    context_consumers: HashMap<ContextDependency, HashSet<ScopeId>>,
1447    context_consumers_by_id: HashMap<ContextId, HashSet<ScopeId>>,
1448    drain_background_next: bool,
1449    window: WindowToken,
1450    scopes: ScopeArena<Box<dyn ErasedScope>>,
1451    task_limiter: Arc<TaskLimiter>,
1452    queue: Rc<RefCell<ComponentQueue>>,
1453    window_endpoint: WindowEndpoint,
1454}
1455
1456impl ComponentStore {
1457    pub fn new(window: WindowToken) -> Self {
1458        Self::with_task_limiter(window, Arc::new(TaskLimiter::default()))
1459    }
1460
1461    fn with_task_limiter(window: WindowToken, task_limiter: Arc<TaskLimiter>) -> Self {
1462        Self {
1463            background: Arc::new(Mutex::new(BackgroundQueue {
1464                envelopes: VecDeque::new(),
1465                open: true,
1466                tasks: HashMap::default(),
1467                wake: None,
1468                wake_pending: false,
1469            })),
1470            context_consumers: HashMap::default(),
1471            context_consumers_by_id: HashMap::default(),
1472            drain_background_next: false,
1473            window,
1474            scopes: ScopeArena::new(),
1475            task_limiter,
1476            queue: Rc::new(RefCell::new(ComponentQueue {
1477                active: HashSet::default(),
1478                envelopes: VecDeque::new(),
1479                open: true,
1480                wake: None,
1481            })),
1482            window_endpoint: WindowEndpoint::new(window),
1483        }
1484    }
1485
1486    pub fn reserve_component<C: Component>(&mut self, input: C::Input) -> ComponentToken {
1487        fn input_changed<C: Component>(
1488            component: &mut C,
1489            input: &C::Input,
1490            sender: LocalSender<C::Message>,
1491            tasks: TaskSpawner,
1492            window: WindowRef,
1493        ) {
1494            component.input_changed(
1495                input,
1496                &ComponentContext {
1497                    sender,
1498                    tasks,
1499                    window,
1500                },
1501            );
1502        }
1503
1504        fn update<C: Component>(
1505            component: &mut C,
1506            message: C::Message,
1507            sender: LocalSender<C::Message>,
1508            tasks: TaskSpawner,
1509            window: WindowRef,
1510        ) {
1511            component.update(
1512                message,
1513                &ComponentContext {
1514                    sender,
1515                    tasks,
1516                    window,
1517                },
1518            );
1519        }
1520
1521        fn view<C: Component>(
1522            component: &C,
1523            input: &C::Input,
1524            sender: LocalSender<C::Message>,
1525            effects: ComponentEffects,
1526            contexts: ContextSnapshot,
1527        ) -> (ComponentViewOutcome, ComponentEffects) {
1528            let mut context = ViewContext {
1529                contexts,
1530                effects,
1531                reads: ContextDependencies::default(),
1532                sender,
1533                color_scheme_observation: SingleDeclaration::default(),
1534                window_size_observation: SingleDeclaration::default(),
1535                window_title: SingleDeclaration::default(),
1536                window_visuals: SingleDeclaration::default(),
1537            };
1538            let view = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
1539                component.view(input, &mut context)
1540            }));
1541            let view = match view {
1542                Ok(view) => view,
1543                Err(payload) => {
1544                    return (ComponentViewOutcome::Panicked(payload), context.effects);
1545                }
1546            };
1547            let ViewContext {
1548                effects,
1549                reads: dependencies,
1550                color_scheme_observation,
1551                window_size_observation,
1552                window_title,
1553                window_visuals,
1554                ..
1555            } = context;
1556            let render = (|| {
1557                let color_scheme_observation = color_scheme_observation
1558                    .resolve(ComponentDeclarationError::ColorSchemeObservation)?;
1559                let window_size_observation = window_size_observation
1560                    .resolve(ComponentDeclarationError::WindowSizeObservation)?;
1561                let window_title = window_title.resolve(ComponentDeclarationError::WindowTitle)?;
1562                let window_visuals =
1563                    window_visuals.resolve(ComponentDeclarationError::WindowVisuals)?;
1564                effects.finish_view()?;
1565                Ok(ComponentRender {
1566                    color_scheme_observation,
1567                    dependencies,
1568                    view,
1569                    window_size_observation,
1570                    window_title,
1571                    window_visuals,
1572                })
1573            })();
1574            (ComponentViewOutcome::Complete(render), effects)
1575        }
1576
1577        let background = Arc::clone(&self.background);
1578        let queue = Rc::clone(&self.queue);
1579        let task_limiter = Arc::clone(&self.task_limiter);
1580        let window = self.window;
1581        let window_endpoint = self.window_endpoint.clone();
1582        let scope = self.scopes.reserve_with(move |scope| {
1583            queue.borrow_mut().active.insert(scope);
1584            let sender = LocalSender {
1585                queue: Rc::clone(&queue),
1586                token: ComponentToken { window, scope },
1587                marker: PhantomData,
1588            };
1589            let tasks = TaskSpawner {
1590                limiter: Arc::clone(&task_limiter),
1591                queue: Arc::clone(&background),
1592                token: ComponentToken { window, scope },
1593            };
1594            window_endpoint.begin();
1595            let component = C::create(
1596                &input,
1597                &ComponentContext {
1598                    sender: sender.clone(),
1599                    tasks,
1600                    window: window_endpoint.reference(),
1601                },
1602            );
1603            window_endpoint.finish();
1604            Box::new(TypedScope {
1605                component,
1606                context_dependencies: None,
1607                effects: RefCell::default(),
1608                input,
1609                input_changed: input_changed::<C>,
1610                sender,
1611                update: update::<C>,
1612                view: view::<C>,
1613                window: window_endpoint,
1614            }) as Box<dyn ErasedScope>
1615        });
1616        ComponentToken {
1617            window: self.window,
1618            scope,
1619        }
1620    }
1621
1622    pub fn publish(&mut self, token: ComponentToken) {
1623        self.validate_window(token);
1624        self.scopes.publish(token.scope).unwrap();
1625    }
1626
1627    pub(crate) fn restarted(&self, window: WindowToken) -> Self {
1628        Self::with_task_limiter(window, Arc::clone(&self.task_limiter))
1629    }
1630
1631    pub(crate) fn take_host_requests(&self) -> Vec<HostRequest> {
1632        self.window_endpoint.take_requests()
1633    }
1634
1635    pub(crate) fn commit_window_close(&self) {
1636        self.window_endpoint.commit_close();
1637    }
1638
1639    pub fn remove(&mut self, token: ComponentToken) {
1640        self.validate_window(token);
1641        self.clear_context_dependencies(token.scope);
1642        self.scopes.remove(token.scope).unwrap();
1643        self.cancel_scope_tasks(token.scope);
1644        self.remove_scope_messages(token.scope);
1645    }
1646
1647    #[cfg(test)]
1648    pub fn sender<M: 'static>(&self, token: ComponentToken) -> LocalSender<M> {
1649        self.validate_window(token);
1650        let scope = self.scopes.get(token.scope).unwrap();
1651        let actual = TypeId::of::<M>();
1652        let expected = scope.message_type();
1653        assert_eq!(actual, expected);
1654        LocalSender {
1655            queue: Rc::clone(&self.queue),
1656            token,
1657            marker: PhantomData,
1658        }
1659    }
1660
1661    pub fn apply_input<I: 'static>(&mut self, token: ComponentToken, input: &I) -> bool {
1662        self.validate_window(token);
1663        let tasks = self.task_spawner(token);
1664        let scope = self.scopes.get_mut(token.scope).unwrap();
1665        let actual = TypeId::of::<I>();
1666        let expected = scope.input_type();
1667        assert_eq!(actual, expected);
1668        scope.apply_input(input, tasks)
1669    }
1670
1671    #[cfg(test)]
1672    pub fn component<C: 'static>(&self, token: ComponentToken) -> &C {
1673        self.validate_window(token);
1674        let component = self.scopes.get(token.scope).unwrap().component();
1675        component.downcast_ref().unwrap()
1676    }
1677
1678    pub fn drain(&mut self, budget: usize) -> DrainReport {
1679        let mut report = DrainReport::default();
1680        self.background.lock().unwrap().wake_pending = false;
1681        for _ in 0..budget {
1682            let pop_background = || {
1683                self.background
1684                    .lock()
1685                    .unwrap()
1686                    .envelopes
1687                    .pop_front()
1688                    .map(PendingEnvelope::Background)
1689            };
1690            let pop_local = || {
1691                self.queue
1692                    .borrow_mut()
1693                    .envelopes
1694                    .pop_front()
1695                    .map(PendingEnvelope::Local)
1696            };
1697            let envelope = if self.drain_background_next {
1698                pop_background().or_else(pop_local)
1699            } else {
1700                pop_local().or_else(pop_background)
1701            };
1702            let Some(envelope) = envelope else {
1703                break;
1704            };
1705            let from_background = matches!(envelope, PendingEnvelope::Background(_));
1706            self.drain_background_next = !from_background;
1707            let token = envelope.token();
1708            if token.window != self.window {
1709                if let Some(control) = envelope.control() {
1710                    control.cancel();
1711                }
1712                report.dropped += 1;
1713                continue;
1714            }
1715            let state = match self.scopes.state(token.scope) {
1716                Ok(state) => state,
1717                Err(ScopeError::Stale(_)) => {
1718                    if let Some(control) = envelope.control() {
1719                        control.cancel();
1720                    }
1721                    report.dropped += 1;
1722                    continue;
1723                }
1724                Err(ScopeError::InvalidTransition(_, _)) => unreachable!(),
1725            };
1726            match state {
1727                ScopeState::Reserved => {
1728                    match envelope {
1729                        PendingEnvelope::Background(envelope) => self
1730                            .background
1731                            .lock()
1732                            .unwrap()
1733                            .envelopes
1734                            .push_front(envelope),
1735                        PendingEnvelope::Local(envelope) => {
1736                            self.queue.borrow_mut().envelopes.push_front(envelope);
1737                        }
1738                    }
1739                    report.blocked = true;
1740                    break;
1741                }
1742                ScopeState::Published => {
1743                    if let PendingEnvelope::Background(background) = &envelope {
1744                        match background.delivery {
1745                            BackgroundDelivery::Completion if !background.control.deliver() => {
1746                                report.dropped += 1;
1747                                continue;
1748                            }
1749                            BackgroundDelivery::Rejection
1750                                if background.control.status() != ComponentTaskStatus::Rejected =>
1751                            {
1752                                report.dropped += 1;
1753                                continue;
1754                            }
1755                            BackgroundDelivery::Completion | BackgroundDelivery::Rejection => {}
1756                        }
1757                    } else if let Some(control) = envelope.control()
1758                        && !control.deliver()
1759                    {
1760                        report.dropped += 1;
1761                        continue;
1762                    }
1763                    let payload: Box<dyn Any> = match envelope {
1764                        PendingEnvelope::Background(envelope) => envelope.payload,
1765                        PendingEnvelope::Local(envelope) => envelope.payload,
1766                    };
1767                    let tasks = self.task_spawner(token);
1768                    self.scopes
1769                        .get_mut(token.scope)
1770                        .unwrap()
1771                        .dispatch(payload, tasks);
1772                    report.dispatched += 1;
1773                    report.dirty.push(token);
1774                }
1775            }
1776        }
1777        report
1778    }
1779
1780    pub fn pending(&self) -> usize {
1781        self.queue.borrow().envelopes.len() + self.background.lock().unwrap().envelopes.len()
1782    }
1783
1784    #[cfg(test)]
1785    pub(crate) fn exhaust_task_capacity(&self) {
1786        self.task_limiter
1787            .active
1788            .store(BACKGROUND_TASK_CAPACITY, Ordering::Release);
1789    }
1790
1791    pub(crate) fn pending_tokens(&self) -> Vec<ComponentToken> {
1792        let mut tokens = self
1793            .queue
1794            .borrow()
1795            .envelopes
1796            .iter()
1797            .map(|envelope| envelope.token)
1798            .collect::<Vec<_>>();
1799        tokens.extend(
1800            self.background
1801                .lock()
1802                .unwrap()
1803                .envelopes
1804                .iter()
1805                .map(|envelope| envelope.token),
1806        );
1807        tokens
1808    }
1809
1810    pub(crate) fn next_pending_token(&self) -> Option<ComponentToken> {
1811        let local = self
1812            .queue
1813            .borrow()
1814            .envelopes
1815            .front()
1816            .map(|envelope| envelope.token);
1817        let background = self
1818            .background
1819            .lock()
1820            .unwrap()
1821            .envelopes
1822            .front()
1823            .map(|envelope| envelope.token);
1824        if self.drain_background_next {
1825            background.or(local)
1826        } else {
1827            local.or(background)
1828        }
1829    }
1830
1831    pub(crate) fn context_dependencies(
1832        &self,
1833        token: ComponentToken,
1834    ) -> Option<&ContextDependencies> {
1835        self.validate_window(token);
1836        self.scopes.get(token.scope).unwrap().context_dependencies()
1837    }
1838
1839    pub(crate) fn set_context_dependencies(
1840        &mut self,
1841        token: ComponentToken,
1842        dependencies: ContextDependencies,
1843    ) {
1844        self.validate_window(token);
1845        let unchanged = self
1846            .scopes
1847            .get(token.scope)
1848            .unwrap()
1849            .context_dependencies()
1850            .map_or_else(
1851                || dependencies.is_empty(),
1852                |previous| previous == &dependencies,
1853            );
1854        if unchanged {
1855            return;
1856        }
1857        let previous = self
1858            .scopes
1859            .get(token.scope)
1860            .unwrap()
1861            .context_dependencies()
1862            .cloned()
1863            .unwrap_or_default();
1864        for dependency in previous
1865            .iter()
1866            .filter(|dependency| !dependencies.contains(dependency))
1867        {
1868            self.remove_context_consumer(*dependency, token.scope);
1869        }
1870        for dependency in dependencies
1871            .iter()
1872            .filter(|dependency| !previous.contains(dependency))
1873            .copied()
1874        {
1875            self.context_consumers
1876                .entry(dependency)
1877                .or_default()
1878                .insert(token.scope);
1879            self.context_consumers_by_id
1880                .entry(dependency.id)
1881                .or_default()
1882                .insert(token.scope);
1883        }
1884        self.scopes
1885            .get_mut(token.scope)
1886            .unwrap()
1887            .set_context_dependencies(dependencies);
1888    }
1889
1890    pub(crate) fn context_consumers(
1891        &self,
1892        dependency: ContextDependency,
1893    ) -> impl Iterator<Item = ScopeId> + '_ {
1894        self.context_consumers
1895            .get(&dependency)
1896            .into_iter()
1897            .flatten()
1898            .copied()
1899    }
1900
1901    pub(crate) fn context_consumers_for_id(
1902        &self,
1903        id: ContextId,
1904    ) -> impl Iterator<Item = ScopeId> + '_ {
1905        self.context_consumers_by_id
1906            .get(&id)
1907            .into_iter()
1908            .flatten()
1909            .copied()
1910    }
1911
1912    pub(crate) fn view(
1913        &self,
1914        token: ComponentToken,
1915        contexts: ContextSnapshot,
1916    ) -> Result<ComponentRender, ComponentDeclarationError> {
1917        self.validate_window(token);
1918        self.scopes.get(token.scope).unwrap().view(contexts)
1919    }
1920
1921    pub(crate) fn type_name(&self, token: ComponentToken) -> &'static str {
1922        self.validate_window(token);
1923        self.scopes.get(token.scope).unwrap().type_name()
1924    }
1925
1926    pub fn cleanup_effects(&self, token: ComponentToken) {
1927        self.validate_window(token);
1928        self.scopes.get(token.scope).unwrap().cleanup_effects();
1929    }
1930
1931    pub fn commit_effects(&self, token: ComponentToken) {
1932        self.validate_window(token);
1933        self.scopes.get(token.scope).unwrap().commit_effects();
1934    }
1935
1936    pub fn prepare_effects(&self, token: ComponentToken) {
1937        self.validate_window(token);
1938        self.scopes.get(token.scope).unwrap().prepare_effects();
1939    }
1940
1941    pub(crate) fn token(&self, scope: ScopeId) -> ComponentToken {
1942        self.scopes.state(scope).unwrap();
1943        ComponentToken {
1944            window: self.window,
1945            scope,
1946        }
1947    }
1948
1949    pub(crate) fn set_waker(&mut self, wake: Rc<dyn Fn()>) {
1950        self.queue.borrow_mut().wake = Some(wake);
1951    }
1952
1953    pub(crate) fn set_background_waker(&mut self, wake: Arc<dyn Fn() -> bool + Send + Sync>) {
1954        self.background.lock().unwrap().wake = Some(wake);
1955    }
1956
1957    pub(crate) fn close(&mut self) {
1958        self.window_endpoint.close();
1959        {
1960            let mut queue = self.queue.borrow_mut();
1961            queue.open = false;
1962            queue.active.clear();
1963            for envelope in queue.envelopes.drain(..) {
1964                if let Some(control) = envelope.control {
1965                    control.cancel();
1966                }
1967            }
1968            queue.wake = None;
1969        }
1970        let mut background = self.background.lock().unwrap();
1971        background.open = false;
1972        for envelope in background.envelopes.drain(..) {
1973            envelope.control.cancel();
1974        }
1975        background.wake = None;
1976        background.wake_pending = false;
1977        for tasks in background.tasks.values() {
1978            for control in tasks.iter().filter_map(Weak::upgrade) {
1979                control.cancel();
1980            }
1981        }
1982        background.tasks.clear();
1983        self.context_consumers.clear();
1984        self.context_consumers_by_id.clear();
1985    }
1986
1987    fn cancel_scope_tasks(&mut self, scope: ScopeId) {
1988        let mut background = self.background.lock().unwrap();
1989        if let Some(tasks) = background.tasks.remove(&scope) {
1990            for control in tasks.iter().filter_map(Weak::upgrade) {
1991                control.cancel();
1992            }
1993        }
1994        for envelope in background
1995            .envelopes
1996            .iter()
1997            .filter(|envelope| envelope.token.scope == scope)
1998        {
1999            envelope.control.cancel();
2000        }
2001        background
2002            .envelopes
2003            .retain(|envelope| envelope.token.scope != scope);
2004    }
2005
2006    fn task_spawner(&self, token: ComponentToken) -> TaskSpawner {
2007        TaskSpawner {
2008            limiter: Arc::clone(&self.task_limiter),
2009            queue: Arc::clone(&self.background),
2010            token,
2011        }
2012    }
2013
2014    fn remove_scope_messages(&mut self, scope: ScopeId) {
2015        let mut queue = self.queue.borrow_mut();
2016        queue.active.remove(&scope);
2017        for envelope in queue
2018            .envelopes
2019            .iter()
2020            .filter(|envelope| envelope.token.scope == scope)
2021        {
2022            if let Some(control) = &envelope.control {
2023                control.cancel();
2024            }
2025        }
2026        queue
2027            .envelopes
2028            .retain(|envelope| envelope.token.scope != scope);
2029    }
2030
2031    fn clear_context_dependencies(&mut self, scope: ScopeId) {
2032        self.set_context_dependencies(
2033            ComponentToken {
2034                window: self.window,
2035                scope,
2036            },
2037            ContextDependencies::default(),
2038        );
2039    }
2040
2041    fn remove_context_consumer(&mut self, dependency: ContextDependency, scope: ScopeId) {
2042        if let Some(consumers) = self.context_consumers.get_mut(&dependency) {
2043            consumers.remove(&scope);
2044            if consumers.is_empty() {
2045                self.context_consumers.remove(&dependency);
2046            }
2047        }
2048        if let Some(consumers) = self.context_consumers_by_id.get_mut(&dependency.id) {
2049            consumers.remove(&scope);
2050            if consumers.is_empty() {
2051                self.context_consumers_by_id.remove(&dependency.id);
2052            }
2053        }
2054    }
2055
2056    fn validate_window(&self, token: ComponentToken) {
2057        assert_eq!(token.window, self.window);
2058    }
2059}
2060
2061impl Drop for ComponentStore {
2062    fn drop(&mut self) {
2063        self.close();
2064    }
2065}
2066
2067#[cfg(test)]
2068#[path = "component_tests.rs"]
2069mod tests;