1use std::collections::{BTreeMap, BTreeSet};
4use std::pin::Pin;
5use std::sync::Arc;
6
7use sva_ast::Graph;
8use sva_formula::{Hash, NodeId};
9
10use super::offer::{Offers, readable};
11use super::session::Session;
12use super::table::{self, Table};
13use super::{Render, RenderConfig, closed, driving, dropped, drove, frontier, planned_over};
14use crate::cache::{Backend, Recording, Stored, Tier};
15use crate::error::EngineError;
16use crate::instantiate;
17use crate::schedule;
18use crate::typing::Typing;
19
20pub trait Abandon {
22 fn abandoned(&self) -> Pin<Box<dyn Future<Output = bool> + '_>>;
23}
24
25pub struct Never;
26
27impl Abandon for Never {
28 fn abandoned(&self) -> Pin<Box<dyn Future<Output = bool> + '_>> {
29 Box::pin(std::future::ready(false))
30 }
31}
32
33pub async fn render_over<B: Backend>(
35 graph: &Graph,
36 target: &str,
37 config: RenderConfig,
38 tier: &Tier<B>,
39) -> Result<Render, EngineError> {
40 render_in(&mut Session::default(), graph, target, config, tier, &Never).await
41}
42
43pub async fn render_in<B: Backend>(
48 session: &mut Session,
49 graph: &Graph,
50 target: &str,
51 config: RenderConfig,
52 tier: &Tier<B>,
53 abandon: &dyn Abandon,
54) -> Result<Render, EngineError> {
55 let round = tier.begin();
56 let mut recording = Recording::over(tier.memory());
57 let instances = instantiate::instantiate(graph, target, config.rate)?;
58 let root = instances.instance_of(target)?;
59 let order = schedule::schedule_from(&instances, std::slice::from_ref(&root))?;
60 let Session { own, stand_in } = session;
61 let typed = own.typed(&instances, &order)?;
62 let lowered = typed.lowered().to_vec();
63 let keys = keys(typed, &order, &config);
64 let mut found = frontier::Frontier::from((&instances, &order), &keys, &root, &config);
65 found.walked(tier, round).await;
66 let stood = |stored: &BTreeMap<String, Arc<Stored>>| {
67 let mut tys = typed.clone();
68 tys.stand(stored);
69 tys
70 };
71 if dropped(&config) && found.stored.contains_key(&root) {
72 recording.found(std::mem::take(&mut found.lookups));
73 let tys = stood(&found.stored);
74 let id = tys.id(&root).ok_or(EngineError::UnknownNode(root))?;
75 let schedule = schedule::plan(&tys, id, &config.asks);
76 let mut held = Render::shell(tys, id, config, schedule);
77 let mut stats = recording.stats();
78 stats.typed = lowered;
79 held.cache_stats = Some(stats);
80 return Ok(held);
81 }
82 let mut retyped = lowered;
83 let mut held = loop {
84 found.walked(tier, round).await;
85 let tys = stood(&found.stored);
86 let id = tys
87 .id(&root)
88 .ok_or_else(|| EngineError::UnknownNode(root.clone()))?;
89 let bounds: BTreeSet<NodeId> = keys
90 .keys()
91 .filter_map(|path| tys.id(path))
92 .filter(|id| readable(&tys, *id))
93 .collect();
94 let mut held = planned_over(
95 (graph, target, &instances),
96 (tys, id),
97 (config.clone(), &mut *stand_in),
98 &bounds,
99 )?;
100 retyped.append(&mut held.stand_in_typed);
101 let short = match (&mut held.table, held.range) {
102 (Some(table), Some(range)) => {
103 let mut needs = table.needs(range);
104 while !needs.is_empty() {
105 let fetched = tier.fetch(&needs).await;
106 for (key, parts) in fetched.handed {
107 table.took(key, &parts);
108 }
109 needs = fetched.left;
110 }
111 short(table, range)
112 }
113 _ => Vec::new(),
114 };
115 if short.is_empty() {
116 break held;
117 }
118 for path in short {
119 found.reopen(&path);
120 }
121 };
122 let mut offers = Offers::of(&mut held, (&keys, &found), tier.memory());
123 recording.found(std::mem::take(&mut found.lookups));
124 let walked = recording.stats();
125 match driving(&mut held, recording)? {
126 Some(mut driver) => {
127 loop {
128 if abandon.abandoned().await {
129 return Err(EngineError::Abandoned);
130 }
131 if !driver.pull()? {
132 break;
133 }
134 offers.whole(&driver.table, tier.memory());
135 let needs = driver.table.needs(driver.next());
136 for (key, parts) in tier.fetch(&needs).await.handed {
137 driver.table.took(key, &parts);
138 }
139 }
140 offers.rest(&driver.table, tier.memory());
141 drove(&mut held, driver);
142 }
143 None => held.cache_stats = Some(walked),
144 }
145 let computed = held.table.as_ref().map_or(Vec::new(), |table| {
146 let computed = table.values.iter();
147 let computed = computed.filter(|(_, v)| !matches!(v.kind, table::Kind::Resident { .. }));
148 computed.map(|(_, v)| v.name.clone()).collect()
149 });
150 if let Some(stats) = &mut held.cache_stats {
151 stats.typed = retyped;
152 stats.planned = computed;
153 stats.unslotted = held.unslotted.clone();
154 }
155 closed(&mut held)?;
156 Ok(held)
157}
158
159pub(crate) fn keys(
162 tys: &Typing,
163 order: &schedule::Order,
164 config: &RenderConfig,
165) -> BTreeMap<String, Hash> {
166 let paths = order.groups.iter().flatten();
167 let named = paths.filter_map(|path| {
168 let identity = crate::refs::identity(tys, tys.id(path)?).ok()?;
169 let key = crate::cache::node_key(identity, config.rate, &config.profile);
170 Some((path.clone(), key))
171 });
172 named.collect()
173}
174
175fn short(table: &Table, range: sva_samples::Extent) -> Vec<String> {
177 let needs = table.demand(range);
178 table
179 .values
180 .iter()
181 .filter(|(at, value)| {
182 matches!(value.kind, table::Kind::Resident { .. }) && !needs[*at].compute.is_empty()
183 })
184 .map(|(_, value)| value.name.clone())
185 .collect()
186}