use core::{ ffi::{c_char, c_int, c_void, CStr}, mem::MaybeUninit, time::Duration }; use yggdrasil_rt::{ process::{ExitCode, Signal}, sync::mutex::Mutex, sys as syscall }; use alloc::vec::Vec; use crate::{ headers::sys_types::pid_t, io::{self, managed::stderr}, }; struct DsoDestructor { f: extern "C" fn (*mut c_void), arg: *mut c_void, } unsafe impl Sync for DsoDestructor {} impl DsoDestructor { unsafe fn invoke(self) { (self.f)(self.arg) } } static AT_EXIT: Mutex> = Mutex::new(Vec::new()); // TODO this will be in the linker instead static AT_DSO_EXIT: Mutex> = Mutex::new(Vec::new()); pub fn getpid() -> pid_t { let pid = unsafe { syscall::get_pid() }; pid.into_raw().try_into().unwrap() } pub fn getpgrp() -> pid_t { let pgid = unsafe { syscall::get_process_group_id() }; pgid.into_raw().try_into().unwrap() } pub fn sleep(duration: Duration) -> Result<(), Duration> { let mut remaining = MaybeUninit::uninit(); if unsafe { syscall::nanosleep(&duration, &mut remaining) }.is_ok() { Ok(()) } else { Err(unsafe { remaining.assume_init() }) } } pub fn abort() -> ! { let pid = unsafe { syscall::get_pid() }; unsafe { syscall::send_signal(pid, Signal::Aborted).ok() }; unreachable!() } pub fn c_exit_immediately(status: c_int) -> ! { let code = ExitCode::Exited(status); unsafe { syscall::exit_process(code) }; } pub fn c_exit(status: c_int) -> ! { unsafe { pre_exit(); c_exit_immediately(status) } } pub fn at_exit(f: extern "C" fn()) { AT_EXIT.lock().push(f); } pub fn at_dso_exit(f: extern "C" fn(*mut c_void), arg: *mut c_void, _dso_handle: *mut c_void) { // TODO no support for dlopen/dlclose, so at_dso_exit == at_exit right now AT_DSO_EXIT.lock().push(DsoDestructor { f, arg }); } unsafe fn call_atexit() { for d in AT_DSO_EXIT.lock().drain(..) { d.invoke(); } for f in AT_EXIT.lock().drain(..) { f(); } } unsafe fn pre_exit() { call_atexit(); io::cleanup(); } #[no_mangle] unsafe extern "C" fn __assert_fail(file: *const c_char, line: c_int, message: *const c_char) -> ! { use core::fmt::Write; let err = stderr.as_mut().unwrap(); let file = match file.is_null() { false => CStr::from_ptr(file).to_str().ok(), true => None, } .unwrap_or("???"); let expr = match message.is_null() { false => CStr::from_ptr(message).to_str().ok(), true => None, } .unwrap_or("???"); writeln!(err, "Assertion failed: '{}' at {}:{}", expr, file, line).ok(); abort() }