use alloc::vec::Vec;
use crate::priority::Priority;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PriorityModel {
ServerDeclared,
ClientPropagated,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PriorityModelPolicy {
pub model: PriorityModel,
pub server_priority: Priority,
}
impl PriorityModelPolicy {
#[must_use]
pub fn effective_priority(&self, propagated: Option<Priority>) -> Priority {
match self.model {
PriorityModel::ClientPropagated => propagated.unwrap_or(self.server_priority),
PriorityModel::ServerDeclared => self.server_priority,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Lane {
pub priority: Priority,
pub static_threads: u32,
pub dynamic_threads: u32,
}
#[derive(Debug, Clone)]
pub struct Threadpool {
pub lanes: Vec<Lane>,
pub stacksize: usize,
pub allow_request_buffering: bool,
pub max_buffered_requests: u32,
}
impl Threadpool {
#[must_use]
pub fn lane_for(&self, priority: Priority) -> Option<&Lane> {
self.lanes
.iter()
.filter(|l| l.priority <= priority)
.max_by_key(|l| l.priority)
.or_else(|| self.lanes.iter().min_by_key(|l| l.priority))
}
#[must_use]
pub fn static_capacity(&self) -> u32 {
self.lanes.iter().map(|l| l.static_threads).sum()
}
}
#[derive(Debug, Clone)]
pub struct ThreadpoolPolicy {
pub pool: Threadpool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PriorityBand {
pub low: Priority,
pub high: Priority,
}
impl PriorityBand {
#[must_use]
pub fn contains(&self, priority: Priority) -> bool {
self.low <= priority && priority <= self.high
}
}
#[derive(Debug, Clone)]
pub struct PriorityBandedConnectionPolicy {
pub bands: Vec<PriorityBand>,
}
impl PriorityBandedConnectionPolicy {
#[must_use]
pub fn band_for(&self, priority: Priority) -> Option<usize> {
self.bands.iter().position(|b| b.contains(priority))
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::panic)]
mod tests {
use super::*;
fn p(v: i16) -> Priority {
Priority::new(v).unwrap()
}
#[test]
fn client_propagated_uses_propagated_priority() {
let pol = PriorityModelPolicy {
model: PriorityModel::ClientPropagated,
server_priority: p(10),
};
assert_eq!(pol.effective_priority(Some(p(99))), p(99));
assert_eq!(pol.effective_priority(None), p(10)); }
#[test]
fn server_declared_ignores_propagated() {
let pol = PriorityModelPolicy {
model: PriorityModel::ServerDeclared,
server_priority: p(10),
};
assert_eq!(pol.effective_priority(Some(p(99))), p(10));
}
#[test]
fn lane_selection_picks_highest_covering() {
let pool = Threadpool {
lanes: alloc::vec![
Lane {
priority: p(0),
static_threads: 2,
dynamic_threads: 0
},
Lane {
priority: p(50),
static_threads: 4,
dynamic_threads: 2
},
Lane {
priority: p(90),
static_threads: 1,
dynamic_threads: 0
},
],
stacksize: 0,
allow_request_buffering: true,
max_buffered_requests: 0,
};
assert_eq!(pool.lane_for(p(60)).unwrap().priority, p(50));
assert_eq!(pool.lane_for(p(90)).unwrap().priority, p(90));
assert_eq!(pool.lane_for(p(10)).unwrap().priority, p(0));
assert_eq!(pool.static_capacity(), 7);
}
#[test]
fn priority_band_selection() {
let pol = PriorityBandedConnectionPolicy {
bands: alloc::vec![
PriorityBand {
low: p(0),
high: p(32)
},
PriorityBand {
low: p(33),
high: p(66)
},
PriorityBand {
low: p(67),
high: p(99)
},
],
};
assert_eq!(pol.band_for(p(10)), Some(0));
assert_eq!(pol.band_for(p(50)), Some(1));
assert_eq!(pol.band_for(p(80)), Some(2));
}
}