rt: signal handling in yggdrasil-rt
This commit is contained in:
parent
cbd823e17b
commit
ed7f6c2f46
@ -1,5 +1,5 @@
|
||||
use core::{
|
||||
sync::atomic::{AtomicU32, Ordering},
|
||||
sync::atomic::{AtomicU32, AtomicUsize, Ordering},
|
||||
task::{Context, Poll},
|
||||
};
|
||||
|
||||
@ -82,6 +82,7 @@ pub struct Process {
|
||||
parent: Option<Weak<Process>>,
|
||||
|
||||
inner: IrqSafeRwLock<ProcessInner>,
|
||||
signal_entry: AtomicUsize,
|
||||
|
||||
pub(crate) exit: OneTimeEvent<ExitCode>,
|
||||
|
||||
@ -107,6 +108,7 @@ impl Process {
|
||||
id,
|
||||
parent,
|
||||
|
||||
signal_entry: AtomicUsize::new(0),
|
||||
inner: IrqSafeRwLock::new(ProcessInner::new(id, group_id, Some(space.clone()), image)),
|
||||
|
||||
exit: OneTimeEvent::new(),
|
||||
@ -323,6 +325,16 @@ impl Process {
|
||||
inner.space = None;
|
||||
}
|
||||
|
||||
/// Updates the signal entry point
|
||||
pub fn set_signal_entry(&self, entry: usize) {
|
||||
self.signal_entry.store(entry, Ordering::Release);
|
||||
}
|
||||
|
||||
/// Retrieves current signal entry of the process
|
||||
pub fn signal_entry(&self) -> usize {
|
||||
self.signal_entry.load(Ordering::Acquire)
|
||||
}
|
||||
|
||||
/// Raises a signal for the specified process
|
||||
///
|
||||
/// If sender is Some(id) - signal was sent by some thread in some (possibly this) process.
|
||||
|
@ -1,4 +1,9 @@
|
||||
use core::{cell::Cell, mem::size_of, ops::Deref};
|
||||
use core::{
|
||||
cell::Cell,
|
||||
mem::size_of,
|
||||
ops::Deref,
|
||||
sync::atomic::{AtomicUsize, Ordering},
|
||||
};
|
||||
|
||||
use alloc::{
|
||||
collections::{btree_map, BTreeMap},
|
||||
@ -59,10 +64,6 @@ struct SignalEntry {
|
||||
stack: usize,
|
||||
}
|
||||
|
||||
struct ThreadInner {
|
||||
signal_entry: Option<SignalEntry>,
|
||||
}
|
||||
|
||||
/// Describes a single thread within the system
|
||||
pub struct Thread {
|
||||
/// Unique thread ID
|
||||
@ -77,7 +78,8 @@ pub struct Thread {
|
||||
space: Option<Arc<ProcessAddressSpace>>,
|
||||
debug: IrqSafeSpinlock<ThreadDebuggingInfo>,
|
||||
|
||||
inner: IrqSafeSpinlock<ThreadInner>,
|
||||
// inner: IrqSafeSpinlock<ThreadInner>,
|
||||
signal_stack: AtomicUsize,
|
||||
signal_queue: SegQueue<Signal>,
|
||||
|
||||
pub exit: Arc<BoolEvent>,
|
||||
@ -133,7 +135,7 @@ impl Thread {
|
||||
process,
|
||||
space,
|
||||
|
||||
inner: IrqSafeSpinlock::new(ThreadInner { signal_entry: None }),
|
||||
signal_stack: AtomicUsize::new(0),
|
||||
signal_queue: SegQueue::new(),
|
||||
exit: Arc::new(BoolEvent::new()),
|
||||
kill: BoolEvent::new(),
|
||||
@ -221,10 +223,9 @@ impl Thread {
|
||||
self.affinity.set(affinity);
|
||||
}
|
||||
|
||||
/// Updates the thread signal entry/stack information
|
||||
pub fn set_signal_entry(&self, entry: usize, stack: usize) {
|
||||
let mut inner = self.inner.lock();
|
||||
inner.signal_entry.replace(SignalEntry { entry, stack });
|
||||
/// Updates the thread signal stack information
|
||||
pub fn set_signal_stack(&self, stack: usize) {
|
||||
self.signal_stack.store(stack, Ordering::Release);
|
||||
}
|
||||
|
||||
/// Returns the thread address space (usually provided by its parent process). If none exists,
|
||||
@ -266,6 +267,7 @@ impl Thread {
|
||||
|
||||
/// Pushes a signal to the thread's signal queue
|
||||
pub fn raise_signal(&self, signal: Signal) {
|
||||
log::debug!("{}: raise signal {signal:?}", self.id);
|
||||
self.signal_queue.push(signal);
|
||||
self.enqueue();
|
||||
}
|
||||
@ -622,6 +624,8 @@ impl CurrentThread {
|
||||
}
|
||||
|
||||
if let Some(signal) = self.signal_queue.pop() {
|
||||
log::debug!("{}: handle signal {signal:?}", self.id);
|
||||
|
||||
if signal == Signal::Debug {
|
||||
frame.set_single_step(true);
|
||||
|
||||
@ -638,19 +642,22 @@ impl CurrentThread {
|
||||
return;
|
||||
}
|
||||
|
||||
let inner = self.inner.lock();
|
||||
let ip = self.process().signal_entry();
|
||||
let sp = self.signal_stack.load(Ordering::Acquire);
|
||||
|
||||
let Some(entry) = inner.signal_entry.as_ref() else {
|
||||
drop(inner);
|
||||
// Check if ip and sp are legible for signal entry
|
||||
if ip == 0 || sp < 4096 {
|
||||
log::warn!("No legible signal handler for {}", self.id);
|
||||
self.exit_process(ExitCode::BySignal(signal));
|
||||
};
|
||||
}
|
||||
|
||||
// TODO check if really in a syscall, lol
|
||||
let syscall_return = -(u32::from(Error::Interrupted) as isize);
|
||||
frame.set_return_value(syscall_return as u64);
|
||||
|
||||
// Setup signal frame
|
||||
let usp = ((entry.stack - size_of::<SignalEntryData>()) & !0xF)
|
||||
// FIXME use write_foreign_*** for this
|
||||
let usp = ((sp - size_of::<SignalEntryData>()) & !0xF)
|
||||
- TaskContextImpl::SIGNAL_STACK_EXTRA_ALIGN;
|
||||
let frame_ptr = usp as *mut SignalEntryData;
|
||||
|
||||
@ -666,13 +673,13 @@ impl CurrentThread {
|
||||
// Setup return to signal handler
|
||||
log::debug!(
|
||||
"Signal entry @ pc={:#x}, sp={:#x} (top = {:#x})",
|
||||
entry.entry,
|
||||
ip,
|
||||
usp,
|
||||
entry.stack
|
||||
sp,
|
||||
);
|
||||
|
||||
frame.set_user_sp(usp);
|
||||
frame.set_user_ip(entry.entry);
|
||||
frame.set_user_ip(ip);
|
||||
|
||||
// Pass the frame pointer as an argument to signal handler entry
|
||||
frame.set_argument(usp as _);
|
||||
|
@ -201,7 +201,14 @@ pub(crate) fn nanosleep(duration: &Duration) -> Result<(), Error> {
|
||||
|
||||
pub(crate) fn set_signal_entry(ip: usize, sp: usize) {
|
||||
let thread = Thread::current();
|
||||
thread.set_signal_entry(ip, sp);
|
||||
let process = thread.process();
|
||||
log::debug!("{}: set_signal_entry({:#x}, {:#x})", thread.id, ip, sp);
|
||||
if ip != usize::MAX {
|
||||
process.set_signal_entry(ip);
|
||||
}
|
||||
if sp != usize::MAX {
|
||||
thread.set_signal_stack(sp);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn send_signal(pid: ProcessId, signal: Signal) -> Result<(), Error> {
|
||||
|
@ -6,4 +6,5 @@ pub use abi::process::{
|
||||
SpawnOption, SpawnOptions, StringArgIter, ThreadId, ThreadSpawnOptions,
|
||||
};
|
||||
|
||||
pub mod signal;
|
||||
pub mod thread_local;
|
||||
|
148
lib/runtime/src/process/signal.rs
Normal file
148
lib/runtime/src/process/signal.rs
Normal file
@ -0,0 +1,148 @@
|
||||
//! Runtime utilities for signal handling
|
||||
use core::ffi::c_int;
|
||||
|
||||
use abi::{
|
||||
error::Error,
|
||||
mem::{MappingFlags, MappingSource},
|
||||
process::{ExitCode, Signal, SignalEntryData},
|
||||
};
|
||||
use alloc::boxed::Box;
|
||||
|
||||
/// Describes how a signal should be handled
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum SignalHandler {
|
||||
/// Terminate when received
|
||||
Terminate,
|
||||
/// Silently ignore the signal
|
||||
Ignore,
|
||||
/// Pass the signal to a custom function (Rust version)
|
||||
Rust(fn(Signal)),
|
||||
/// Pass the signal to a custom function (C compat version)
|
||||
C(unsafe extern "C" fn(c_int)),
|
||||
}
|
||||
|
||||
// TODO RwLock here
|
||||
const MAX_SIGNALS: usize = 16;
|
||||
static mut TABLE: [SignalHandler; MAX_SIGNALS] = [const { SignalHandler::Terminate }; MAX_SIGNALS];
|
||||
|
||||
unsafe extern "C" fn common_signal_entry(data: &SignalEntryData) -> ! {
|
||||
let index = data.signal.into_raw() as usize;
|
||||
|
||||
if index >= MAX_SIGNALS {
|
||||
terminate_by_signal(data.signal);
|
||||
}
|
||||
|
||||
match TABLE[index] {
|
||||
SignalHandler::Rust(function) => function(data.signal),
|
||||
SignalHandler::C(function) => {
|
||||
let signum = data.signal.into_raw() as i32;
|
||||
function(signum);
|
||||
}
|
||||
SignalHandler::Terminate => terminate_by_signal(data.signal),
|
||||
SignalHandler::Ignore => (),
|
||||
}
|
||||
|
||||
crate::sys::exit_signal(data)
|
||||
}
|
||||
|
||||
// TODO add signal print hook function to dump signal info here
|
||||
fn terminate_by_signal(signal: Signal) -> ! {
|
||||
crate::debug_trace!("runtime: terminated by signal: {signal:?}");
|
||||
unsafe { crate::sys::exit_process(ExitCode::BySignal(signal)) };
|
||||
}
|
||||
|
||||
/// Updates the handler for a particular signal. Returns the old handler used for that signal.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Marked as unsafe due to being thread-unsafe. Will be lifted once I port RwLock into the runtime
|
||||
/// crate.
|
||||
pub unsafe fn set_handler(signal: Signal, handler: SignalHandler) -> SignalHandler {
|
||||
let entry = &mut TABLE[signal.into_raw() as usize];
|
||||
let old_handler = core::mem::replace(entry, handler);
|
||||
old_handler
|
||||
}
|
||||
|
||||
/// Sets the stack that will be used to handle signals **on this thread**.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// To be legible for signal handling, `sp` must be non-NULL and have at least 4K of space
|
||||
/// below it. It is up to the caller to make sure `sp` points to a proper stack's top.
|
||||
///
|
||||
/// TLDR: just use [setup_signal_stack].
|
||||
pub unsafe fn set_signal_stack(sp: usize) {
|
||||
crate::sys::set_signal_entry(usize::MAX, sp);
|
||||
}
|
||||
|
||||
fn allocate_signal_stack(mut size: usize) -> Result<usize, Error> {
|
||||
if size == 0 {
|
||||
size = 4096;
|
||||
}
|
||||
size = (size + 0xFFF) & !0xFFF;
|
||||
let base = unsafe {
|
||||
crate::sys::map_memory(None, size, MappingFlags::WRITE, &MappingSource::Anonymous)
|
||||
}?;
|
||||
let top = base + size;
|
||||
Ok(top)
|
||||
}
|
||||
|
||||
/// Allocates a signal stack and sets it **for this thread**. Returns an error if allocation fails.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// This function may allocate more memory than requested in the `size` parameter. It will allocate
|
||||
/// at least 4KiB of memory, rounding the size up to a page boundary. This is required for the
|
||||
/// stack to be legible for entry from the kernel.
|
||||
///
|
||||
/// The allocated stack is expected to live forever or at least until it is replaced by another
|
||||
/// one, so current implementation just leaks the stack. The implementation also provides no
|
||||
/// guarantees on what will happen if a raw syscall to [crate::sys::set_signal_entry] is done
|
||||
/// after calling this function.
|
||||
pub fn setup_signal_stack(size: usize) -> Result<(), Error> {
|
||||
let sp = allocate_signal_stack(size)?;
|
||||
unsafe { set_signal_stack(sp) };
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Performs full signal setup for the process:
|
||||
///
|
||||
/// * Allocates a signal stack for **the current thread**.
|
||||
/// * Sets up a signal entry into the runtime-managed handler for the whole process.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// This function is intended to be called from the main thread, while [setup_signal_stack] is
|
||||
/// intended for setting up thread signal stacks. A caller *may* call this function elsewhere,
|
||||
/// and this will be safe, but likely will not make any sense.
|
||||
///
|
||||
/// Also see notes for [setup_signal_stack].
|
||||
pub fn setup_signal_full(size: usize) -> Result<(), Error> {
|
||||
let sp = allocate_signal_stack(size)?;
|
||||
unsafe { crate::sys::set_signal_entry(imp::signal_entry as usize, sp) };
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// implementation details
|
||||
|
||||
#[cfg(any(target_arch = "x86", rust_analyzer))]
|
||||
mod imp {
|
||||
// i686 is a bit tricky: cdecl ABI requires the arguments to be passed on the stack, while
|
||||
// the kernel does the easy thing and just puts it into %eax, so a fixup needs to be done
|
||||
// before entering a proper handler. The wrapper also aligns the stack nicely.
|
||||
#[naked]
|
||||
pub(super) unsafe extern "C" fn signal_entry() -> ! {
|
||||
// %eax - SignalEntryData pointer
|
||||
core::arch::naked_asm!(
|
||||
r#"
|
||||
jmp .
|
||||
"#,
|
||||
options(att_syntax)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(not(target_arch = "x86"), rust_analyzer))]
|
||||
mod imp {
|
||||
pub(super) use super::common_signal_entry as signal_entry;
|
||||
}
|
17
test.c
17
test.c
@ -1,20 +1,3 @@
|
||||
#include <pthread.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/yggdrasil.h>
|
||||
|
||||
static void *function(void *arg) {
|
||||
abort();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int main(int argc, const char **argv) {
|
||||
pthread_t thread;
|
||||
|
||||
assert(pthread_create(&thread, NULL, function, (void *) NULL) == 0);
|
||||
pthread_join(thread, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,6 +1,10 @@
|
||||
#ifndef _YGGDRASIL_SETJMP_H
|
||||
#define _YGGDRASIL_SETJMP_H 1
|
||||
|
||||
int setjmp(jmp_buf buf);
|
||||
int _setjmp(jmp_buf buf);
|
||||
|
||||
[[noreturn]] void longjmp(jmp_buf buf, int val);
|
||||
[[noreturn]] void _longjmp(jmp_buf buf, int val);
|
||||
|
||||
#endif
|
||||
|
@ -11,7 +11,7 @@ typedef _Atomic int sig_atomic_t;
|
||||
|
||||
#define SIG_IGN __sig_ignore
|
||||
// TODO
|
||||
#define SIG_ERR __sig_terminate
|
||||
#define SIG_ERR ((sig_handler_t) 1)
|
||||
// TODO
|
||||
#define SIG_HOLD __sig_terminate
|
||||
|
||||
|
@ -102,10 +102,13 @@ impl<T> EResult<T> {
|
||||
}
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub fn expect(self, message: &str) -> T {
|
||||
match self {
|
||||
Self::Ok(value) => value,
|
||||
Self::Err(err) => panic!("expect() failed: {}, errno={:?}", message, err)
|
||||
Self::Err(err) => {
|
||||
panic!("expect() failed: {}, errno={:?}", message, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,4 @@ exclude = [
|
||||
"longjmp",
|
||||
"_setjmp",
|
||||
"_longjmp",
|
||||
"__x86_64_setjmp",
|
||||
"__x86_64_longjmp"
|
||||
]
|
||||
|
@ -2,11 +2,10 @@ use core::ffi::{c_char, c_int, c_long, c_void};
|
||||
|
||||
use yggdrasil_rt::process::Signal;
|
||||
|
||||
use crate::{error::CIntZeroResult, util::PointerExt};
|
||||
use crate::{error::{self, CIntZeroResult, EResult, TryFromExt}, signal, util::PointerExt};
|
||||
|
||||
use super::{
|
||||
sys_time::__ygg_timespec_t,
|
||||
sys_types::{pid_t, uid_t},
|
||||
errno, sys_time::__ygg_timespec_t, sys_types::{pid_t, uid_t}
|
||||
};
|
||||
|
||||
pub type sig_handler_t = unsafe extern "C" fn(SigNumber);
|
||||
@ -104,16 +103,15 @@ impl SigNumber {
|
||||
pub const INVALID: Self = Self(-65536);
|
||||
}
|
||||
|
||||
impl TryFrom<SigNumber> for Signal {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: SigNumber) -> Result<Self, Self::Error> {
|
||||
impl TryFromExt<SigNumber> for Signal {
|
||||
fn e_try_from(value: SigNumber) -> EResult<Self> {
|
||||
match value {
|
||||
SIGSEGV => Ok(Signal::MemoryAccessViolation),
|
||||
SIGABRT => Ok(Signal::Aborted),
|
||||
SIGKILL => Ok(Signal::Killed),
|
||||
SIGINT => Ok(Signal::Interrupted),
|
||||
_ => Err(()),
|
||||
SIGSEGV => EResult::Ok(Signal::MemoryAccessViolation),
|
||||
SIGABRT => EResult::Ok(Signal::Aborted),
|
||||
SIGKILL => EResult::Ok(Signal::Killed),
|
||||
SIGINT => EResult::Ok(Signal::Interrupted),
|
||||
SIGTRAP => EResult::Ok(Signal::Debug),
|
||||
_ => EResult::Err(errno::EINVAL),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -229,8 +227,25 @@ unsafe extern "C" fn sigismember(_mask: *const sigset_t, _signum: c_int) -> c_in
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn signal(_handler: sig_handler_t, _signum: c_int) -> sig_handler_t {
|
||||
todo!()
|
||||
unsafe extern "C" fn signal(handler: sig_handler_t, signum: c_int) -> sig_handler_t {
|
||||
// Validate handler
|
||||
let address: usize = core::mem::transmute(handler);
|
||||
// NULL or SIG_ERR
|
||||
if address == 0 || address == 1 {
|
||||
yggdrasil_rt::debug_trace!("libc: signal() was passed an invalid handler");
|
||||
error::errno = errno::EINVAL;
|
||||
// SIG_ERR
|
||||
return core::mem::transmute(1usize);
|
||||
}
|
||||
|
||||
match signal::set(SigNumber(signum), handler) {
|
||||
EResult::Ok(handler) => handler,
|
||||
EResult::Err(err) => {
|
||||
error::errno = err;
|
||||
// SIG_ERR
|
||||
core::mem::transmute(1usize)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
|
@ -18,7 +18,7 @@ unsafe extern "C" fn nanosleep(
|
||||
|
||||
let amount = Duration::from(*rqtp);
|
||||
|
||||
process::sleep(amount)?;
|
||||
process::sleep(amount).expect("TODO interruptible sleep in nanosleep()");
|
||||
|
||||
CIntZeroResult::SUCCESS
|
||||
}
|
||||
|
@ -1,8 +1,14 @@
|
||||
#![allow(unused)]
|
||||
|
||||
use core::ffi::c_int;
|
||||
|
||||
mod exec;
|
||||
mod fs;
|
||||
mod getopt;
|
||||
mod io;
|
||||
mod process;
|
||||
mod util;
|
||||
|
||||
pub const STDIN_FILENO: c_int = 0;
|
||||
pub const STDOUT_FILENO: c_int = 1;
|
||||
pub const STDERR_FILENO: c_int = 2;
|
||||
|
@ -6,8 +6,8 @@ use core::{
|
||||
use yggdrasil_rt::debug_trace;
|
||||
|
||||
use crate::{
|
||||
error::{CIntCountResult, CIntZeroResult},
|
||||
headers::sys_types::{gid_t, pid_t, uid_t},
|
||||
error::{self, CIntCountResult, CIntZeroResult},
|
||||
headers::{errno, sys_types::{gid_t, pid_t, uid_t}},
|
||||
process,
|
||||
};
|
||||
|
||||
@ -121,6 +121,11 @@ unsafe extern "C" fn setuid(uid: uid_t) -> c_int {
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn sleep(seconds: c_uint) -> c_uint {
|
||||
let duration = Duration::from_secs(seconds.try_into().unwrap());
|
||||
process::sleep(duration).expect("TODO: sleep failed");
|
||||
0
|
||||
match process::sleep(duration) {
|
||||
Ok(()) => 0,
|
||||
Err(remaining) => {
|
||||
error::errno = errno::EINTR;
|
||||
remaining.as_secs() as _
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -42,6 +42,7 @@ mod sync;
|
||||
mod types;
|
||||
mod util;
|
||||
mod thread;
|
||||
mod signal;
|
||||
|
||||
pub mod headers;
|
||||
|
||||
@ -50,6 +51,7 @@ unsafe extern "C" fn __ygglibc_entry(
|
||||
main: extern "C" fn(c_int, *const *const c_char, *const *const c_char) -> c_int,
|
||||
arg: usize,
|
||||
) {
|
||||
signal::init(true);
|
||||
init::init();
|
||||
|
||||
// Setup args
|
||||
|
@ -24,9 +24,9 @@ pub fn getpgrp() -> pid_t {
|
||||
pgid.into_raw().try_into().unwrap()
|
||||
}
|
||||
|
||||
pub fn sleep(duration: Duration) -> EResult<()> {
|
||||
unsafe { syscall::nanosleep(&duration) }?;
|
||||
EResult::Ok(())
|
||||
pub fn sleep(duration: Duration) -> Result<(), Duration> {
|
||||
// TODO make nanosleep return duration
|
||||
unsafe { syscall::nanosleep(&duration) }.map_err(|_| Duration::from_secs(1))
|
||||
}
|
||||
|
||||
pub fn abort() -> ! {
|
||||
|
56
userspace/lib/ygglibc/src/signal.rs
Normal file
56
userspace/lib/ygglibc/src/signal.rs
Normal file
@ -0,0 +1,56 @@
|
||||
use yggdrasil_rt::process::{
|
||||
signal::{self, SignalHandler},
|
||||
Signal,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
error::{EResult, ResultExt, TryFromExt},
|
||||
headers::{
|
||||
self, errno, signal::{sig_handler_t, SigNumber}
|
||||
},
|
||||
};
|
||||
|
||||
const SIGNAL_STACK_SIZE: usize = 4096 * 8;
|
||||
|
||||
// These are just stubs for addresses, which get converted into Rust handlers
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn __sig_ignore(signum: SigNumber) {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn __sig_terminate(signum: SigNumber) {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
pub unsafe fn set(signum: SigNumber, handler: sig_handler_t) -> EResult<sig_handler_t> {
|
||||
let signal = Signal::e_try_from(signum)?;
|
||||
let handler = match handler {
|
||||
// Transform special cases into Rust signal handlers instead
|
||||
_ if handler == __sig_terminate => todo!(),
|
||||
_ if handler == __sig_ignore => todo!(),
|
||||
// This is safe: handler essentially has the same type, just in a wrapper
|
||||
_ => core::mem::transmute(handler)
|
||||
};
|
||||
let old = signal::set_handler(signal, SignalHandler::C(handler));
|
||||
let old = match old {
|
||||
// Transform Rust special cases into C "handlers"
|
||||
SignalHandler::Ignore => __sig_ignore,
|
||||
SignalHandler::Terminate => __sig_terminate,
|
||||
// libc doesn't set Rust signal handlers, return terminate just in case
|
||||
SignalHandler::Rust(_) => __sig_terminate,
|
||||
SignalHandler::C(handler) => core::mem::transmute(handler),
|
||||
};
|
||||
EResult::Ok(old)
|
||||
}
|
||||
|
||||
pub fn init(main: bool) {
|
||||
// TODO reset any non-default handlers possibly set by whatever libc might've been entered from
|
||||
if main {
|
||||
signal::setup_signal_full(SIGNAL_STACK_SIZE)
|
||||
.expect("Couldn't setup signal handler for the main thread");
|
||||
} else {
|
||||
signal::setup_signal_stack(SIGNAL_STACK_SIZE).expect("Couldn't setup thread signal stack");
|
||||
}
|
||||
}
|
@ -17,7 +17,7 @@ use crate::{
|
||||
error::{EResult, OptionExt}, headers::{
|
||||
errno::{self, Errno},
|
||||
sys_types::{pthread_attr_t, pthread_t},
|
||||
}, process, sync::Mutex
|
||||
}, process, signal, sync::Mutex
|
||||
};
|
||||
|
||||
pub mod tls;
|
||||
@ -183,7 +183,7 @@ impl Thread {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
|
||||
unsafe { Self::set_this(argument.thread.clone()) };
|
||||
unsafe { Self::setup_self(&argument.thread); }
|
||||
|
||||
let result = (argument.entry)(argument.argument);
|
||||
argument.thread.result.store(result, Ordering::Release);
|
||||
@ -194,6 +194,12 @@ impl Thread {
|
||||
// TODO call thread-local destructors
|
||||
unsafe { yggdrasil_rt::sys::exit_thread() }
|
||||
}
|
||||
|
||||
unsafe fn setup_self(self: &Arc<Self>) {
|
||||
Self::set_this(self.clone());
|
||||
// Setup signal stack
|
||||
signal::init(false);
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Thread {
|
||||
|
Loading…
x
Reference in New Issue
Block a user