use alloc::{
sync::{Arc, Weak},
vec::Vec,
};
use core::{ffi::c_long, sync::atomic::Ordering};
use axerrno::{AxError, AxResult};
use axtask::{AxTaskRef, TaskInner, WeakAxTaskRef, current};
use bytemuck::AnyBitPattern;
use linux_raw_sys::general::ROBUST_LIST_LIMIT;
use spin::RwLock;
use starry_process::{Pid, ProcessGroup, Session};
use starry_signal::{SignalInfo, Signo};
use starry_vm::{VmMutPtr, VmPtr};
use weak_map::WeakMap;
use super::{
AsThread, FutexKey, ProcessData, TimerState, futex_table_for, send_signal_thread_inner,
send_signal_to_process, send_signal_to_thread,
};
static TASK_TABLE: RwLock<WeakMap<Pid, WeakAxTaskRef>> = RwLock::new(WeakMap::new());
static PROCESS_TABLE: RwLock<WeakMap<Pid, Weak<ProcessData>>> = RwLock::new(WeakMap::new());
static PROCESS_GROUP_TABLE: RwLock<WeakMap<Pid, Weak<ProcessGroup>>> = RwLock::new(WeakMap::new());
static SESSION_TABLE: RwLock<WeakMap<Pid, Weak<Session>>> = RwLock::new(WeakMap::new());
pub fn cleanup_task_tables() {
TASK_TABLE.write().cleanup();
PROCESS_TABLE.write().cleanup();
PROCESS_GROUP_TABLE.write().cleanup();
SESSION_TABLE.write().cleanup();
}
pub fn add_task_to_table(task: &AxTaskRef) {
let tid = task.id().as_u64() as Pid;
let mut task_table = TASK_TABLE.write();
task_table.insert(tid, task);
let proc_data = &task.as_thread().proc_data;
let proc = &proc_data.proc;
let pid = proc.pid();
let mut proc_table = PROCESS_TABLE.write();
if proc_table.contains_key(&pid) {
return;
}
proc_table.insert(pid, proc_data);
let pg = proc.group();
let mut pg_table = PROCESS_GROUP_TABLE.write();
if pg_table.contains_key(&pg.pgid()) {
return;
}
pg_table.insert(pg.pgid(), &pg);
let session = pg.session();
let mut session_table = SESSION_TABLE.write();
if session_table.contains_key(&session.sid()) {
return;
}
session_table.insert(session.sid(), &session);
}
pub fn tasks() -> Vec<AxTaskRef> {
TASK_TABLE.read().values().collect()
}
pub fn get_task(tid: Pid) -> AxResult<AxTaskRef> {
if tid == 0 {
return Ok(current().clone());
}
TASK_TABLE.read().get(&tid).ok_or(AxError::NoSuchProcess)
}
pub fn processes() -> Vec<Arc<ProcessData>> {
PROCESS_TABLE.read().values().collect()
}
pub fn get_process_data(pid: Pid) -> AxResult<Arc<ProcessData>> {
if pid == 0 {
return Ok(current().as_thread().proc_data.clone());
}
PROCESS_TABLE.read().get(&pid).ok_or(AxError::NoSuchProcess)
}
pub fn get_process_group(pgid: Pid) -> AxResult<Arc<ProcessGroup>> {
PROCESS_GROUP_TABLE
.read()
.get(&pgid)
.ok_or(AxError::NoSuchProcess)
}
pub fn get_session(sid: Pid) -> AxResult<Arc<Session>> {
SESSION_TABLE.read().get(&sid).ok_or(AxError::NoSuchProcess)
}
pub fn poll_timer(task: &TaskInner) {
let Some(thr) = task.try_as_thread() else {
return;
};
let Ok(mut time) = thr.time.try_borrow_mut() else {
return;
};
time.poll(|signo| {
send_signal_thread_inner(task, thr, SignalInfo::new_kernel(signo));
});
}
pub fn set_timer_state(task: &TaskInner, state: TimerState) {
let Some(thr) = task.try_as_thread() else {
return;
};
let Ok(mut time) = thr.time.try_borrow_mut() else {
return;
};
time.poll(|signo| {
send_signal_thread_inner(task, thr, SignalInfo::new_kernel(signo));
});
time.set_state(state);
}
#[repr(C)]
#[derive(Debug, Copy, Clone, AnyBitPattern)]
pub struct RobustList {
pub next: *mut RobustList,
}
#[repr(C)]
#[derive(Debug, Copy, Clone, AnyBitPattern)]
pub struct RobustListHead {
pub list: RobustList,
pub futex_offset: c_long,
pub list_op_pending: *mut RobustList,
}
fn handle_futex_death(entry: *mut RobustList, offset: i64) -> AxResult<()> {
let address = (entry as u64)
.checked_add_signed(offset)
.ok_or(AxError::InvalidInput)?;
let address: usize = address.try_into().map_err(|_| AxError::InvalidInput)?;
let key = FutexKey::new_current(address);
let futex_table = futex_table_for(&key);
let Some(futex) = futex_table.get(&key) else {
return Ok(());
};
futex.owner_dead.store(true, Ordering::SeqCst);
futex.wq.wake(1, u32::MAX);
Ok(())
}
pub fn exit_robust_list(head: *const RobustListHead) -> AxResult<()> {
let mut limit = ROBUST_LIST_LIMIT;
let end_ptr = unsafe { &raw const (*head).list };
let head = head.vm_read()?;
let mut entry = head.list.next;
let offset = head.futex_offset;
let pending = head.list_op_pending;
while !core::ptr::eq(entry, end_ptr) {
let next_entry = entry.vm_read()?.next;
if entry != pending {
handle_futex_death(entry, offset)?;
}
entry = next_entry;
limit -= 1;
if limit == 0 {
return Err(AxError::FilesystemLoop);
}
axtask::yield_now();
}
Ok(())
}
pub fn do_exit(exit_code: i32, group_exit: bool) {
let curr = current();
let thr = curr.as_thread();
info!("{} exit with code: {}", curr.id_name(), exit_code);
let clear_child_tid = thr.clear_child_tid() as *mut u32;
if clear_child_tid.vm_write(0).is_ok() {
let key = FutexKey::new_current(clear_child_tid as usize);
let table = futex_table_for(&key);
let guard = table.get(&key);
if let Some(futex) = guard {
futex.wq.wake(1, u32::MAX);
}
axtask::yield_now();
}
let head = thr.robust_list_head() as *const RobustListHead;
if !head.is_null()
&& let Err(err) = exit_robust_list(head)
{
warn!("exit robust list failed: {err:?}");
}
let process = &thr.proc_data.proc;
if process.exit_thread(curr.id().as_u64() as Pid, exit_code) {
process.exit();
if let Some(parent) = process.parent() {
if let Some(signo) = thr.proc_data.exit_signal {
let _ = send_signal_to_process(parent.pid(), Some(SignalInfo::new_kernel(signo)));
}
if let Ok(data) = get_process_data(parent.pid()) {
data.child_exit_event.wake();
}
}
thr.proc_data.exit_event.wake();
crate::syscall::SHM_MANAGER
.lock()
.clear_proc_shm(process.pid());
}
thr.exit_event.wake();
if group_exit && !process.is_group_exited() {
process.group_exit();
let sig = SignalInfo::new_kernel(Signo::SIGKILL);
for tid in process.threads() {
let _ = send_signal_to_thread(None, tid, Some(sig.clone()));
}
}
thr.set_exit();
}