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}