use std::mem::replace;
use std::pin::Pin;
use std::sync::Mutex;
use super::ProcessData;
#[derive(Debug)]
pub(super) struct RunQueue {
root: Mutex<Branch>,
}
type Branch = Option<Box<Node>>;
#[derive(Debug)]
struct Node {
process: Pin<Box<ProcessData>>,
left: Branch,
right: Branch,
}
impl RunQueue {
pub(super) fn empty() -> RunQueue {
RunQueue {
root: Mutex::new(None),
}
}
pub(super) fn len(&self) -> usize {
match &mut *self.root.lock().unwrap() {
Some(branch) => branch.len(),
None => 0,
}
}
pub(super) fn has_process(&self) -> bool {
self.root.lock().unwrap().is_some()
}
pub(super) fn add(&self, process: Pin<Box<ProcessData>>) {
let mut next_node = &mut *self.root.lock().unwrap();
loop {
match next_node {
Some(node) => {
if node.process < process {
next_node = &mut node.left
} else {
next_node = &mut node.right
}
}
None => {
*next_node = Some(Node::new(process));
return;
}
}
}
}
pub(super) fn remove(&self) -> Option<Pin<Box<ProcessData>>> {
let mut next_node = &mut *self.root.lock().unwrap();
loop {
match next_node {
Some(node) if node.left.is_none() => {
let right_node = node.right.take();
return replace(next_node, right_node).map(|node| node.process);
}
Some(node) => {
next_node = &mut node.left;
}
None => return None,
}
}
}
}
impl Node {
fn new(process: Pin<Box<ProcessData>>) -> Box<Node> {
Box::new(Node {
process,
left: None,
right: None,
})
}
fn len(&self) -> usize {
let mut count = 1; if let Some(branch) = self.left.as_ref() {
count += branch.len();
}
if let Some(branch) = self.right.as_ref() {
count += branch.len();
}
count
}
}
#[cfg(test)]
mod tests {
use std::mem::size_of;
use std::pin::Pin;
use std::time::Duration;
use crate::process::{Process, ProcessId, ProcessResult};
use crate::spawn::options::Priority;
use crate::RuntimeRef;
use super::{Node, ProcessData, RunQueue};
#[test]
fn size_assertions() {
assert!(size_of::<RunQueue>() <= 24);
assert_eq!(size_of::<Node>(), 24);
}
struct TestProcess;
impl Process for TestProcess {
fn name(&self) -> &'static str {
"TestProcess"
}
fn run(self: Pin<&mut Self>, _: &mut RuntimeRef, _: ProcessId) -> ProcessResult {
ProcessResult::Complete
}
}
fn add_process(run_queue: &RunQueue, fair_runtime: Duration) -> ProcessId {
let mut process = Box::pin(ProcessData::new(Priority::NORMAL, Box::pin(TestProcess)));
process.set_fair_runtime(fair_runtime);
let pid = process.as_ref().id();
run_queue.add(process);
pid
}
macro_rules! runqueue_test {
(
// Name of the test.
$name: ident,
// List of processes to add in order. Value passed in the value of
// the duration of the process' runtime (used in ordering in the
// `RunQueue`).
add_order: [ $($add: expr),* ],
remove_order: [ $($remove: expr),* ],
) => {
#[test]
fn $name() {
let run_queue = RunQueue::empty();
assert!(!run_queue.has_process());
let pids = [
$( add_process(&run_queue, Duration::from_secs($add)), )*
];
assert!(run_queue.has_process());
$(
let process = run_queue.remove().expect("failed to remove process");
assert_eq!(process.as_ref().id(), pids[$remove - 1]);
)*
assert!(!run_queue.has_process());
assert!(run_queue.remove().is_none());
}
};
}
#[test]
fn tree_empty() {
let run_queue = RunQueue::empty();
assert!(!run_queue.has_process());
assert!(run_queue.remove().is_none());
}
runqueue_test!(
tree_1_full,
add_order: [1],
remove_order: [1],
);
runqueue_test!(
tree_2_right,
add_order: [1, 2],
remove_order: [1, 2],
);
runqueue_test!(
tree_2_left,
add_order: [2, 1],
remove_order: [2, 1],
);
runqueue_test!(
tree_2_full,
add_order: [2, 1, 3],
remove_order: [2, 1, 3],
);
runqueue_test!(
tree_3_left_leaning,
add_order: [3, 2, 1],
remove_order: [3, 2, 1],
);
runqueue_test!(
tree_3_left_right,
add_order: [3, 1, 2],
remove_order: [2, 3, 1],
);
runqueue_test!(
tree_3_right_leaning,
add_order: [1, 2, 3],
remove_order: [1, 2, 3 ],
);
runqueue_test!(
tree_3_right_left,
add_order: [1, 3, 2],
remove_order: [1, 3, 2],
);
runqueue_test!(
tree_3_left_filled,
add_order: [4, 2, 1, 3],
remove_order: [3, 2, 4, 1],
);
runqueue_test!(
tree_3_left_leaning_and_right,
add_order: [3, 2, 1, 4],
remove_order: [3, 2, 1, 4],
);
runqueue_test!(
tree_3_left_right_and_right,
add_order: [3, 1, 4, 2],
remove_order: [2, 4, 1, 3],
);
runqueue_test!(
tree_3_left_and_right_left,
add_order: [2, 1, 4, 3],
remove_order: [2, 1, 4, 3],
);
runqueue_test!(
tree_3_right_leaning_and_left,
add_order: [2, 1, 3, 4],
remove_order: [2, 1, 3, 4],
);
runqueue_test!(
tree_3_right_filled,
add_order: [1, 3, 2, 4],
remove_order: [1, 3, 2, 4],
);
runqueue_test!(
tree_3_left_filled_and_right,
add_order: [4, 2, 5, 1, 3],
remove_order: [4, 2, 5, 1, 3],
);
runqueue_test!(
tree_3_left_leftand_right_left,
add_order: [3, 2, 5, 1, 4],
remove_order: [4, 2, 1, 5, 3],
);
runqueue_test!(
tree_3_left_left_and_right_right,
add_order: [3, 2, 4, 1, 5],
remove_order: [4, 2, 1, 3, 5],
);
runqueue_test!(
tree_3_left_right_and_right_left,
add_order: [3, 2, 5, 1, 4],
remove_order: [4, 2, 1, 5, 3],
);
runqueue_test!(
tree_3_left_right_and_right_right,
add_order: [3, 2, 4, 1, 5],
remove_order: [4, 2, 1, 3, 5],
);
runqueue_test!(
tree_3_left_and_right_filled,
add_order: [2, 1, 4, 3, 5],
remove_order: [2, 1, 4, 3, 5],
);
runqueue_test!(
tree_3_left_filled_and_right_left,
add_order: [4, 2, 6, 1, 3, 5],
remove_order: [4, 2, 5, 1, 6, 3],
);
runqueue_test!(
tree_3_left_filled_and_right_right,
add_order: [4, 2, 5, 1, 3, 6],
remove_order: [4, 2, 5, 1, 3, 6],
);
runqueue_test!(
tree_3_left_left_and_right_filled,
add_order: [3, 2, 5, 1, 4, 6],
remove_order: [4, 2, 1, 5, 3, 6],
);
runqueue_test!(
tree_3_left_right_and_right_filled,
add_order: [3, 1, 5, 2, 4, 6],
remove_order: [2, 4, 1, 5, 3, 6],
);
runqueue_test!(
tree_3_full,
add_order: [4, 2, 6, 1, 3, 5, 7],
remove_order: [4, 2, 5, 1, 6, 3, 7],
);
}