yew_stdweb/agent/local/
context.rs

1use super::*;
2use crate::callback::Callback;
3use crate::scheduler::Shared;
4use anymap::{self, AnyMap};
5use slab::Slab;
6use std::cell::RefCell;
7use std::marker::PhantomData;
8use std::rc::Rc;
9
10thread_local! {
11    static LOCAL_AGENTS_POOL: RefCell<AnyMap> = RefCell::new(AnyMap::new());
12}
13
14/// Create a single instance in the current thread.
15#[allow(missing_debug_implementations)]
16pub struct Context<AGN> {
17    _agent: PhantomData<AGN>,
18}
19
20impl<AGN> Discoverer for Context<AGN>
21where
22    AGN: Agent,
23{
24    type Agent = AGN;
25
26    fn spawn_or_join(callback: Option<Callback<AGN::Output>>) -> Box<dyn Bridge<AGN>> {
27        let mut scope_to_init = None;
28        let bridge = LOCAL_AGENTS_POOL.with(|pool| {
29            let mut pool = pool.borrow_mut();
30            match pool.entry::<LocalAgent<AGN>>() {
31                anymap::Entry::Occupied(mut entry) => entry.get_mut().create_bridge(callback),
32                anymap::Entry::Vacant(entry) => {
33                    let scope = AgentScope::<AGN>::new();
34                    let launched = LocalAgent::new(&scope);
35                    let responder = SlabResponder {
36                        slab: launched.slab(),
37                    };
38                    scope_to_init = Some((scope, responder));
39                    entry.insert(launched).create_bridge(callback)
40                }
41            }
42        });
43        if let Some((scope, responder)) = scope_to_init {
44            let agent_link = AgentLink::connect(&scope, responder);
45            let upd = AgentLifecycleEvent::Create(agent_link);
46            scope.send(upd);
47        }
48        let upd = AgentLifecycleEvent::Connected(bridge.id);
49        bridge.scope.send(upd);
50        Box::new(bridge)
51    }
52}
53
54struct SlabResponder<AGN: Agent> {
55    slab: Shared<Slab<Option<Callback<AGN::Output>>>>,
56}
57
58impl<AGN: Agent> Responder<AGN> for SlabResponder<AGN> {
59    fn respond(&self, id: HandlerId, output: AGN::Output) {
60        locate_callback_and_respond::<AGN>(&self.slab, id, output);
61    }
62}
63
64impl<AGN: Agent> Dispatchable for Context<AGN> {}
65
66struct ContextBridge<AGN: Agent> {
67    scope: AgentScope<AGN>,
68    id: HandlerId,
69}
70
71impl<AGN: Agent> Bridge<AGN> for ContextBridge<AGN> {
72    fn send(&mut self, msg: AGN::Input) {
73        let upd = AgentLifecycleEvent::Input(msg, self.id);
74        self.scope.send(upd);
75    }
76}
77
78impl<AGN: Agent> Drop for ContextBridge<AGN> {
79    fn drop(&mut self) {
80        let terminate_worker = LOCAL_AGENTS_POOL.with(|pool| {
81            let mut pool = pool.borrow_mut();
82            let terminate_worker = {
83                if let Some(launched) = pool.get_mut::<LocalAgent<AGN>>() {
84                    launched.remove_bridge(self)
85                } else {
86                    false
87                }
88            };
89
90            if terminate_worker {
91                pool.remove::<LocalAgent<AGN>>();
92            }
93
94            terminate_worker
95        });
96
97        let upd = AgentLifecycleEvent::Disconnected(self.id);
98        self.scope.send(upd);
99
100        if terminate_worker {
101            let upd = AgentLifecycleEvent::Destroy;
102            self.scope.send(upd);
103        }
104    }
105}
106
107struct LocalAgent<AGN: Agent> {
108    scope: AgentScope<AGN>,
109    slab: SharedOutputSlab<AGN>,
110}
111
112impl<AGN: Agent> LocalAgent<AGN> {
113    pub fn new(scope: &AgentScope<AGN>) -> Self {
114        let slab = Rc::new(RefCell::new(Slab::new()));
115        LocalAgent {
116            scope: scope.clone(),
117            slab,
118        }
119    }
120
121    fn slab(&self) -> SharedOutputSlab<AGN> {
122        self.slab.clone()
123    }
124
125    fn create_bridge(&mut self, callback: Option<Callback<AGN::Output>>) -> ContextBridge<AGN> {
126        let respondable = callback.is_some();
127        let mut slab = self.slab.borrow_mut();
128        let id: usize = slab.insert(callback);
129        let id = HandlerId::new(id, respondable);
130        ContextBridge {
131            scope: self.scope.clone(),
132            id,
133        }
134    }
135
136    fn remove_bridge(&mut self, bridge: &ContextBridge<AGN>) -> Last {
137        let mut slab = self.slab.borrow_mut();
138        let _ = slab.remove(bridge.id.raw_id());
139        slab.is_empty()
140    }
141}