1use std::collections::BTreeMap;
4use std::pin::Pin;
5use std::rc::Rc;
6use std::sync::Arc;
7
8use sva_ast::Graph;
9use sva_formula::NodeId;
10use sva_samples::Buffer;
11
12use super::value_graph;
13use super::world::{Reach, Reached, Walking, World};
14use super::{
15 Render, RenderConfig, closed, cut_of, driving, dropped, drove, ended, planned_over, told_end,
16};
17use crate::cache::{Backend, Call, Kind, Known, Phase, Recording, Stored, Tier};
18use crate::error::EngineError;
19use crate::schedule;
20
21pub trait Abandon {
23 fn abandoned(&self) -> Pin<Box<dyn Future<Output = bool> + '_>>;
24}
25
26pub struct Never;
27
28pub trait Hear {
30 fn heard(&self, block: &Buffer) -> bool;
32}
33
34#[derive(Default)]
36pub struct Session {
37 own: Option<World>,
38 pub(super) stand_in: Option<World>,
40}
41
42impl Abandon for Never {
43 fn abandoned(&self) -> Pin<Box<dyn Future<Output = bool> + '_>> {
44 Box::pin(std::future::ready(false))
45 }
46}
47
48pub async fn render_over<B: Backend>(
50 graph: &Graph,
51 target: &str,
52 config: RenderConfig,
53 tier: &Tier<B>,
54) -> Result<Render, EngineError> {
55 render_in(
56 &mut Session::default(),
57 graph,
58 target,
59 config,
60 tier,
61 (&Never, None),
62 )
63 .await
64}
65
66pub async fn render_in<B: Backend>(
71 session: &mut Session,
72 graph: &Graph,
73 target: &str,
74 config: RenderConfig,
75 tier: &Tier<B>,
76 hooks: (&dyn Abandon, Option<&dyn Hear>),
77) -> Result<Render, EngineError> {
78 let call = tier.call(Kind::Render, target, Phase::Typing);
79 render_called(session, graph, target, config, tier, hooks, call).await
80}
81
82pub async fn render_called<B: Backend>(
84 session: &mut Session,
85 graph: &Graph,
86 target: &str,
87 config: RenderConfig,
88 tier: &Tier<B>,
89 (abandon, hear): (&dyn Abandon, Option<&dyn Hear>),
90 call: Call,
91) -> Result<Render, EngineError> {
92 let hooks = (abandon, hear, &call);
93 let done = run(session, (graph, target), config, tier, hooks).await;
94 call.end(match &done {
95 Ok(_) => Phase::Done,
96 Err(EngineError::Abandoned) => Phase::Abandoned,
97 Err(_) => Phase::Failed,
98 });
99 done
100}
101
102fn delivered(call: &Call, driver: &super::drive::Driver, every: u64) {
104 let to = driver.end().unwrap_or(driver.at);
105 let last = driver.end().unwrap_or(driver.last());
106 let (samples, total) = ((to - driver.start) as u64, (last - driver.start) as u64);
107 call.delivered((samples, Some(total), every), || {
108 (driver.progress(), driver.recording.tally())
109 });
110}
111
112async fn run<B: Backend>(
113 session: &mut Session,
114 (graph, target): (&Graph, &str),
115 config: RenderConfig,
116 tier: &Tier<B>,
117 (abandon, hear, call): (&dyn Abandon, Option<&dyn Hear>, &Call),
118) -> Result<Render, EngineError> {
119 call.phase(Phase::Typing);
120 let memory = tier.memory();
121 let mut recording = Recording::over(memory);
122 let Session { own, stand_in } = session;
123 let typed = |typed| call.typed(typed);
124 let (world, root) = World::rendered(own, graph, target, (config.rate, Some(&typed)))?;
125 let (instances, typed) = (&world.instances, &world.typing);
126 let id = typed
127 .id(&root)
128 .ok_or_else(|| EngineError::UnknownNode(root.clone()))?;
129 let profile = config.profile;
130 let round = tier.begin();
131 let (mut supports, mut end) = world.bounds(id, &profile);
132 let known = supports.len();
133 let mut told = 0;
134 call.phase(Phase::LookingUp);
135 if end.is_none() && config.range.end.is_none() {
136 let stored = root_stored(world, &root, &config, (tier, round)).await;
137 let stated = stored.and_then(|stored| Some((stored.support, told_end(&stored, &profile)?)));
138 if let Some((support, stated)) = stated {
139 told += usize::from(supports.told(id, support));
140 end = Some(stated);
141 }
142 }
143 let (config, mut decided) = ended(typed, id, config, (supports, end));
144 world.bound(id, &profile, &decided);
145 let cut = decided.1.map(|end| cut_of(&end, &profile));
146 let lowered = typed.lowered().to_vec();
147 let found = walked(
148 world,
149 (&root, &config, false),
150 (tier, round, &mut recording),
151 )
152 .await;
153 call.looked(Phase::Planning, || recording.tally());
154 let tys = Rc::clone(typed);
155 if dropped(&config) && found.held.contains_key(&root) {
156 let schedule = schedule::plan(&tys, id, &config.asks);
157 let mut held = Render::shell((tys, id), (config, schedule), memory.clone());
158 let mut stats = recording.stats(memory);
159 stats.typed = lowered;
160 stats.analysed = decided.0.len().saturating_sub(known + told);
161 held.cache_stats = Some(stats);
162 let stored = &found.held[&root];
163 let answered = stored.extents().iter().map(|e| e.len() as u64).sum();
164 let from_store = recording.stored(stored.key);
165 call.answered(answered, from_store, recording.tally());
166 return Ok(held);
167 }
168 let hits: BTreeMap<NodeId, Arc<Stored>> = found
169 .held
170 .iter()
171 .filter_map(|(path, stored)| Some((tys.id(path)?, Arc::clone(stored))))
172 .collect();
173 for (hit, stored) in &hits {
174 told += usize::from(decided.0.told(*hit, stored.support));
175 }
176 let mut analysed = decided.0.len();
177 let mut held = planned_over(
178 (graph, target, instances),
179 (tys, id),
180 (config, &mut *stand_in, memory.clone()),
181 (decided, &hits),
182 )?;
183 if let Some(value_graph) = &held.value_graph {
184 world.bound(id, &profile, &(value_graph.supports.clone(), None));
185 }
186 let mut retyped = lowered;
187 retyped.append(&mut held.stand_in_typed);
188 if let (Some(value_graph), Some(range)) = (&mut held.value_graph, held.range) {
189 loop {
190 let mut needs = value_graph.needs(range);
191 while !needs.is_empty() {
192 let fetched = tier.fetch(&needs).await;
193 for (key, parts) in fetched.handed {
194 value_graph.took(key, &parts);
195 }
196 needs = fetched.left;
197 call.looked(Phase::Fetching, || recording.tally());
198 }
199 let short = value_graph.short((value_graph.root, range, value_graph::Past::Held));
200 if short.is_empty() {
201 break;
202 }
203 for at in short {
204 let under = held
205 .tys
206 .name(value_graph.answered(&held.tys, at))
207 .to_string();
208 let walking = (under.as_str(), &held.config, true);
209 let found = walked(world, walking, (tier, round, &mut recording)).await;
210 let hits = found.held.iter();
211 let hits = hits
212 .filter_map(|(path, stored)| Some((held.tys.id(path)?, Arc::clone(stored))));
213 let hits: BTreeMap<NodeId, Arc<Stored>> = hits.collect();
214 for (hit, stored) in &hits {
215 told += usize::from(value_graph.supports.told(*hit, stored.support));
216 }
217 value_graph.read_on(&held.tys, at, &hits)?;
218 }
219 value_graph.settled(value_graph.root, &[], (range.start, false));
220 value_graph.refuse_endless(range)?;
221 }
222 value_graph.offers(&held.tys, range, cut);
223 analysed = value_graph.supports.len();
224 }
225 let analysed = analysed.saturating_sub(known + told);
226 let walked = recording.stats(memory);
227 match driving(&mut held, (memory, recording))? {
228 Some(mut driver) => {
229 let every = u64::from(held.config.rate / 10);
230 delivered(call, &driver, every);
231 loop {
232 if abandon.abandoned().await {
233 return Err(EngineError::Abandoned);
234 }
235 let from = driver.at;
236 let hears = |progress| call.worked(progress);
237 let heard = call.subscribed().then_some(&hears as &dyn Fn(_));
238 if !driver.pull(heard)? {
239 break;
240 }
241 if hear.is_some_and(|hear| !hear.heard(&driver.played(from))) {
242 return Err(EngineError::Abandoned);
243 }
244 let needs = driver.value_graph.needs(driver.next());
245 for (key, parts) in tier.fetch(&needs).await.handed {
246 driver.value_graph.took(key, &parts);
247 }
248 delivered(call, &driver, every);
249 }
250 if driver.stop().is_some() {
251 delivered(call, &driver, 0);
252 }
253 call.phase(Phase::Finishing);
254 drove(&mut held, driver);
255 }
256 None => held.cache_stats = Some(walked),
257 }
258 let computed = held.value_graph.as_ref().map_or(Vec::new(), |value_graph| {
259 let computed = value_graph.values.iter();
260 let computed =
261 computed.filter(|(_, v)| !matches!(v.kind, value_graph::Kind::Resident { .. }));
262 computed.map(|(_, v)| v.name.clone()).collect()
263 });
264 if let Some(stats) = &mut held.cache_stats {
265 stats.typed = retyped;
266 stats.planned = computed;
267 stats.analysed = analysed;
268 stats.unslotted = held.unslotted.clone();
269 }
270 closed(&mut held)?;
271 Ok(held)
272}
273
274async fn root_stored<B: Backend>(
276 world: &World,
277 root: &str,
278 config: &RenderConfig,
279 (tier, round): (&Tier<B>, u64),
280) -> Option<Arc<Stored>> {
281 let key = world.root_key(root, config)?;
282 loop {
283 match tier.memory().answer(key, round) {
284 Known::Hit(stored) => return Some(stored),
285 Known::Miss => return None,
286 Known::Unknown => tier.lookup(key, round).await,
287 }
288 }
289}
290
291async fn walked<B: Backend>(
294 world: &World,
295 (root, config, under): (&str, &RenderConfig, bool),
296 (tier, round, seen): (&Tier<B>, u64, &mut Recording),
297) -> Reached {
298 let walking = Walking {
299 root,
300 config,
301 whole: true,
302 under,
303 };
304 loop {
305 let memory = tier.memory();
306 match world.walk(&walking, &mut |node, key| {
307 memory.answered((key, round), (node, seen))
308 }) {
309 Reach::Reached(reached) => return reached,
310 Reach::Asks(keys) => {
311 for key in keys {
312 tier.lookup(key, round).await;
313 }
314 }
315 }
316 }
317}