#![allow(non_snake_case)]
#![allow(dead_code)]
use crate::ported::hashtable::Hashtable;
use crate::ported::machine::Machine;
use crate::ported::object::Object;
use crate::ported::process::{Process_getPid, Process_makeCommandStr, Process_setPid};
use crate::ported::settings::Settings;
use crate::ported::table::{
Table, Table_add, Table_cleanupRow, Table_compact, Table_done, Table_init, Table_prepareEntries,
};
#[repr(C)]
pub struct ProcessTable {
pub super_: Table,
pub pidMatchList: Option<*mut Hashtable>,
pub totalTasks: u32,
pub runningTasks: u32,
pub userlandThreads: u32,
pub kernelThreads: u32,
}
impl ProcessTable {
pub fn empty() -> ProcessTable {
ProcessTable {
super_: Table::empty(),
pidMatchList: None,
totalTasks: 0,
runningTasks: 0,
userlandThreads: 0,
kernelThreads: 0,
}
}
}
pub fn ProcessTable_init(
this: &mut ProcessTable,
host: *const Machine,
pidMatchList: Option<usize>,
) {
Table_init(&mut this.super_, host);
this.pidMatchList = pidMatchList.map(|h| h as *mut Hashtable);
}
pub fn ProcessTable_done(this: &mut ProcessTable) {
Table_done(&mut this.super_);
}
pub fn ProcessTable_getProcess(
this: &mut ProcessTable,
pid: i32,
constructor: fn(*const Machine) -> Box<dyn Object>,
) -> (bool, usize) {
if let Some(&idx) = this.super_.table.get(&pid) {
debug_assert_eq!(
Process_getPid(
this.super_.rows[idx]
.as_ref()
.unwrap()
.as_process()
.unwrap()
),
pid
);
return (true, idx);
}
let host = this.super_.host;
let mut obj = constructor(host);
debug_assert!(
obj.as_process().unwrap().cmdline.is_none(),
"getProcess: fresh process must have no cmdline"
);
Process_setPid(obj.as_process_mut().unwrap(), pid);
let idx = this.super_.rows.len();
Table_add(&mut this.super_, obj);
(false, idx)
}
pub fn ProcessTable_prepareEntries(this: &mut ProcessTable) {
this.totalTasks = 0;
this.userlandThreads = 0;
this.kernelThreads = 0;
this.runningTasks = 0;
Table_prepareEntries(&mut this.super_);
}
pub fn ProcessTable_iterateEntries(this: &mut ProcessTable) {
ProcessTable_goThroughEntries(this);
}
pub fn ProcessTable_goThroughEntries(_this: &mut ProcessTable) {
todo!("platform ProcessTable_goThroughEntries — /proc scan, filled by linux/ layer")
}
pub fn ProcessTable_cleanupEntries(this: &mut ProcessTable) {
let host: *mut Machine = this.super_.host as *mut Machine;
let settings: *const Settings = unsafe {
(*host)
.settings
.as_ref()
.expect("ProcessTable_cleanupEntries: host->settings is NULL")
as *const Settings
};
let mut dirty_index = this.super_.rows.len();
for i in (0..this.super_.rows.len()).rev() {
{
let p = this.super_.rows[i]
.as_mut()
.expect("ProcessTable_cleanupEntries: NULL row slot")
.as_process_mut()
.expect("ProcessTable_cleanupEntries: row is not a Process");
Process_makeCommandStr(p, unsafe { &*settings });
}
let (st_uid, pid) = {
let p = this.super_.rows[i]
.as_ref()
.unwrap()
.as_process()
.expect("ProcessTable_cleanupEntries: row is not a Process");
(p.st_uid, Process_getPid(p))
};
unsafe {
if st_uid > (*host).maxUserId {
(*host).maxUserId = st_uid;
}
if pid > (*host).maxProcessId {
(*host).maxProcessId = pid;
}
}
if !Table_cleanupRow(&mut this.super_, i) {
dirty_index = i;
}
}
Table_compact(&mut this.super_, dirty_index);
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ported::machine::{Machine, ScreenSettings, Settings};
use crate::ported::row::Row;
use crate::ported::table::{Table_add, Table_findRow};
fn host(mono: u64) -> Machine {
let mut m = Machine::default();
m.monotonicMs = mono;
m.settings = Some(Settings {
highlightChanges: false,
highlightDelaySecs: 0,
screens: vec![ScreenSettings {
treeView: false,
..Default::default()
}],
..Default::default()
});
m
}
fn row(id: i32) -> Row {
let mut r = Row::default();
r.id = id;
r.show = true;
r.showChildren = true;
r
}
fn make_proc(_host: *const Machine) -> Box<dyn Object> {
Box::new(crate::ported::process::Process::default())
}
#[test]
fn getProcess_creates_new_then_finds_existing() {
let h = host(1000);
let mut pt = ProcessTable::empty();
ProcessTable_init(&mut pt, &h as *const Machine, None);
let (pre1, idx1) = ProcessTable_getProcess(&mut pt, 42, make_proc);
assert!(!pre1);
assert_eq!(pt.super_.rows.len(), 1);
let p = pt.super_.rows[idx1].as_ref().unwrap().as_process().unwrap();
assert_eq!(Process_getPid(p), 42);
let (pre2, idx2) = ProcessTable_getProcess(&mut pt, 42, make_proc);
assert!(pre2);
assert_eq!(idx2, idx1);
assert_eq!(pt.super_.rows.len(), 1);
}
#[test]
fn init_wires_base_table_and_pid_match_list() {
let h = host(0);
let mut pt = ProcessTable::empty();
ProcessTable_init(&mut pt, &h as *const Machine, Some(77));
assert_eq!(pt.pidMatchList, Some(77 as *mut Hashtable));
assert!(pt.super_.needsSort);
assert_eq!(pt.super_.following, -1);
}
#[test]
fn prepare_entries_zeroes_counters_and_resets_rows() {
let h = host(10);
let mut pt = ProcessTable::empty();
ProcessTable_init(&mut pt, &h as *const Machine, None);
Table_add(&mut pt.super_, Box::new(row(1)));
Table_add(&mut pt.super_, Box::new(row(2)));
pt.totalTasks = 9;
pt.runningTasks = 3;
pt.userlandThreads = 5;
pt.kernelThreads = 4;
pt.super_.rows[0]
.as_mut()
.unwrap()
.as_row_mut()
.unwrap()
.updated = true;
pt.super_.rows[0]
.as_mut()
.unwrap()
.as_row_mut()
.unwrap()
.show = false;
ProcessTable_prepareEntries(&mut pt);
assert_eq!(pt.totalTasks, 0);
assert_eq!(pt.runningTasks, 0);
assert_eq!(pt.userlandThreads, 0);
assert_eq!(pt.kernelThreads, 0);
let r0 = Table_findRow(&pt.super_, 1).unwrap();
assert!(!r0.updated);
assert!(r0.show);
assert!(!r0.wasShown); }
#[test]
#[should_panic(expected = "goThroughEntries")]
fn iterate_entries_delegates_to_platform_scan_stub() {
let h = host(0);
let mut pt = ProcessTable::empty();
ProcessTable_init(&mut pt, &h as *const Machine, None);
ProcessTable_iterateEntries(&mut pt);
}
}