Skip to main content

active_rpc/
lane.rs

1//! Lanes: independent concurrent execution contexts inside one agent.
2//!
3//! A lane owns a budget and a set of channels and capabilities. The kernel
4//! schedules lanes cooperatively: each step credits one ready job to one lane,
5//! round-robin, so two lanes interleave deterministically and a lane that
6//! exhausts its budget cannot starve another.
7
8use std::collections::{BTreeSet, VecDeque};
9
10use crate::budget::Budget;
11use crate::error::{Error, Result};
12
13/// Identity of a lane.
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
15pub struct LaneId(pub u64);
16
17impl std::fmt::Display for LaneId {
18    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
19        write!(f, "lane{}", self.0)
20    }
21}
22
23/// A lane's state.
24#[derive(Debug, Clone)]
25pub struct Lane {
26    /// Kernel-assigned identifier.
27    pub id: LaneId,
28    /// The name the caller chose.
29    pub name: String,
30    /// The lane's own call and byte ceiling.
31    pub budget: Budget,
32    /// Calls issued by this lane that have not completed.
33    pub pending: u32,
34    /// Completed calls.
35    pub completed: u64,
36    /// Channels this lane is subscribed to.
37    pub channels: BTreeSet<u64>,
38    /// Capabilities minted for this lane.
39    pub caps: BTreeSet<u128>,
40    /// True once the lane has been destroyed; further calls are refused.
41    pub closed: bool,
42    /// Per-lane ready queue of job ids.
43    pub ready: VecDeque<u64>,
44}
45
46impl Lane {
47    /// Create an open lane with its own budget.
48    pub fn new(id: LaneId, name: impl Into<String>, budget: Budget) -> Self {
49        Self {
50            id,
51            name: name.into(),
52            budget,
53            pending: 0,
54            completed: 0,
55            channels: BTreeSet::new(),
56            caps: BTreeSet::new(),
57            closed: false,
58            ready: VecDeque::new(),
59        }
60    }
61
62    /// Attenuate a lane's budget; refuses widening.
63    pub fn attenuate(&mut self, budget: Budget) -> Result<()> {
64        if !budget.is_attenuation_of(&self.budget) {
65            return Err(Error::Escalation("lane budget grows"));
66        }
67        self.budget = budget;
68        Ok(())
69    }
70
71    /// Record a completed call against the lane.
72    pub fn complete(&mut self) {
73        self.completed += 1;
74        self.pending = self.pending.saturating_sub(1);
75    }
76
77    /// Close the lane.
78    pub fn destroy(&mut self) {
79        self.closed = true;
80        self.ready.clear();
81        self.pending = 0;
82    }
83
84    /// Whether the lane may issue another call under its budget.
85    ///
86    /// Pending calls count: queueing a thousand calls before `run()` must not
87    /// let a lane outspend its grant.
88    pub fn has_capacity(&self) -> bool {
89        !self.closed && self.completed.saturating_add(self.pending as u64) < self.budget.max_calls
90    }
91}