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, Known, 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 (abandon, hear): (&dyn Abandon, Option<&dyn Hear>),
77) -> Result<Render, EngineError> {
78 let memory = tier.memory();
79 let mut recording = Recording::over(memory);
80 let Session { own, stand_in } = session;
81 let (world, root) = World::rendered(own, graph, target, config.rate)?;
82 let (instances, typed) = (&world.instances, &world.typing);
83 let id = typed
84 .id(&root)
85 .ok_or_else(|| EngineError::UnknownNode(root.clone()))?;
86 let profile = config.profile;
87 let round = tier.begin();
88 let (mut supports, mut end) = world.bounds(id, &profile);
89 let known = supports.len();
90 let mut told = 0;
91 if end.is_none() && config.range.end.is_none() {
92 let stored = root_stored(world, &root, &config, (tier, round)).await;
93 let stated = stored.and_then(|stored| Some((stored.support, told_end(&stored, &profile)?)));
94 if let Some((support, stated)) = stated {
95 told += usize::from(supports.told(id, support));
96 end = Some(stated);
97 }
98 }
99 let (config, mut decided) = ended(typed, id, config, (supports, end));
100 world.bound(id, &profile, &decided);
101 let cut = decided.1.map(|end| cut_of(&end, &profile));
102 let lowered = typed.lowered().to_vec();
103 let found = walked(
104 world,
105 (&root, &config, false),
106 (tier, round, &mut recording),
107 )
108 .await;
109 let tys = Rc::clone(typed);
110 if dropped(&config) && found.held.contains_key(&root) {
111 let schedule = schedule::plan(&tys, id, &config.asks);
112 let mut held = Render::shell((tys, id), (config, schedule), memory.clone());
113 let mut stats = recording.stats(memory);
114 stats.typed = lowered;
115 stats.analysed = decided.0.len().saturating_sub(known + told);
116 held.cache_stats = Some(stats);
117 return Ok(held);
118 }
119 let hits: BTreeMap<NodeId, Arc<Stored>> = found
120 .held
121 .iter()
122 .filter_map(|(path, stored)| Some((tys.id(path)?, Arc::clone(stored))))
123 .collect();
124 for (hit, stored) in &hits {
125 told += usize::from(decided.0.told(*hit, stored.support));
126 }
127 let mut analysed = decided.0.len();
128 let mut held = planned_over(
129 (graph, target, instances),
130 (tys, id),
131 (config, &mut *stand_in, memory.clone()),
132 (decided, &hits),
133 )?;
134 if let Some(value_graph) = &held.value_graph {
135 world.bound(id, &profile, &(value_graph.supports.clone(), None));
136 }
137 let mut retyped = lowered;
138 retyped.append(&mut held.stand_in_typed);
139 if let (Some(value_graph), Some(range)) = (&mut held.value_graph, held.range) {
140 loop {
141 let mut needs = value_graph.needs(range);
142 while !needs.is_empty() {
143 let fetched = tier.fetch(&needs).await;
144 for (key, parts) in fetched.handed {
145 value_graph.took(key, &parts);
146 }
147 needs = fetched.left;
148 }
149 let short = value_graph.short((value_graph.root, range, value_graph::Past::Held));
150 if short.is_empty() {
151 break;
152 }
153 for at in short {
154 let under = held
155 .tys
156 .name(value_graph.answered(&held.tys, at))
157 .to_string();
158 let walking = (under.as_str(), &held.config, true);
159 let found = walked(world, walking, (tier, round, &mut recording)).await;
160 let hits = found.held.iter();
161 let hits = hits
162 .filter_map(|(path, stored)| Some((held.tys.id(path)?, Arc::clone(stored))));
163 let hits: BTreeMap<NodeId, Arc<Stored>> = hits.collect();
164 for (hit, stored) in &hits {
165 told += usize::from(value_graph.supports.told(*hit, stored.support));
166 }
167 value_graph.read_on(&held.tys, at, &hits)?;
168 }
169 value_graph.settled(value_graph.root, &[], (range.start, false));
170 value_graph.refuse_endless(range)?;
171 }
172 value_graph.offers(&held.tys, range, cut);
173 analysed = value_graph.supports.len();
174 }
175 let analysed = analysed.saturating_sub(known + told);
176 let walked = recording.stats(memory);
177 match driving(&mut held, (memory, recording))? {
178 Some(mut driver) => {
179 loop {
180 if abandon.abandoned().await {
181 return Err(EngineError::Abandoned);
182 }
183 let from = driver.at;
184 if !driver.pull()? {
185 break;
186 }
187 if hear.is_some_and(|hear| !hear.heard(&driver.played(from))) {
188 return Err(EngineError::Abandoned);
189 }
190 let needs = driver.value_graph.needs(driver.next());
191 for (key, parts) in tier.fetch(&needs).await.handed {
192 driver.value_graph.took(key, &parts);
193 }
194 }
195 drove(&mut held, driver);
196 }
197 None => held.cache_stats = Some(walked),
198 }
199 let computed = held.value_graph.as_ref().map_or(Vec::new(), |value_graph| {
200 let computed = value_graph.values.iter();
201 let computed =
202 computed.filter(|(_, v)| !matches!(v.kind, value_graph::Kind::Resident { .. }));
203 computed.map(|(_, v)| v.name.clone()).collect()
204 });
205 if let Some(stats) = &mut held.cache_stats {
206 stats.typed = retyped;
207 stats.planned = computed;
208 stats.analysed = analysed;
209 stats.unslotted = held.unslotted.clone();
210 }
211 closed(&mut held)?;
212 Ok(held)
213}
214
215async fn root_stored<B: Backend>(
217 world: &World,
218 root: &str,
219 config: &RenderConfig,
220 (tier, round): (&Tier<B>, u64),
221) -> Option<Arc<Stored>> {
222 let key = world.root_key(root, config)?;
223 loop {
224 match tier.memory().answer(key, round) {
225 Known::Hit(stored) => return Some(stored),
226 Known::Miss => return None,
227 Known::Unknown => tier.lookup(key, round).await,
228 }
229 }
230}
231
232async fn walked<B: Backend>(
235 world: &World,
236 (root, config, under): (&str, &RenderConfig, bool),
237 (tier, round, seen): (&Tier<B>, u64, &mut Recording),
238) -> Reached {
239 let walking = Walking {
240 root,
241 config,
242 whole: true,
243 under,
244 };
245 loop {
246 let memory = tier.memory();
247 match world.walk(&walking, &mut |node, key| {
248 memory.answered((key, round), (node, seen))
249 }) {
250 Reach::Reached(reached) => return reached,
251 Reach::Asks(keys) => {
252 for key in keys {
253 tier.lookup(key, round).await;
254 }
255 }
256 }
257 }
258}