use std::sync::atomic::{AtomicU64, Ordering};
use dashmap::DashMap;
use dashmap::mapref::one::Ref;
use crate::process::Process;
use crate::process::heap::DEFAULT_HEAP_SIZE;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct ProcessHandle {
pid: u64,
}
impl ProcessHandle {
#[must_use]
pub const fn pid(self) -> u64 {
self.pid
}
}
#[derive(Debug)]
pub struct ProcessTable {
next_pid: AtomicU64,
processes: DashMap<u64, ProcessHandle>,
}
impl ProcessTable {
#[must_use]
pub fn new() -> Self {
Self {
next_pid: AtomicU64::new(0),
processes: DashMap::new(),
}
}
pub fn spawn(&self) -> u64 {
let pid = self.next_pid.fetch_add(1, Ordering::Relaxed);
let process = Process::new(pid, DEFAULT_HEAP_SIZE);
let handle = ProcessHandle { pid: process.pid() };
self.processes.insert(pid, handle);
pid
}
pub fn spawn_with_pid(&self, pid: u64) {
let handle = ProcessHandle { pid };
self.processes.insert(pid, handle);
}
#[must_use]
pub fn get(&self, pid: u64) -> Option<Ref<'_, u64, ProcessHandle>> {
self.processes.get(&pid)
}
pub fn remove(&self, pid: u64) -> Option<ProcessHandle> {
self.processes.remove(&pid).map(|(_pid, handle)| handle)
}
#[must_use]
pub fn len(&self) -> usize {
self.processes.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.processes.is_empty()
}
}
impl Default for ProcessTable {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::{ProcessHandle, ProcessTable};
#[test]
fn new_table_is_empty() {
let table = ProcessTable::new();
assert!(table.is_empty());
assert_eq!(table.len(), 0);
}
#[test]
fn spawn_assigns_sequential_pids_from_zero() {
let table = ProcessTable::new();
let first = table.spawn();
let second = table.spawn();
assert_eq!(first, 0);
assert_eq!(second, 1);
assert_ne!(first, second);
}
#[test]
fn get_returns_handle_for_spawned_process() {
let table = ProcessTable::new();
let pid = table.spawn();
assert_eq!(table.get(pid).as_deref(), Some(&ProcessHandle { pid }));
}
#[test]
fn get_returns_none_for_missing_process() {
let table = ProcessTable::new();
assert!(table.get(99).is_none());
}
#[test]
fn removed_pids_are_not_reused() {
let table = ProcessTable::new();
let first = table.spawn();
assert_eq!(table.remove(first), Some(ProcessHandle { pid: first }));
let second = table.spawn();
assert!(table.get(first).is_none());
assert_eq!(second, 1);
assert_eq!(
table.get(second).as_deref(),
Some(&ProcessHandle { pid: second })
);
}
}