feat: add Rust userspace support; fix TLS layout for p_align < 16; kill whole process on sibling-thread faults
This commit is contained in:
1 parent
a81298fb24
commit
f8b8f9610f
31 files changed
+1076
-38
No files matched your search
@@ -0,0 +1,93 @@
|
||||
//! Threads (SYS_THREAD_SPAWN / SYS_THREAD_JOIN).
|
||||
//!
|
||||
//! The kernel uses the caller's stack verbatim and delivers one argument in
|
||||
//! %rdi. A thread cannot free the stack it runs on, so the [`JoinHandle`]
|
||||
//! owns it and frees it after a successful join; dropping an unjoined
|
||||
//! handle leaks the stack (the thread may still be running on it).
|
||||
|
||||
use alloc::alloc::{alloc, dealloc, Layout};
|
||||
use alloc::boxed::Box;
|
||||
|
||||
use crate::sys::{self, Status};
|
||||
|
||||
pub const DEFAULT_STACK_SIZE: usize = 256 * 1024;
|
||||
const STACK_ALIGN: usize = 16;
|
||||
|
||||
pub struct JoinHandle {
|
||||
tid: u32,
|
||||
stack: *mut u8,
|
||||
stack_layout: Layout,
|
||||
}
|
||||
|
||||
unsafe impl Send for JoinHandle {}
|
||||
|
||||
impl JoinHandle {
|
||||
pub fn id(&self) -> u32 {
|
||||
self.tid
|
||||
}
|
||||
|
||||
/// Wait for the thread to finish and return its exit code.
|
||||
pub fn join(self) -> Result<i32, Status> {
|
||||
let mut code: i32 = 0;
|
||||
let rc = unsafe {
|
||||
sys::syscall2(sys::SYS_THREAD_JOIN, self.tid as u64, &mut code as *mut i32 as u64)
|
||||
};
|
||||
// The kernel reports a bad tid as -1 (GenericFailure).
|
||||
Status::from_result(rc)?;
|
||||
unsafe { dealloc(self.stack, self.stack_layout) };
|
||||
Ok(code)
|
||||
}
|
||||
}
|
||||
|
||||
/// Spawn a thread running `f`; its return value becomes the exit code.
|
||||
pub fn spawn<F>(f: F) -> Result<JoinHandle, Status>
|
||||
where
|
||||
F: FnOnce() -> i32 + Send + 'static,
|
||||
{
|
||||
spawn_with_stack(DEFAULT_STACK_SIZE, f)
|
||||
}
|
||||
|
||||
pub fn spawn_with_stack<F>(stack_size: usize, f: F) -> Result<JoinHandle, Status>
|
||||
where
|
||||
F: FnOnce() -> i32 + Send + 'static,
|
||||
{
|
||||
let stack_layout = Layout::from_size_align(stack_size.max(4096), STACK_ALIGN)
|
||||
.map_err(|_| Status::InvalidArgument)?;
|
||||
let stack = unsafe { alloc(stack_layout) };
|
||||
if stack.is_null() {
|
||||
return Err(Status::OutOfMemory);
|
||||
}
|
||||
let closure = Box::into_raw(Box::new(f));
|
||||
let top = (stack as usize + stack_layout.size()) & !(STACK_ALIGN - 1);
|
||||
|
||||
let rc = unsafe {
|
||||
sys::syscall3(sys::SYS_THREAD_SPAWN, trampoline::<F> as *const () as u64, closure as u64, top as u64)
|
||||
};
|
||||
match Status::from_result(rc) {
|
||||
Ok(tid) => Ok(JoinHandle { tid: tid as u32, stack, stack_layout }),
|
||||
Err(e) => unsafe {
|
||||
drop(Box::from_raw(closure));
|
||||
dealloc(stack, stack_layout);
|
||||
Err(e)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn trampoline<F>(arg: *mut F) -> !
|
||||
where
|
||||
F: FnOnce() -> i32,
|
||||
{
|
||||
let f = unsafe { Box::from_raw(arg) };
|
||||
let code = f();
|
||||
unsafe { sys::syscall1(sys::SYS_THREAD_EXIT, code as u32 as u64) };
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
/// The calling thread's id (equals the process id on the main thread).
|
||||
pub fn current_id() -> u32 {
|
||||
unsafe { sys::syscall0(sys::SYS_THREAD_SELF) as u32 }
|
||||
}
|
||||
|
||||
pub fn yield_now() {
|
||||
unsafe { sys::syscall0(sys::SYS_YIELD) };
|
||||
}
|
||||
Reference in new issue
Block a user