//! What a process may hold: frames and kernel objects. A child's limits //! are carved from its creator's unused budget and go back when it dies, //! so the limits of all live processes never add up to more than init got. use crate::memory::quota::Quota; pub use types::budget::Limits; #[cfg(test)] #[path = "budget_tests.rs"] mod tests; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Budget { pub pages: Quota, pub endpoints: Quota, pub notifications: Quota, pub regions: Quota, } impl Budget { pub const EMPTY: Self = Self::new(Limits { pages: 0, endpoints: 0, notifications: 0, regions: 0, }); pub const fn new(limits: Limits) -> Self { Self { pages: Quota::new(limits.pages), endpoints: Quota::new(limits.endpoints), notifications: Quota::new(limits.notifications), regions: Quota::new(limits.regions), } } pub fn limits(&self) -> Limits { Limits { pages: self.pages.limit(), endpoints: self.endpoints.limit(), notifications: self.notifications.limit(), regions: self.regions.limit(), } } fn parts(&mut self) -> [&mut Quota; 4] { [ &mut self.pages, &mut self.endpoints, &mut self.notifications, &mut self.regions, ] } /// Carves `child` out of the unused budget: all of it or nothing. pub fn carve(&mut self, child: Limits) -> bool { let amounts = amounts(child); let fits = self.parts().iter().zip(amounts).all(|(q, n)| { q.limit() .checked_sub(n) .is_some_and(|rest| rest >= q.used()) }); if fits { for (q, n) in self.parts().into_iter().zip(amounts) { q.carve(n); } } fits } pub fn refund(&mut self, child: Limits) { for (q, n) in self.parts().into_iter().zip(amounts(child)) { q.refund(n); } } } fn amounts(l: Limits) -> [usize; 4] { [l.pages, l.endpoints, l.notifications, l.regions] }