Skip to main content

harmos_runtime/operations/
lane.rs

1/* ***
2 *
3 * Harmos
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 *
7 * ***
8 */
9
10/* === Modules === */
11
12/* === Re-Exports === */
13
14/* === Imports === */
15
16// Standard Library Imports
17use std::collections::BTreeMap;
18use std::sync::Arc;
19// Third Party Imports
20use tokio::sync::Semaphore;
21// Local Imports
22
23/* === Constants === */
24
25// The lane resident controls run in: unbounded, so a control that runs until
26// cancelled never holds a slot another call is waiting for.
27pub(crate) const RESIDENT: &str = "harmos.resident";
28
29/* === Macros === */
30
31/* === Type Aliases === */
32
33/* === Structs === */
34
35#[derive(Clone, Copy, Debug, PartialEq, Eq)]
36pub struct Capacity {
37    width: usize,
38}
39
40#[derive(Clone, Copy, Debug, PartialEq, Eq)]
41pub struct LaneSettings {
42    pub capacity: Capacity,
43}
44
45#[derive(Clone, Debug, Default)]
46pub struct LaneSet {
47    entries: Vec<(&'static str, Capacity)>,
48}
49
50#[derive(Clone)]
51pub(crate) struct Admission {
52    name: &'static str,
53    width: usize,
54    semaphore: Arc<Semaphore>,
55}
56
57pub(crate) struct Lanes {
58    lanes: BTreeMap<&'static str, Admission>,
59}
60
61/* === Enums === */
62
63/* === Traits === */
64
65pub trait Lane: Copy + Eq + Send + Sync + 'static {
66    fn catalog() -> LaneSet;
67    fn default_lane() -> Self;
68    fn name(&self) -> &'static str;
69    fn capacity(&self) -> Capacity;
70}
71
72/* === Implementations === */
73
74impl Capacity {
75    pub const fn serial() -> Self {
76        Self { width: 1 }
77    }
78
79    pub const fn concurrent(width: usize) -> Self {
80        Self { width }
81    }
82
83    pub const fn unbounded() -> Self {
84        Self { width: usize::MAX }
85    }
86
87    pub(crate) const fn width(self) -> usize {
88        self.width
89    }
90}
91
92impl LaneSet {
93    pub fn declared(entries: impl IntoIterator<Item = (&'static str, Capacity)>) -> Self {
94        Self { entries: entries.into_iter().collect() }
95    }
96
97    pub(crate) fn entries(self) -> Vec<(&'static str, Capacity)> {
98        self.entries
99    }
100
101    pub(crate) fn contains(&self, lane: &str) -> bool {
102        self.entries.iter().any(|(name, _)| *name == lane)
103    }
104
105    pub(crate) fn configure(&mut self, lane: &str, capacity: Capacity) -> bool {
106        let Some((_, configured)) = self.entries.iter_mut().find(|(name, _)| *name == lane) else {
107            return false;
108        };
109        *configured = capacity;
110        true
111    }
112}
113
114impl Admission {
115    pub(crate) fn name(&self) -> &'static str {
116        self.name
117    }
118
119    pub(crate) fn bounded(&self) -> bool {
120        self.width != usize::MAX
121    }
122
123    pub(crate) fn semaphore(&self) -> Arc<Semaphore> {
124        Arc::clone(&self.semaphore)
125    }
126}
127
128impl Lanes {
129    pub(crate) fn freeze(declared: LaneSet) -> Result<Self, String> {
130        let mut lanes = BTreeMap::from([(
131            RESIDENT,
132            Admission {
133                name: RESIDENT,
134                width: usize::MAX,
135                semaphore: Arc::new(Semaphore::new(Semaphore::MAX_PERMITS)),
136            },
137        )]);
138
139        for (name, capacity) in declared.entries() {
140            if name.is_empty() {
141                return Err("a lane name must not be empty".to_owned());
142            }
143            if capacity.width() == 0 {
144                return Err(format!("lane `{name}` must admit at least one job"));
145            }
146            let admission = Admission {
147                name,
148                width: capacity.width(),
149                semaphore: Arc::new(Semaphore::new(capacity.width().min(Semaphore::MAX_PERMITS))),
150            };
151            if lanes.insert(name, admission).is_some() {
152                return Err(format!("lane `{name}` is declared more than once"));
153            }
154        }
155
156        Ok(Self { lanes })
157    }
158
159    pub(crate) fn get(&self, name: &str) -> Option<Admission> {
160        self.lanes.get(name).cloned()
161    }
162}
163
164/* === Public Functions === */
165
166/* === Private Functions === */
167
168/* === Tests === */