pub trait Kernel:
Send
+ Sync
+ KernelInternals {
Show 53 methods
// Required methods
fn engine(&self) -> Engine;
fn set_inputs(&mut self, coords: &[u64]);
fn set_input(&mut self, name: &str, value: Value) -> Result<(), WriteError>;
fn set_cursor(
&mut self,
name: &str,
partition: &Partition,
) -> Result<(), WriteError>;
fn eval(&mut self);
fn pull(&mut self, name: &str) -> Value;
fn input_names(&self) -> Vec<String>;
fn output_names(&self) -> Vec<String>;
fn output_type(&self, name: &str) -> Option<PortType>;
fn externs(&self) -> Vec<(String, PortType)>;
fn cursor_schemas(&self) -> &[SourceSchema];
fn plan(&self) -> EnginePlan;
fn input_value(&self, name: &str) -> Option<Value>;
fn const_inits(&self) -> &[ConstInit];
fn init_input_at(
&mut self,
index: usize,
value: Value,
) -> Result<(), WriteError>;
fn traversals(&self) -> &[Traversal];
fn traverse(&mut self, index: usize) -> Result<TraversalStream, String>;
fn invalidate_all(&mut self);
fn shared_cells(&self) -> Vec<SharedCellEntry>;
fn attach_shared_cell(
&mut self,
name: &str,
cell: Arc<SharedCellInner>,
) -> Result<(), String>;
fn into_program(self: Box<Self>) -> Arc<dyn KernelProgram> ⓘ;
fn ledger(&self) -> &Arc<CompileLedger> ⓘ;
fn resources(&self) -> &ResourceScope;
fn canonical_hash(&self) -> [u8; 32];
fn coord_count(&self) -> usize;
fn input_value_at(&self, index: usize) -> Option<Value>;
fn input_default_at(&self, index: usize) -> Option<Value>;
fn input_is_cell_bound(&self, index: usize) -> bool;
fn reset_inputs(&mut self);
fn fork(&self) -> Box<dyn Kernel>;
fn publish_broadcasts(&mut self);
fn commit_write_throughs(&mut self) -> Result<(), String>;
fn input_type_origin(&self, name: &str) -> Option<TypeOrigin>;
fn program_id(&self) -> ProgramId;
// Provided methods
fn input_index(&self, name: &str) -> Option<usize> { ... }
fn set_input_at(
&mut self,
index: usize,
value: Value,
) -> Result<(), WriteError> { ... }
fn output_index(&self, name: &str) -> Option<usize> { ... }
fn pull_at(&mut self, index: usize) -> Value { ... }
fn init(&mut self) -> Result<(), KernelError> { ... }
fn traverse_all(&mut self) -> Result<Vec<TraversalStream>, String> { ... }
fn output_cell(&self, _name: &str) -> Option<Arc<SharedCellInner>> { ... }
fn output_modifier(&self, _name: &str) -> BindingModifier { ... }
fn cells_in_scope(&self) -> Vec<SharedCellEntry> { ... }
fn set_transit_cells(&mut self, _cells: Vec<SharedCellEntry>) { ... }
fn scope_coordinates(&self) -> &[ScopeCoord] { ... }
fn extend_scope_coordinates(&mut self, _outer: &[ScopeCoord]) { ... }
fn input_port_type(&self, _name: &str) -> Option<PortType> { ... }
fn bind_input_cell(
&mut self,
_name: &str,
_cell: Arc<SharedCellInner>,
) -> bool { ... }
fn instance_hash(&self, ancestors: &[&dyn Kernel]) -> [u8; 32] { ... }
fn is_equivalent_to(&self, other: &dyn Kernel) -> bool { ... }
fn is_subset_of(&self, parent: &dyn Kernel) -> bool { ... }
fn as_interpreter(&self) -> Option<&PolydatKernel> { ... }
fn as_interpreter_mut(&mut self) -> Option<&mut PolydatKernel> { ... }
}Expand description
One kernel API for every engine, and the one error a write to a
declared slot can fail with. WriteError sits beside KernelError
at the root because they are the pair a host handles: one for
building a kernel, one for writing to it.
A kernel on any engine: the interpreter, the closure tier, the
hybrid kernel, or pure native code. Every engine accepts every
program the interpreter accepts, or refuses it at construction
with a reason, and computes the same values for the same inputs;
the choice of engine changes how fast a program runs and nothing
else. This trait is the surface a host drives an engine through
without knowing which one it has.
The interpreter kernel and the compiled kernels also keep their
inherent methods (raw slot readers, eval(&[u64]), engine_counts)
as engine-specific extras; where a name is shared, the inherent
method is the one a call on the concrete type reaches, and the
trait’s is reached through dyn Kernel or Kernel::pull(&mut k, …).
Required Methods§
Sourcefn set_inputs(&mut self, coords: &[u64])
fn set_inputs(&mut self, coords: &[u64])
Set the coordinate inputs for the next evaluation.
Sourcefn set_input(&mut self, name: &str, value: Value) -> Result<(), WriteError>
fn set_input(&mut self, name: &str, value: Value) -> Result<(), WriteError>
Set an extern by name. One rule on every engine: the value must
satisfy the declared port type (a carrier’s bit-stuffed forms
included) or be None, which clears the extern; a value of
another type is refused at the write, never healed. A coordinate
is set with Self::set_inputs, not here. An unknown name is
an error naming the known ones.
Sourcefn set_cursor(
&mut self,
name: &str,
partition: &Partition,
) -> Result<(), WriteError>
fn set_cursor( &mut self, name: &str, partition: &Partition, ) -> Result<(), WriteError>
Narrow a cursor to one partition: its Ext slot and its six
scalar projections are set.
Sourcefn pull(&mut self, name: &str) -> Value
fn pull(&mut self, name: &str) -> Value
The named output for the inputs set so far, evaluating what it
needs and no more: the output’s cone, on all four engines (pure
native code, though one function, runs only the fusion units
of the output’s cone; engines.md §1). A side channel in
the cone fires when the output is pulled; a failing node fails
when pulled, with the same attributed message on every engine:
the node’s name, the outputs it feeds, the program’s context,
and its inputs. The value is owned; a handle is never returned to
the host, and a slot that holds None reads as None.
Sourcefn input_names(&self) -> Vec<String>
fn input_names(&self) -> Vec<String>
Every input by name, the coordinates first.
Sourcefn output_names(&self) -> Vec<String>
fn output_names(&self) -> Vec<String>
Every named output.
Sourcefn output_type(&self, name: &str) -> Option<PortType>
fn output_type(&self, name: &str) -> Option<PortType>
The declared port type of a named output.
Sourcefn cursor_schemas(&self) -> &[SourceSchema]
fn cursor_schemas(&self) -> &[SourceSchema]
The cursors the program declares, with the partitions the compiler resolved where it could.
Sourcefn plan(&self) -> EnginePlan
fn plan(&self) -> EnginePlan
What this kernel’s engine decided for the program: how much of it runs as native segments, as closure steps, and on the interpreter. The one planning detail a kernel exposes.
Sourcefn input_value(&self, name: &str) -> Option<Value>
fn input_value(&self, name: &str) -> Option<Value>
The value of a named input as the kernel holds it now, an extern
or a coordinate; None for a name that is not an input.
Sourcefn const_inits(&self) -> &[ConstInit]
fn const_inits(&self) -> &[ConstInit]
The const bindings this kernel initializes, in the order
Self::init evaluates them: a const that reads another comes
after it.
Sourcefn init_input_at(
&mut self,
index: usize,
value: Value,
) -> Result<(), WriteError>
fn init_input_at( &mut self, index: usize, value: Value, ) -> Result<(), WriteError>
Write an input as part of initialization. It is
Self::set_input_at except that a const’s slot is accepted,
which is how Self::init stores each const’s value.
Sourcefn traversals(&self) -> &[Traversal]
fn traversals(&self) -> &[Traversal]
The traversals the program declares, in document order.
Sourcefn traverse(&mut self, index: usize) -> Result<TraversalStream, String>
fn traverse(&mut self, index: usize) -> Result<TraversalStream, String>
Open the traversal at index against this kernel’s current
values (for_traversal.md §3.6): the comprehension’s sources see
the wires they reference as this kernel holds them now, and the
cascaded wires are snapshotted into every activation. Every
engine opens traversals (engines.md §3.6).
Sourcefn invalidate_all(&mut self)
fn invalidate_all(&mut self)
Begin the next cycle with nothing current, so every step, a side
channel included, runs again when pulled. The runtime model makes
a cycle whose inputs did not move cost nothing; this is how a
host runs such a cycle anyway, as the polydat binary does when
every input is fixed.
The cells this kernel’s shared bindings are bound to (scope
model §6): one register per binding, which every kernel holding
the cell reads and writes.
Bind the shared binding name to cell, so this kernel and
every other holder of the cell read and write one register:
a write on any of them is what the others read next, and a
dependent output is recomputed. A name that is not a shared
binding is an error naming the ones that are.
Sourcefn into_program(self: Box<Self>) -> Arc<dyn KernelProgram> ⓘ
fn into_program(self: Box<Self>) -> Arc<dyn KernelProgram> ⓘ
The program this kernel runs, shareable across threads: each
thread creates its own kernel from it with
KernelProgram::create_kernel.
What this kernel was set to does not travel with it. A
kernel created from the program starts at the program: every
extern at its declared default and every shared binding with
a cell of its own, whatever this kernel had been written to
before it became one. That holds on every engine.
The reason is that an extern is per-kernel state, in the same
family as the coordinates: both are writes into declared slots
of a running kernel, and neither is part of the compiled
program. A host that wants a value fixed for the program
fixes it before compiling, with
transform::assign_values
or an extern default in the source; a host that wants every
thread to see one register attaches a cell with
Self::attach_shared_cell.
Sourcefn ledger(&self) -> &Arc<CompileLedger> ⓘ
fn ledger(&self) -> &Arc<CompileLedger> ⓘ
The compile ledger of the program tree this kernel belongs to: what compiling it and everything opened from it has built.
Sourcefn resources(&self) -> &ResourceScope
fn resources(&self) -> &ResourceScope
The resource scope of the program tree this kernel belongs to:
the slot for the host’s ResourceAccessor
that every node of the tree looks resources up through. A host
that did not hand one to the compile
(CompileOptions::resources) installs its accessor here, once;
kernels created from or forked off this one, subscopes built
under it, and its traversal bodies share the scope.
Sourcefn canonical_hash(&self) -> [u8; 32]
fn canonical_hash(&self) -> [u8; 32]
The canonical hash of this kernel’s program (scope_model.md §8): equal for one program built on any of the four engines, and for every kernel created from or forked off it, and a function of what the program computes rather than of its source text. What a host keys a checkpoint on.
Sourcefn coord_count(&self) -> usize
fn coord_count(&self) -> usize
How many of the inputs are coordinates: they come first in
input_names, and set_inputs writes them.
Sourcefn input_value_at(&self, index: usize) -> Option<Value>
fn input_value_at(&self, index: usize) -> Option<Value>
The value input index holds now: a coordinate’s pending or
current value, an extern’s current value. None past the end.
Sourcefn input_default_at(&self, index: usize) -> Option<Value>
fn input_default_at(&self, index: usize) -> Option<Value>
The value input index starts with: an extern’s declared
default, U64(0) for a coordinate. None past the end.
Sourcefn input_is_cell_bound(&self, index: usize) -> bool
fn input_is_cell_bound(&self, index: usize) -> bool
Whether input index is bound to a shared cell, so that its
value is the cell’s and a reset leaves it alone.
Sourcefn reset_inputs(&mut self)
fn reset_inputs(&mut self)
Every input that is not a coordinate and not bound to a cell back at its default, and whatever depends on a changed one not current. What a host does at a boundary where values written for the last stretch must not leak into the next.
Sourcefn fork(&self) -> Box<dyn Kernel>
fn fork(&self) -> Box<dyn Kernel>
A new kernel over the same program with this kernel’s state: its inputs, its current outputs, and its cells, which stay shared (a cell is the scope’s register, not a value it holds), transit cells included. Callable concurrently on a kernel shared across threads (native_scope_trees.md §4).
Sourcefn publish_broadcasts(&mut self)
fn publish_broadcasts(&mut self)
Pull every output a descendant bound to by cell, so the descendant reads the current value. Nothing happens on a kernel nothing is bound under. A failing output is left for the pull that needs it to report.
Sourcefn commit_write_throughs(&mut self) -> Result<(), String>
fn commit_write_throughs(&mut self) -> Result<(), String>
Commit the Rule 2 write-throughs: pull each synthetic
__write_<name> output and write it through the cell of the
shared binding it exports to. No-op for a kernel without them.
Sourcefn input_type_origin(&self, name: &str) -> Option<TypeOrigin>
fn input_type_origin(&self, name: &str) -> Option<TypeOrigin>
How input name’s type was established: written by the author,
or inferred by the compiler and so open to
CompileOptions::input_variance (input_variance.md §3). A host
that compiles many scopes at Info reports each open input once
from here rather than from every compile’s log.
Sourcefn program_id(&self) -> ProgramId
fn program_id(&self) -> ProgramId
The identity of this kernel’s program: equal for kernels created from one program and for forks, different for any two programs, the same program compiled twice included. What a host seals a plan of pre-resolved indices against.
Provided Methods§
Sourcefn input_index(&self, name: &str) -> Option<usize>
fn input_index(&self, name: &str) -> Option<usize>
The index of a named input among Self::input_names, the
coordinates first: what Self::set_input_at takes.
Sourcefn set_input_at(&mut self, index: usize, value: Value) -> Result<(), WriteError>
fn set_input_at(&mut self, index: usize, value: Value) -> Result<(), WriteError>
Self::set_input by index, for a host that binds the same
inputs every cycle: the name is resolved once, with
Self::input_index, and no lookup runs per write.
Sourcefn output_index(&self, name: &str) -> Option<usize>
fn output_index(&self, name: &str) -> Option<usize>
The index of a named output among Self::output_names: what
Self::pull_at takes.
Sourcefn pull_at(&mut self, index: usize) -> Value
fn pull_at(&mut self, index: usize) -> Value
Self::pull by index, for a host that reads the same outputs
every cycle: the name is resolved once, with
Self::output_index, and no lookup runs per pull.
Sourcefn init(&mut self) -> Result<(), KernelError>
fn init(&mut self) -> Result<(), KernelError>
Initialize the kernel: evaluate every const binding once, in
dependency order, and store its value for the rest of the
kernel’s life.
Every way a kernel comes into existence initializes it: a build, a kernel created from a shared program, a child bound under a parent (after the parent’s values are bound), and a traversal activation (after its tuple is bound). A host calls it again when it wants the consts recomputed from the inputs as they are now, for example after setting externs a const reads. Inputs keep their values; only the consts change.
A const whose expression yields None takes the value the binder
copied from the enclosing scope, so the outer binding stays
visible. A const whose expression fails makes initialization
fail, naming the const; a slow one makes initialization slow.
A shared register with a computed starting value is seeded here
too, in the same order, while nothing has written the register: a
kernel attached to a register another scope declared, and one
initialized again, leave it as it is.
Sourcefn traverse_all(&mut self) -> Result<Vec<TraversalStream>, String>
fn traverse_all(&mut self) -> Result<Vec<TraversalStream>, String>
Open every traversal, in document order.
Sourcefn output_cell(&self, _name: &str) -> Option<Arc<SharedCellInner>>
fn output_cell(&self, _name: &str) -> Option<Arc<SharedCellInner>>
The broadcast cell for a computed output, created on the first ask: a descendant that binds its matching input slot to this cell reads the value each of this kernel’s pulls publishes through it, rather than a copy taken once when the descendant was built (cross_fiber_invalidation.md §3.1).
None when the name is not an output of this kernel. All four
engines have broadcast cells: the interpreter seeds one per
output at construction, and the closure tier, native, and pure
native make them on demand, so a compiled program with no
descendant bound to it allocates none (engines.md §3.6).
Sourcefn output_modifier(&self, _name: &str) -> BindingModifier
fn output_modifier(&self, _name: &str) -> BindingModifier
The binding modifier a named output was declared with — const,
shared, final, or none. A binder reads it to decide how a
descendant takes the output: a const is effectively fixed for
the scope’s life and is value-copied, where a computed output is
bound to its broadcast cell (scope_model.md §4).
NONE for a name this kernel does not declare.
Sourcefn cells_in_scope(&self) -> Vec<SharedCellEntry>
fn cells_in_scope(&self) -> Vec<SharedCellEntry>
Every cell a descendant of this kernel could bind to: the ones
its own shared slots hold, plus the ones it carries forward
for a descendant without holding a slot for them itself. The
second kind is why an ancestral shared reaches a grandchild
whose parent’s program never names it.
Self::shared_cells is the first kind alone.
Sourcefn set_transit_cells(&mut self, _cells: Vec<SharedCellEntry>)
fn set_transit_cells(&mut self, _cells: Vec<SharedCellEntry>)
Carry cells forward for this kernel’s descendants. The binder
writes what the parent had and this kernel holds no slot for.
Sourcefn scope_coordinates(&self) -> &[ScopeCoord]
fn scope_coordinates(&self) -> &[ScopeCoord]
This kernel’s place in the comprehension nest its scope was built under, outermost last: a child’s path is its own followed by its parent’s. Empty for a root, which is every kernel a host compiles rather than binds, so the compiled engines answer empty until one is bound under a parent.
Sourcefn extend_scope_coordinates(&mut self, _outer: &[ScopeCoord])
fn extend_scope_coordinates(&mut self, _outer: &[ScopeCoord])
Append outer to this kernel’s own scope-coordinate path, which
the binder does once the child’s inputs are in. A no-op on an
engine that keeps no path.
Sourcefn input_port_type(&self, _name: &str) -> Option<PortType>
fn input_port_type(&self, _name: &str) -> Option<PortType>
The declared type of a named input slot, coordinates included. The binder reads it to adapt a value the parent supplies into the type the child’s slot declares.
Sourcefn bind_input_cell(&mut self, _name: &str, _cell: Arc<SharedCellInner>) -> bool
fn bind_input_cell(&mut self, _name: &str, _cell: Arc<SharedCellInner>) -> bool
Bind the named input slot to cell, whether or not the slot was
built as a shared register, and answer whether it was bound.
This is what a parent does to a child, not what a host does to
two kernels. A child declares its imports extern; it is the
parent binding it that decides one of them reads a register
rather than a copied value. Self::attach_shared_cell is the
host’s operation and refuses a slot that is not already a
register on both sides, which is the right answer for joining
two kernels and the wrong one for building a child.
Sourcefn instance_hash(&self, ancestors: &[&dyn Kernel]) -> [u8; 32]
fn instance_hash(&self, ancestors: &[&dyn Kernel]) -> [u8; 32]
The instance hash of this kernel’s program under ancestors,
innermost first (scope_model.md §8.2): what
PolydatProgram::instance_hash
gives for the same programs, whichever engines the kernels are on.
Sourcefn is_equivalent_to(&self, other: &dyn Kernel) -> bool
fn is_equivalent_to(&self, other: &dyn Kernel) -> bool
Whether other runs the same program (scope_model.md §8.3): their
canonical hashes are equal, whichever engines the two are on.
Sourcefn is_subset_of(&self, parent: &dyn Kernel) -> bool
fn is_subset_of(&self, parent: &dyn Kernel) -> bool
Whether this kernel’s program adds nothing parent’s does not
already supply (scope_model.md §8.3): it is equivalent to
parent, or it outputs nothing but its own inputs and every
input it declares parent declares too. The answer
PolydatProgram::is_subset_of
gives for the same programs, on any engines.
Sourcefn as_interpreter(&self) -> Option<&PolydatKernel>
fn as_interpreter(&self) -> Option<&PolydatKernel>
Sourcefn as_interpreter_mut(&mut self) -> Option<&mut PolydatKernel>
fn as_interpreter_mut(&mut self) -> Option<&mut PolydatKernel>
Self::as_interpreter, mutably.
Dyn Compatibility§
This trait is dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".