Skip to main content

sva_engine/render/
run.rs

1// Concern: renders a target over memory, from the root down to what it answers | Non-concern: what memory keeps or writes, computing a value | IO: (&Graph, target, Tier) -> Render
2
3use 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
20/// Whether a render's caller let it go, asked between blocks.
21pub 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
33/// `target` over `tier`, from the root down, in a session of its own.
34pub 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
43/// `target` over `tier`, from the root down: every node is named by what it computes, typed
44/// anew only where `session` typed it otherwise, and a node memory answers stands as its
45/// samples, nothing under it planned or computed; with `out` dropped and no reading, a root it
46/// answers ends the render unread. Abandoned, it stops before its next block.
47pub 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
159/// Each instance's node key: what it computes, at the render's rate and profile; an instance
160/// whose identity refuses is never looked up.
161pub(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
175/// Each node memory answered whose samples miss some its readers ask over `range`.
176fn 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}