Skip to content

Releases: cedrivard/embassy-supervisor

embassy-supervisor 0.10.0

Choose a tag to compare

Adds a graph-level default executor, fault injection, and a stricter local
resource invariant. Companion crate versions: macros 0.10.0, syntax 0.4.0,
tools 0.5.0.

Added

  • default executor NAME; sets a default executor for all task: and spawn:
    nodes and pools. An explicit executor: override still wins. One per graph,
    not #[cfg]-gable. See the README for usage on interrupt tiers.
  • fault-inject feature adds TaskNode::inject(Fault), clear_fault(), and
    fault() with Fault::{Stall, Wedge, Crash, Hog(Duration)}. The task: shell
    intercepts polls for stall, crash, and hog faults. Wedge hides shutdown
    requests and swallows acks until cleared or replaced. Hand-written spawn:
    tasks only support wedge. Adds one AtomicU8, one AtomicU32, and one
    AtomicWaker per node. Off by default.

Changed

  • local resources are now local to the node's executor, not the supervisor.
    The local + executor: rejection is removed; instead, the macro checks that
    every local slot resolves to one executor. Bring-up now peeks the slot
    without moving the value.
  • wait_shutdown() re-checks the request after a wake when fault-inject is
    enabled. Unchanged otherwise.

Fixed

  • Fixed docs: the Send bound applies to spawn arguments, not the routed future
    itself. SpawnerSlot's rustdoc now explains this with a compile_fail
    doctest.

embassy-supervisor-tools 0.5.0

Choose a tag to compare

embassy-supervisor-tools-v0.5.0

embassy-supervisor-tools 0.5.0

embassy-supervisor-syntax 0.4.0

Choose a tag to compare

Adds default executor NAME; and drops the local + executor: rejection.
Requires embassy-supervisor-macros 0.10.0 and embassy-supervisor-tools 0.5.0.

Added

  • default executor NAME; item, applied to eligible nodes and pools with
    executor_defaulted = true. Errors if it has #[cfg], appears in a fragment, or is
    duplicated.

Removed

  • Rejection of local combined with executor:. The macro crate now checks that every
    local slot resolves to one executor.

embassy-supervisor-macros 0.10.0

Choose a tag to compare

Pins embassy-supervisor-syntax 0.4.0.

Added

  • default executor NAME;: the default executor is seen by all downstream checks, and
    fragments declaring one are rejected at the fragment site as well as at compose.
  • fault-inject feature: task: shells wrap the worker in Injected and mark the node
    shelled. exit: provides the wrapped Option, preserving the "unreachable call"
    diagnostic for diverging workers.
  • Reject a local slot declared on two executors, naming both declarers and their
    tiers, including a provides: whose node tier differs.

Changed

  • Local slot is_filled now peeks through the cell pointer under the lock, so the
    supervisor's executor never moves the !Send value even bytewise.
  • Updated the executor: routing comment to state the Send bound is on the spawn
    arguments, not the future.

embassy-supervisor v0.9.0

Choose a tag to compare

One waker per budget slot, and the crates point at the documentation site.

  • Breaking (hand-written Divisible only): Divisible::register now takes
    the slot to park on. Budget keeps one AtomicWaker per slot and only wakes
    slots whose grant changed during rebalance.

  • Corrected the veto feature's cost note: 4 bytes per Coupling without
    coupling-observe (16 to 20 bytes on thumbv8m), zero with it. The budget
    note now states the budget size and the 8-byte claims slice per node.

embassy-supervisor v0.8.1

Choose a tag to compare

Ships with embassy-supervisor-macros 0.9.1 (pinned by exact version) on
embassy-supervisor-syntax 0.3.1; embassy-supervisor-tools 0.4.1 scans
cfg_attr-applied #[dataflow] fns.

Fixed

  • #[dataflow_bundle] no longer drops a member fn whose #[dataflow] is
    applied through cfg_attr: the bundle macro reads the module's attributes
    before the compiler applies cfg_attr, so the layer is unwrapped there.
    The tools' scanners see the same form now, so supervisor-mermaid draws
    such a fn's derived edges instead of warning that it found no #[dataflow].

Changed

  • The trace hooks follow the executor's next API. embassy-executor's git
    main (the release after 0.10) replaces the seven _embassy_trace_* symbols
    with a raw::trace::Trace impl registered through trace_impl!, and passes
    ExecutorId/TaskId newtypes instead of u32s. RUSTFLAGS="--cfg embassy_supervisor_trace_v2" builds the trace layer against it; the
    recorders keep their u32 keys, ids narrowing through the new
    trace::task_key / trace::executor_key (cfg-only). The hook bodies moved
    from the proc-macro into __sv_trace_hooks!, a hidden macro of this crate
    that supervisor_graph! expands under trace-hooks, so which API they
    speak is decided by this crate's build. No change on embassy-executor 0.10.
  • CI's trace vs embassy git (canary) job now also runs the trace test
    suite, and patches embassy-executor-timer-queue to git together with the
    executor: with only the executor patched, embassy-time's queue utils kept a
    second copy of the timer-queue crate, sized differently, and every timeout
    in the wedged-task tests silently never fired.

embassy-supervisor-tools 0.4.1

Choose a tag to compare

Pins embassy-supervisor-syntax = "=0.3.1".

Fixed

  • A fn whose #[dataflow] is applied through cfg_attr was reported as
    "no #[dataflow] fn among the scanned files" and its derived edges left
    undrawn. supervisor-mermaid and supervisor-lint now scan it; the
    cfg_attr predicate draws on its edges like a #[cfg] on the fn.

embassy-supervisor-syntax 0.3.1

Choose a tag to compare

Additive. embassy-supervisor-macros = "=0.9.1" and embassy-supervisor-tools
0.4.1 are the matching consumers.

Added

  • dataflow_attr(&Attribute) -> Option<(Attribute, Option<String>)>: the
    dataflow attribute an attribute carries once cfg_attr is applied, plus
    the predicate it sits under (feature="grown"; nested layers fold into
    all(..)).

Fixed

  • is_dataflow_attr and scan_dataflow now see a #[dataflow] written as
    #[cfg_attr(pred, dataflow)]. The compiler expands that form, so the
    tables were emitted while the textual scan skipped the fn. Accesses found
    this way carry pred in cfgs, as a #[cfg] on the fn would.
  • Access::cfgs records a predicate once when the fn and the call site
    repeat it, instead of twice.

embassy-supervisor-macros 0.9.1

Choose a tag to compare

Rides embassy-supervisor-syntax = "=0.3.1"; ships with embassy-supervisor
0.8.1, whose __sv_trace_hooks! the trace-hooks expansion now calls.

Changed

  • trace-hooks expands the supervisor's __sv_trace_hooks! at the unnamed
    graph's declaration site instead of spelling out the seven no_mangle
    _embassy_trace_* functions itself: the hook bodies are the supervisor's,
    so which executor hook API they speak (0.10's symbols, or the Trace impl
    embassy git main takes under --cfg embassy_supervisor_trace_v2) is
    decided by that crate's build. Same expansion result on embassy-executor 0.10.

Fixed

  • #[dataflow_bundle] sees its members' attributes as written, before the
    compiler applies cfg_attr. A member declared #[cfg_attr(pred, dataflow)]
    was skipped, its write or read missing from the bundle while the fn's own
    tables carried it. The bundle now unwraps the layer through the syntax
    crate's dataflow_attr.

embassy-supervisor v0.8.0

Choose a tag to compare

Six things a graph could not say about its couplings, and now can.

Ships with embassy-supervisor-macros 0.9.0 (pinned by exact version) on
embassy-supervisor-syntax 0.3.0; embassy-supervisor-tools 0.4.0 reads the
new markers.

Added

  • The other half of demand-start. Backed counts its readers: open
    hands back an Open<T> guard (Deref to the signal, signal() for the
    'static reference), Backed::openers() is the count, and
    Backed::unwatched(cooldown) resolves once it has sat at zero for the
    whole cooldown — a reader arriving inside it restarts the clock.
    TaskNode::retire(&SIG, cooldown) is the producer's verb: it waits that
    out, withdraws readiness (so a late opener waits instead of reading a
    producer on its way out, and an opener admitted just before cancels the
    retirement), and with control requests its own Deactivate. The next
    open re-activates it, as before.
  • divisible resources (feature budget). resources: [POWER: divisible] declares a Budget<K> the graph sizes to its holders (one slot
    per node, one per pool member). Each holder's shell receives a Claimant
    bound to its slot (want, grant, wait_grant_change); an allocator
    provides the capacity and divides it with a BudgetPolicyFairShare
    or ShrinkFastGrowSlow — on wait_change(); a change landing while
    rebalance runs re-arms wait_change, so a looping allocator re-divides
    at once. The supervisor releases a
    holder's share when it stops, including a holder that misses its shutdown
    ack; a Pause park keeps it. An unprovided budget is ResourceMissing;
    provides: may name one.
  • Distributed veto (feature veto). writes: [crate::TRIP veto] gives a
    writer one contributor slot of a VetoGate<N>: asserted while any
    contributor holds it, released only once all let go. The macro numbers the
    writers per gate and emits a compile-time check that the target is a
    VetoGate with a slot for each. node.veto(&TRIP) yields a Veto that
    moves only its own bit; the actuator parks on wait_asserted /
    wait_released. A stopped writer's bit stays asserted; release is explicit.
    Under coupling-observe the gate counts its flips, so veto observed beat
    is a heartbeat.
  • shared serialized: a compile-time rule that every holder of the slot
    runs on one executor — priority ceiling by construction for a serialized
    link, since embassy can neither boost a holder nor migrate a task. No
    feature, no runtime cost.
  • Stamped<T> (feature coupling): w() stamps when a write begins and
    hands the inner signal over, so a consumer can ask age(), is_fresh(max)
    or read_fresh(max); the token forwards to the inner signal. The read-side
    half of write freshness; the README draws the line to value validity, which
    stays the consumer's.
  • The README states the private-module recipe for gated statics and its
    limit ("bypass requires going out of your way"); supervisor-lint --only public-gate reports a Backed/Leased/VetoGate static that is not
    private.

Changed

  • Breaking: TaskNode::open returns T::Handle instead of &'static T
    an Open<T> for Backed. A reader that chained a 'static-taking call
    onto the result (node.open(&X).await.receiver()) binds the guard first;
    one that needs the 'static reference itself calls .signal(). Gated
    gains type Handle: Deref and fn admit(&'static self) -> Self::Handle; a
    wrapper that only waits sets Handle = &'static Self.
  • retire and veto are built-in write verbs of the #[dataflow] scanner.
    A body that already called a node method by either name is now rewritten.
  • ack_dropped also wakes openers parked on the stopped producer's gate (so
    they request the next start without waiting out the retry) and releases the
    node's divisible shares; FaultKind::ShutdownTimeout releases them too.
  • Signal entry markers (observed, beat, veto) parse in any order;
    via <expr> still qualifies observed and ends the entry.

Migration notes in the README, full details in the CHANGELOG.