yew_stdweb/agent/local/
context.rs1use 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#[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}