Skip to main content

ProcessBroker

Struct ProcessBroker 

Source
pub struct ProcessBroker {
    cache: Mutex<HashMap<String, ProcessStatus>>,
    mps_broker: Option<Arc<Broker>>,
}

Fields§

§cache: Mutex<HashMap<String, ProcessStatus>>

Broker-owned cache — the single source future callers read from, rather than each composing blockcontroller/process_tracker themselves. parking_lot::Mutex, not an async mutex: unlike the credential broker’s Entry::lock (which guards an actual network refresh call and must not block a tokio worker thread while held), this cache’s critical section is two cheap in-memory map reads plus a struct build — synchronous and fast, the same reasoning process_tracker::registry::AgentProcessRegistry already uses parking_lot::Mutex for. Coarse-grained (one mutex, not per-block_id): contention is expected to be negligible for the same reason; narrow the granularity later if profiling says otherwise. If a later phase adds a genuinely blocking liveness check (report §3.0.3 point 1 — e.g. a real health-probe round-trip), that phase should revisit this as an async lock at the same time, not before.

§mps_broker: Option<Arc<Broker>>

Implementations§

Source§

impl ProcessBroker

Source

pub fn new(mps_broker: Option<Arc<WpsBroker>>) -> Self

Source

pub fn status(&self, block_id: &str) -> ProcessStatus

Recompute and return one block’s status. Single-flight-guarded by holding the cache lock across the entire read-compare-write sequence, not just the write — compute_status must run inside the critical section, not before it. reagent P1 on #2273 caught this: with the read outside the lock, two concurrent callers (e.g. an Agent pane mount and Swarm’s refresh landing near-simultaneously) could interleave so the thread that read first but wrote second overwrites a fresher value the other thread already wrote — the cache regresses to a stale snapshot even though a newer read already happened. Locking around the whole sequence (safe here because compute_status is synchronous and cheap — see the cache field’s own doc comment) makes “last writer wins” and “last reader wins” the same thing, which is what single-flight is supposed to guarantee.

Source

pub fn list(&self) -> Vec<ProcessStatus>

The missing batch query (report §5.4) — replaces both agent.tracked-blocks’s .chain() of two independent registries and the N sequential single-block GetControllerStatus calls Swarm’s subscribeToBlockStatuses issues today with one call. Discovery source is blockcontroller::get_all_controllers — authoritative for every controller type, closing the coverage gap process_tracker alone has for shell/acp blocks (report §1).

Returns EVERY controller-backed block — plain shell/cmd terminals included, not just agent panes. Use list_agent_panes() for the agent-scoped subset (which is what agent.tracked-blocks actually needs — see that method’s doc comment).

Source

pub fn list_agent_panes(&self) -> Vec<ProcessStatus>

Agent-scoped subset of list(). agent.tracked-blocks’s original (pre-broker) implementation was implicitly agent-only — the two registries it unioned (process_tracker, populated only by agent-CLI controller types, and reactive’s registration list, populated only via register_agent) never included a plain terminal. list() alone loses that scoping because get_all_controllers() is controller-type-agnostic by design (reagent/codex P1 on #2273 — an earlier version of this PR fed list() straight into agent.tracked-blocks, which made every open terminal pane show up in Swarm mislabeled as an “Agent”). See ProcessStatus::is_agent for the exact classification rule.

Source

pub fn forget(&self, block_id: &str)

Drop a block from the cache. Call on pane close so list() doesn’t keep serving a stale entry for a block whose controller is gone — compute_status would report Lifecycle::Unknown for it on the next status() call regardless, but a closed pane shouldn’t appear in list() (sourced from get_all_controllers(), which already won’t include it) while a stale cache entry lingers unreachable.

Source

fn emit_changed(&self, status: &ProcessStatus)

Source

pub fn emit_tracked_blocks_changed(&self)

Announce that list()/list_agent_panes() membership may have changed — i.e. blockcontroller::register_controller/ delete_controller just ran. Unscoped (no block:<id>, unlike emit_changed above): this is “recompute your list,” not a status update for one already-known block, so there is no single block scope to attach it to.

Exists because agent.tracked-blocks’s only data source (CONTROLLER_REGISTRY) never announced its own writes — Swarm’s agent.tracked-blocks list was refreshed only by two different, independently-timed mechanisms standing in as proxies (process_tracker’s 2s poll-and-diff loop, and reactive‘s own registration events), neither directly coupled to when a controller actually got registered or removed. That gap let restored agent panes’ controllers register successfully (confirmed via resync_controller’s own logging) while Swarm’s client-side list — fetched once at mount, refreshed only by those two proxies — stayed stale indefinitely for any block whose proxy signal didn’t happen to fire. See docs/reports/REPORT_SWARM_MOUNT_DEPENDENT_TRACKING_GAP_2026_09_15.md.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<T> Downcast for T
where T: Any,

§

fn into_any(self: Box<T>) -> Box<dyn Any>

Convert Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.
§

fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>

Convert Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be further downcast into Rc<ConcreteType> where ConcreteType implements Trait.
§

fn as_any(&self) -> &(dyn Any + 'static)

Convert &Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &Any’s vtable from &Trait’s.
§

fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)

Convert &mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &mut Any’s vtable from &mut Trait’s.
§

impl<T> DowncastSync for T
where T: Any + Send + Sync,

§

fn into_any_arc(self: Arc<T>) -> Arc<dyn Any + Send + Sync> ⓘ

Convert Arc<Trait> (where Trait: Downcast) to Arc<Any>. Arc<Any> can then be further downcast into Arc<ConcreteType> where ConcreteType implements Trait.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<A, B, T> HttpServerConnExec<A, B> for T
where B: Body,

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
§

impl<T> PolicyExt for T
where T: ?Sized,

§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns [Action::Follow] only if self and other return Action::Follow. Read more
§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns [Action::Follow] if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more