feat(jobq): add a job builder that names nodes instead of counting them
`JobBuilder::node(payload)` hands back a `NodeRef` handle obtainable no other way, and edges are handle -> handle. `insert_into(graph, root_parent)` consumes the builder, inserts in declaration order, and returns the id each handle was minted as. There is no intermediate node-description type: the builder inserts through `Graph::insert` directly, so nothing has to stay in sync with that signature. `root_parent` is the group's attachment point — a node that declared no parent hangs there. That is what makes a job a self-contained sub-DAG: a template is written without knowing which container node it will live under, and the same builder serves a runtime-emitted subgraph hanging off its emitting node. Generic over the same `N`/`R` as `Graph`, so it belongs to the library rather than to any one caller's node kind. `.needs(name)` / `.needs_units(name, count)` declare resource deps at the construction site, next to the node that needs them. `Scheduler::insert_job` is the same over a scheduler, via the shared `insert_with` sink, so a caller building a job never reaches past the scheduler at the graph underneath. Declaration order is enforced rather than papered over: a node referencing one declared later is a named `BuildError::ForwardEdge` / `ForwardParent`. Sorting for the caller would silently accept a shape the graph cannot express, and would put ordering logic in a second place. Additive — `Graph` is untouched.
This commit is contained in:
parent
42fdd98a7a
commit
f161f8e40f
3 changed files with 547 additions and 0 deletions
|
|
@ -31,6 +31,7 @@
|
|||
use std::collections::HashMap;
|
||||
use std::hash::Hash;
|
||||
|
||||
use crate::builder::{BuildError, JobBuilder, NodeGuid};
|
||||
use crate::resources::ResourceTable;
|
||||
use crate::{Dep, Graph, GraphError, NodeId, State, TerminalState};
|
||||
|
||||
|
|
@ -99,6 +100,28 @@ impl<N, R: Clone + Eq + Hash> Scheduler<N, R> {
|
|||
self.graph.insert(payload, deps, parent)
|
||||
}
|
||||
|
||||
/// Insert a whole job — every node a [`JobBuilder`] declared — under
|
||||
/// `root_parent`, returning the id each handle's node was minted as.
|
||||
///
|
||||
/// The scheduler-side counterpart of [`JobBuilder::insert_into`]: same
|
||||
/// resolution, but each node goes through [`Scheduler::append`], so a
|
||||
/// caller building a job never has to reach past the scheduler at the graph
|
||||
/// underneath. Call [`Scheduler::settle`] afterwards to start whatever
|
||||
/// became runnable.
|
||||
///
|
||||
/// # Errors
|
||||
/// Propagates [`BuildError`] — a forward reference in the job's own
|
||||
/// declarations, or a graph rejection.
|
||||
pub fn insert_job(
|
||||
&mut self,
|
||||
job: JobBuilder<N, R>,
|
||||
root_parent: Option<NodeId>,
|
||||
) -> Result<HashMap<NodeGuid, NodeId>, BuildError> {
|
||||
job.insert_with(root_parent, |payload, deps, parent| {
|
||||
self.append(payload, deps, parent)
|
||||
})
|
||||
}
|
||||
|
||||
/// Claim every currently-runnable pending node and start it: node-deps
|
||||
/// satisfied and all resource-deps acquired atomically (all-or-nothing).
|
||||
/// Each claimed node is marked `Running`, its acquired units recorded, and
|
||||
|
|
|
|||
Loading…
Reference in a new issue