rt: unify thread handling in runtime library
This commit is contained in:
parent
09a0b01855
commit
1802c62558
@ -64,7 +64,7 @@ pub struct Thread {
|
||||
/// Unique thread ID
|
||||
pub id: ThreadId,
|
||||
/// Thread name
|
||||
pub name: Option<String>,
|
||||
pub name: IrqSafeRwLock<String>,
|
||||
/// Scheduling information
|
||||
pub sched: IrqSafeSpinlock<ThreadSchedulingInfo>,
|
||||
/// Low-level context details
|
||||
@ -112,7 +112,7 @@ impl Thread {
|
||||
) -> Arc<Self> {
|
||||
let thread = Arc::new(Self {
|
||||
id,
|
||||
name,
|
||||
name: IrqSafeRwLock::new(name.unwrap_or_default()),
|
||||
sched: IrqSafeSpinlock::new(ThreadSchedulingInfo {
|
||||
state: ThreadState::Suspended,
|
||||
in_queue: false,
|
||||
@ -204,6 +204,10 @@ impl Thread {
|
||||
self.context.replace(context)
|
||||
}
|
||||
|
||||
pub fn set_name(&self, name: impl Into<String>) {
|
||||
*self.name.write() = name.into();
|
||||
}
|
||||
|
||||
pub fn set_thread_pointer(&self, tp: usize) {
|
||||
unsafe { (*self.context.as_ptr()).set_thread_pointer(tp) };
|
||||
}
|
||||
@ -294,7 +298,7 @@ impl Thread {
|
||||
log::debug!(
|
||||
"Set breakpoint in {} ({:?}) @ {:#x}",
|
||||
self.id,
|
||||
self.name,
|
||||
*self.name.read(),
|
||||
address
|
||||
);
|
||||
let mut debug = self.debug.lock();
|
||||
@ -305,7 +309,7 @@ impl Thread {
|
||||
log::debug!(
|
||||
"Read memory in {} ({:?}) @ {:#x}",
|
||||
self.id,
|
||||
self.name,
|
||||
*self.name.read(),
|
||||
address
|
||||
);
|
||||
|
||||
@ -549,7 +553,7 @@ impl CurrentThread {
|
||||
} else {
|
||||
// Single step cleared
|
||||
if !debug.single_step {
|
||||
log::debug!("Clear single step ({} {:?})", self.id, self.name);
|
||||
log::debug!("Clear single step ({} {:?})", self.id, *self.name.read());
|
||||
frame.set_single_step(false);
|
||||
return true;
|
||||
}
|
||||
@ -582,7 +586,7 @@ impl CurrentThread {
|
||||
log::debug!(
|
||||
"Thread {} ({:?}) hit a breakpoint @ {:#x}, step={}",
|
||||
self.id,
|
||||
self.name,
|
||||
*self.name.read(),
|
||||
ip,
|
||||
debug.single_step
|
||||
);
|
||||
@ -619,7 +623,11 @@ impl CurrentThread {
|
||||
}
|
||||
|
||||
if let Some(signal) = self.signal_queue.pop() {
|
||||
log::debug!("{}: handle signal {signal:?}", self.id);
|
||||
log::debug!(
|
||||
"{} ({:?}): handle signal {signal:?}",
|
||||
self.id,
|
||||
*self.name.read()
|
||||
);
|
||||
|
||||
if signal == Signal::Debug {
|
||||
frame.set_single_step(true);
|
||||
|
@ -116,7 +116,7 @@ static IDT: IrqSafeRwLock<[Entry; SIZE]> = IrqSafeRwLock::new([Entry::NULL; SIZE
|
||||
|
||||
fn dump_user_exception(kind: ExceptionKind, frame: &ExceptionFrame) {
|
||||
let thread = Thread::current();
|
||||
warnln!("{:?} in {} ({:?})", kind, thread.id, thread.name);
|
||||
warnln!("{:?} in {} ({:?})", kind, thread.id, *thread.name.read());
|
||||
warnln!("ip = {:02x}:{:08x}", frame.cs, frame.rip);
|
||||
warnln!("cr3 = {:#010x}", CR3.get());
|
||||
if kind == ExceptionKind::PageFault {
|
||||
|
@ -309,6 +309,7 @@ pub(crate) fn get_thread_option(option: &mut ThreadOption) -> Result<(), Error>
|
||||
match option {
|
||||
// There're better ways to do this, don't ask the kernel
|
||||
ThreadOption::ThreadPointer(_) => Err(Error::InvalidOperation),
|
||||
ThreadOption::Name(_) => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -320,5 +321,11 @@ pub(crate) fn set_thread_option(option: &ThreadOption) -> Result<(), Error> {
|
||||
thread.set_thread_pointer(tp);
|
||||
Ok(())
|
||||
}
|
||||
&ThreadOption::Name(name) => {
|
||||
// Make a kernel-owned string
|
||||
log::debug!("{:?}: set thread name: {name:?}", thread.id);
|
||||
thread.set_name(name);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -83,8 +83,8 @@ syscall get_pid() -> ProcessId;
|
||||
syscall spawn_thread(options: &ThreadSpawnOptions) -> Result<u32>;
|
||||
syscall exit_thread() -> !;
|
||||
syscall wait_thread(tid: u32) -> Result<()>;
|
||||
syscall get_thread_option(option: &mut ThreadOption) -> Result<()>;
|
||||
syscall set_thread_option(option: &ThreadOption) -> Result<()>;
|
||||
syscall get_thread_option(option: &mut ThreadOption<'_>) -> Result<()>;
|
||||
syscall set_thread_option(option: &ThreadOption<'_>) -> Result<()>;
|
||||
|
||||
syscall nanosleep(duration: &Duration, remaining: &mut MaybeUninit<Duration>) -> Result<()>;
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
#[derive(Debug)]
|
||||
pub enum ThreadOption {
|
||||
pub enum ThreadOption<'a> {
|
||||
ThreadPointer(usize),
|
||||
Name(&'a str),
|
||||
}
|
||||
|
@ -10,11 +10,13 @@ use core::{
|
||||
use abi::{
|
||||
error::Error,
|
||||
mem::{MappingFlags, MappingSource},
|
||||
process::ThreadSpawnOptions,
|
||||
process::{ThreadOption, ThreadSpawnOptions},
|
||||
};
|
||||
use alloc::{boxed::Box, sync::Arc};
|
||||
|
||||
use super::signal;
|
||||
use crate::process::thread_local;
|
||||
|
||||
use super::{signal, thread_local::TlsImage};
|
||||
|
||||
/// Describes a runtime thread.
|
||||
///
|
||||
@ -22,7 +24,7 @@ use super::signal;
|
||||
pub struct Thread<R> {
|
||||
id: AtomicU32,
|
||||
// TODO mutex
|
||||
result: UnsafeCell<MaybeUninit<R>>,
|
||||
result: UnsafeCell<Option<R>>,
|
||||
}
|
||||
|
||||
/// Describes a "handle" to some runtime thread. This is what gets returned to the caller
|
||||
@ -56,7 +58,7 @@ pub enum ThreadStack {
|
||||
/// Describes the thread's function that will get entered once the thread starts running.
|
||||
///
|
||||
/// `A` parameter denotes the argument type that will be passed to the thread.
|
||||
pub enum ThreadFunction<A, R: Send> {
|
||||
pub enum ThreadFunction<A, R> {
|
||||
/// A boxed closure will be called.
|
||||
Closure(Box<dyn FnOnce(A) -> R + 'static>),
|
||||
/// A function pointer will be called.
|
||||
@ -64,27 +66,30 @@ pub enum ThreadFunction<A, R: Send> {
|
||||
}
|
||||
|
||||
/// Describes how a thread should be constructed
|
||||
pub struct ThreadCreateInfo<A, R: Send> {
|
||||
pub struct ThreadCreateInfo<A, R> {
|
||||
/// See [ThreadSignalStack].
|
||||
pub signal_stack: ThreadSignalStack,
|
||||
/// See [ThreadStack].
|
||||
pub stack: ThreadStack,
|
||||
/// See [ThreadFunction].
|
||||
pub entry: ThreadFunction<A, R>,
|
||||
/// Image used to initialize the thread's TLS
|
||||
pub tls_image: Option<&'static TlsImage>,
|
||||
}
|
||||
|
||||
struct ThreadArgument<A, R: Send> {
|
||||
struct ThreadArgument<A, R> {
|
||||
entry: Option<(A, ThreadFunction<A, R>)>,
|
||||
signal_stack: ThreadSignalStack,
|
||||
thread_self: Arc<Thread<R>>,
|
||||
set_thread_self: bool,
|
||||
tls_image: Option<&'static TlsImage>,
|
||||
}
|
||||
|
||||
// TODO maybe put this under a `thread-self` feature and avoid a TLS allocation?
|
||||
#[thread_local]
|
||||
static mut SELF: usize = 0;
|
||||
|
||||
impl<R: Send> Thread<R> {
|
||||
impl<R> Thread<R> {
|
||||
/// Creates a new thread with the requested options and passes it an argument.
|
||||
pub fn spawn<A>(
|
||||
info: ThreadCreateInfo<A, R>,
|
||||
@ -101,7 +106,7 @@ impl<R: Send> Thread<R> {
|
||||
|
||||
let thread = Arc::new(Thread::<R> {
|
||||
id: AtomicU32::new(0),
|
||||
result: UnsafeCell::new(MaybeUninit::uninit()),
|
||||
result: UnsafeCell::new(None),
|
||||
});
|
||||
|
||||
let thread_argument = Box::into_raw(Box::new(ThreadArgument {
|
||||
@ -109,6 +114,7 @@ impl<R: Send> Thread<R> {
|
||||
thread_self: thread.clone(),
|
||||
signal_stack: info.signal_stack,
|
||||
set_thread_self: runtime,
|
||||
tls_image: info.tls_image,
|
||||
}))
|
||||
.addr();
|
||||
|
||||
@ -124,6 +130,16 @@ impl<R: Send> Thread<R> {
|
||||
Ok(ThreadHandle { stack, thread })
|
||||
}
|
||||
|
||||
/// Returns this thread's ID.
|
||||
pub fn id(&self) -> u32 {
|
||||
self.id.load(Ordering::Acquire)
|
||||
}
|
||||
|
||||
/// Sets the current thread name.
|
||||
pub fn set_name(name: &str) {
|
||||
unsafe { crate::sys::set_thread_option(&ThreadOption::Name(name)).ok() };
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// This function has to be called with the same `Self` type as the thread it was created with.
|
||||
@ -138,20 +154,26 @@ impl<R: Send> Thread<R> {
|
||||
}
|
||||
|
||||
extern "C" fn thread_entry<A>(raw: usize) -> ! {
|
||||
crate::debug_trace!("thread_entry {raw:#x}");
|
||||
let raw: *mut ThreadArgument<A, R> = ptr::with_exposed_provenance_mut(raw);
|
||||
// This scope will ensure all the stuff is dropped before thread exit is called.
|
||||
{
|
||||
let mut argument = unsafe { Box::from_raw(raw) };
|
||||
|
||||
// Setup TLS as soon as possible
|
||||
if let Err(err) = unsafe { thread_local::init_tls(argument.tls_image, true) } {
|
||||
crate::debug_trace!("thread_entry failed: TLS init error: {err:?}");
|
||||
// TODO result is uninit
|
||||
unsafe { crate::sys::exit_thread() };
|
||||
}
|
||||
|
||||
// TODO there will be a better way to do this, I promise
|
||||
while argument.thread_self.id.load(Ordering::Acquire) == 0 {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
crate::debug_trace!(
|
||||
"thread_entry id={}",
|
||||
argument.thread_self.id.load(Ordering::Acquire)
|
||||
);
|
||||
|
||||
// Insert compiler fence just in case to prevent it from trying to go into
|
||||
// (uninitialized) TLS
|
||||
core::sync::atomic::compiler_fence(Ordering::Release);
|
||||
|
||||
// Setup SELF if needed
|
||||
if argument.set_thread_self {
|
||||
@ -159,7 +181,6 @@ impl<R: Send> Thread<R> {
|
||||
SELF = Arc::into_raw(argument.thread_self.clone()).addr();
|
||||
debug_assert!(Arc::ptr_eq(&Thread::current(), &argument.thread_self));
|
||||
}
|
||||
crate::debug_trace!("thread_entry set SELF!!!");
|
||||
}
|
||||
|
||||
// Setup signal stack if needed
|
||||
@ -180,18 +201,22 @@ impl<R: Send> Thread<R> {
|
||||
|
||||
let result_ptr = argument.thread_self.result.get();
|
||||
unsafe {
|
||||
(*result_ptr).write(result);
|
||||
(*result_ptr).insert(result);
|
||||
}
|
||||
}
|
||||
|
||||
crate::debug_trace!("thread_entry exit_thread()");
|
||||
unsafe { crate::sys::exit_thread() };
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Send> ThreadHandle<R> {
|
||||
impl<R> ThreadHandle<R> {
|
||||
/// Returns the thread ID this handle is related to.
|
||||
pub fn id(&self) -> u32 {
|
||||
self.thread.id.load(Ordering::Acquire)
|
||||
}
|
||||
|
||||
/// Waits for the thread to finish and returns its result.
|
||||
pub fn join(self) -> Result<R, Error> {
|
||||
pub fn join(self) -> Result<Option<R>, Error> {
|
||||
// TODO prevent threads from attempting to join themselves?
|
||||
unsafe {
|
||||
crate::sys::wait_thread(self.thread.id.load(Ordering::Acquire))?;
|
||||
@ -204,7 +229,7 @@ impl<R: Send> ThreadHandle<R> {
|
||||
/// # Panics
|
||||
///
|
||||
/// Will panic if the kernel returns any error besides [Error::Interrupted].
|
||||
pub fn join_uninterruptible(self) -> R {
|
||||
pub fn join_uninterruptible(self) -> Option<R> {
|
||||
loop {
|
||||
match unsafe { crate::sys::wait_thread(self.thread.id.load(Ordering::Acquire)) } {
|
||||
Ok(result) => (),
|
||||
@ -215,8 +240,8 @@ impl<R: Send> ThreadHandle<R> {
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn into_result(self) -> R {
|
||||
(*self.thread.result.get()).assume_init_read()
|
||||
unsafe fn into_result(self) -> Option<R> {
|
||||
(*self.thread.result.get()).take()
|
||||
}
|
||||
}
|
||||
|
||||
@ -237,6 +262,21 @@ impl OwnedStack {
|
||||
|
||||
impl Drop for OwnedStack {
|
||||
fn drop(&mut self) {
|
||||
crate::debug_trace!("Drop OwnedStack {:#x?}", self.base..self.base + self.size);
|
||||
unsafe { crate::sys::unmap_memory(self.base, self.size) }.ok();
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the `SELF` TLS variable
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Must only be called once by the runtime library during setup. TLS must be initialized prior to
|
||||
/// this call.
|
||||
pub unsafe fn track_main<R>() {
|
||||
let main = Arc::new(Thread::<R> {
|
||||
id: AtomicU32::new(0),
|
||||
result: UnsafeCell::new(None),
|
||||
});
|
||||
SELF = Arc::into_raw(main).addr();
|
||||
}
|
||||
|
@ -131,11 +131,12 @@ impl Dtv {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_key(list: &[*mut c_void], key: usize) -> *mut c_void {
|
||||
fn get_key(list: &[*mut c_void], key: usize) -> Option<*mut c_void> {
|
||||
if key == 0 || key > list.len() {
|
||||
panic!("Out-of-bounds TLS key: {key}");
|
||||
None
|
||||
} else {
|
||||
Some(list[key - 1])
|
||||
}
|
||||
list[key - 1]
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@ -152,7 +153,7 @@ impl Dtv {
|
||||
///
|
||||
/// Will panic if key == 0.
|
||||
/// Will panic if key is longer than the DTV itself.
|
||||
pub fn get_specific(&self, key: usize) -> *mut c_void {
|
||||
pub fn get_specific(&self, key: usize) -> Option<*mut c_void> {
|
||||
Self::get_key(&self.specific, key)
|
||||
}
|
||||
|
||||
@ -162,7 +163,10 @@ impl Dtv {
|
||||
///
|
||||
/// Will panic if key == 0.
|
||||
/// Will panic if key is longer than the DTV itself.
|
||||
pub fn set_specific(&mut self, key: usize, value: *mut c_void) {
|
||||
pub fn set_specific(&mut self, key: usize, value: *mut c_void, grow: bool) {
|
||||
if key > self.entries.len() && grow {
|
||||
self.specific.resize(key, null_mut());
|
||||
}
|
||||
Self::set_key(&mut self.specific, key, value)
|
||||
}
|
||||
|
||||
@ -179,7 +183,7 @@ impl Dtv {
|
||||
/// Will panic if key == 0.
|
||||
/// Will panic if key is larger than the DTV itself.
|
||||
pub fn get(&self, key: usize) -> *mut c_void {
|
||||
Self::get_key(&self.entries, key)
|
||||
Self::get_key(&self.entries, key).expect("Out-of-bounds DTV key")
|
||||
}
|
||||
|
||||
/// Sets a DTV entry, growing the DTV allocation if necessary
|
||||
|
37
test.c
37
test.c
@ -1,33 +1,26 @@
|
||||
#include <sys/time.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
|
||||
static void silence_sigint(int signum) {}
|
||||
static void *thread(void *arg) {
|
||||
pthread_t self = pthread_self();
|
||||
printf("[child] pthread_self() = %u\n", self);
|
||||
sleep(3);
|
||||
return arg;
|
||||
}
|
||||
|
||||
int main(int argc, const char **argv) {
|
||||
signal(silence_sigint, SIGINT);
|
||||
struct timespec duration;
|
||||
struct timespec remaining;
|
||||
pthread_t id;
|
||||
|
||||
while (1) {
|
||||
duration.tv_sec = 3;
|
||||
duration.tv_nsec = 0;
|
||||
assert(pthread_create(&id, NULL, thread, (void *) 0x1234) == 0);
|
||||
|
||||
int result = nanosleep(&duration, &remaining);
|
||||
pthread_t self = pthread_self();
|
||||
printf("[main] pthread_self() = %u\n", self);
|
||||
|
||||
void *result;
|
||||
assert(pthread_join(id, &result) == 0);
|
||||
printf("[main] result: %p\n", result);
|
||||
|
||||
if (result != 0) {
|
||||
assert(errno == EINTR);
|
||||
printf(
|
||||
"EINTR: remaining: sec=%lu, nsec=%u\n",
|
||||
(unsigned long) remaining.tv_sec,
|
||||
(unsigned int) remaining.tv_nsec
|
||||
);
|
||||
} else {
|
||||
printf("NO EINTR\n");
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -74,16 +74,16 @@ unsafe extern "C" fn pthread_getschedparam(
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn pthread_join(thread: pthread_t, result: *mut *mut c_void) -> CIntZeroResult {
|
||||
let thread = Thread::join(thread)?;
|
||||
let value = Thread::join(thread)?;
|
||||
if let Some(result) = NonNull::new(result) {
|
||||
result.write(thread.result());
|
||||
result.write(value);
|
||||
}
|
||||
CIntZeroResult::SUCCESS
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn pthread_self() -> pthread_t {
|
||||
Thread::this().expect("pthread_self() failed").id()
|
||||
Thread::this().expect("pthread_self() failed")
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
|
@ -70,7 +70,7 @@ static PANIC_COUNT: AtomicUsize = AtomicUsize::new(0);
|
||||
fn panic_handler(pi: &core::panic::PanicInfo) -> ! {
|
||||
use core::{fmt::Write, sync::atomic::Ordering};
|
||||
|
||||
use crate::{error::EResult, thread::Thread};
|
||||
use crate::{error::EResult, process, thread::Thread};
|
||||
|
||||
match PANIC_COUNT.fetch_add(1, Ordering::Relaxed) {
|
||||
0 => {
|
||||
@ -79,10 +79,10 @@ fn panic_handler(pi: &core::panic::PanicInfo) -> ! {
|
||||
|
||||
writeln!(printer, "!!! ygglibc panic !!!").ok();
|
||||
if let EResult::Ok(this) = pthread_self {
|
||||
if this.is_main() {
|
||||
if this == 0 {
|
||||
writeln!(printer, "* Main thread panicked *").ok();
|
||||
} else {
|
||||
writeln!(printer, "* Thread {} panicked*", this.id()).ok();
|
||||
writeln!(printer, "* Thread {} panicked*", this).ok();
|
||||
}
|
||||
}
|
||||
if let Some(location) = pi.location() {
|
||||
@ -98,5 +98,5 @@ fn panic_handler(pi: &core::panic::PanicInfo) -> ! {
|
||||
_ => {}
|
||||
}
|
||||
|
||||
Thread::abort_current()
|
||||
process::abort();
|
||||
}
|
||||
|
@ -8,7 +8,10 @@ use core::{
|
||||
use alloc::{boxed::Box, collections::BTreeMap, sync::Arc};
|
||||
use yggdrasil_rt::{
|
||||
mem::{MappingFlags, MappingSource},
|
||||
process::{thread_local, ProgramArgumentInner, ThreadSpawnOptions},
|
||||
process::{
|
||||
thread::{self as rt, ThreadCreateInfo},
|
||||
thread_local, ProgramArgumentInner, ThreadSpawnOptions,
|
||||
},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@ -23,200 +26,236 @@ use crate::{
|
||||
|
||||
pub mod tls;
|
||||
|
||||
static THREADS: Mutex<BTreeMap<pthread_t, Arc<Thread>>> = Mutex::new(BTreeMap::new());
|
||||
static THREADS: Mutex<BTreeMap<pthread_t, Thread>> = Mutex::new(BTreeMap::new());
|
||||
|
||||
#[thread_local]
|
||||
static SELF: OnceCell<Arc<Thread>> = OnceCell::new();
|
||||
|
||||
enum ThreadStack {
|
||||
Owned(usize, usize),
|
||||
Borrowed,
|
||||
}
|
||||
|
||||
struct ThreadArgument {
|
||||
entry: extern "C" fn(*mut c_void) -> *mut c_void,
|
||||
argument: *mut c_void,
|
||||
thread: Arc<Thread>,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct Thread {
|
||||
id: AtomicU32,
|
||||
stack: ThreadStack,
|
||||
result: AtomicPtr<c_void>,
|
||||
handle: rt::ThreadHandle<*mut c_void>,
|
||||
}
|
||||
|
||||
impl Thread {
|
||||
fn main() -> Self {
|
||||
Self {
|
||||
id: AtomicU32::new(0),
|
||||
stack: ThreadStack::Borrowed,
|
||||
result: AtomicPtr::new(null_mut()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_main(&self) -> bool {
|
||||
self.id.load(Ordering::Acquire) == 0
|
||||
}
|
||||
|
||||
pub fn spawn(
|
||||
attr: Option<&pthread_attr_t>,
|
||||
entry: extern "C" fn(*mut c_void) -> *mut c_void,
|
||||
argument: *mut c_void,
|
||||
) -> EResult<pthread_t> {
|
||||
// TODO move defaults somewhere else
|
||||
let stack_size = attr
|
||||
.as_ref()
|
||||
.map(|attr| attr.stack_size)
|
||||
.unwrap_or(4096 * 16);
|
||||
// If no custom stack was specified via pthread_attr_setstack, allocate owned stack
|
||||
let stack_base = attr.as_ref().map(|attr| attr.stack).unwrap_or(null_mut());
|
||||
|
||||
let stack_size = stack_size.max(4096 * 8);
|
||||
|
||||
let (stack, stack_top) = match stack_base.is_null() {
|
||||
// program-supplied stack
|
||||
false => todo!(),
|
||||
// NULL or unspecified stack
|
||||
true => {
|
||||
let base = unsafe {
|
||||
yggdrasil_rt::sys::map_memory(
|
||||
None,
|
||||
stack_size,
|
||||
MappingFlags::WRITE,
|
||||
&MappingSource::Anonymous,
|
||||
)
|
||||
}?;
|
||||
let top = base + stack_size;
|
||||
yggdrasil_rt::debug_trace!("Allocate stack: {:#x?}", base..top);
|
||||
(ThreadStack::Owned(base, stack_size), top)
|
||||
}
|
||||
// TODO honor attr stack
|
||||
let info = ThreadCreateInfo {
|
||||
signal_stack: rt::ThreadSignalStack::Allocate(4096 * 8),
|
||||
stack: rt::ThreadStack::Allocate(4096 * 16),
|
||||
entry: rt::ThreadFunction::Closure(Box::new(move |arg| entry(arg))),
|
||||
#[allow(static_mut_refs)]
|
||||
tls_image: unsafe { tls::TLS_IMAGE.as_ref() },
|
||||
};
|
||||
|
||||
let thread = Arc::new(Self {
|
||||
id: AtomicU32::new(0),
|
||||
result: AtomicPtr::new(null_mut()),
|
||||
stack,
|
||||
});
|
||||
let argument = Box::into_raw(Box::new(ThreadArgument {
|
||||
entry,
|
||||
argument,
|
||||
thread: thread.clone(),
|
||||
}));
|
||||
|
||||
let options = ThreadSpawnOptions {
|
||||
entry: Self::thread_entry,
|
||||
argument: argument.addr(),
|
||||
stack_top,
|
||||
};
|
||||
|
||||
let id = unsafe { yggdrasil_rt::sys::spawn_thread(&options) }?;
|
||||
thread.id.store(id, Ordering::Release);
|
||||
|
||||
THREADS.lock().insert(id, thread);
|
||||
|
||||
let handle = rt::Thread::spawn(info, argument, true)?;
|
||||
let id = handle.id();
|
||||
THREADS.lock().insert(id, Thread { handle });
|
||||
EResult::Ok(id)
|
||||
}
|
||||
|
||||
pub fn join(id: pthread_t) -> EResult<Arc<Thread>> {
|
||||
unsafe { yggdrasil_rt::sys::wait_thread(id) }?;
|
||||
let thread = THREADS
|
||||
.lock()
|
||||
.remove(&id)
|
||||
.expect("wait_thread() succeeded but thread not registered?");
|
||||
EResult::Ok(thread)
|
||||
pub fn join(id: pthread_t) -> EResult<*mut c_void> {
|
||||
let thread = THREADS.lock().remove(&id).e_ok_or(errno::EINVAL)?;
|
||||
// TODO handle EINTR during join
|
||||
let result = thread.handle.join_uninterruptible().unwrap_or(null_mut());
|
||||
EResult::Ok(result)
|
||||
}
|
||||
|
||||
pub fn id(&self) -> pthread_t {
|
||||
self.id.load(Ordering::Acquire)
|
||||
}
|
||||
|
||||
pub fn this() -> EResult<Arc<Thread>> {
|
||||
SELF.get().cloned().e_ok_or(errno::EINVAL)
|
||||
}
|
||||
|
||||
unsafe fn set_this(thread: Arc<Self>) {
|
||||
if SELF.set(thread).is_err() {
|
||||
unreachable!("pthread_self already initialized?")
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn clear_this() {
|
||||
let this = SELF.get().expect("pthread_self == NULL");
|
||||
Arc::decrement_strong_count(this);
|
||||
}
|
||||
|
||||
pub fn result(&self) -> *mut c_void {
|
||||
self.result.load(Ordering::Acquire)
|
||||
}
|
||||
|
||||
pub fn abort_current() -> ! {
|
||||
let this = Self::this();
|
||||
if let EResult::Ok(this) = this
|
||||
&& !this.is_main()
|
||||
{
|
||||
this.result.store(null_mut(), Ordering::Release);
|
||||
unsafe { yggdrasil_rt::sys::exit_thread() };
|
||||
} else {
|
||||
process::abort();
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn thread_entry(argument: usize) -> ! {
|
||||
// Set up TLS as soon as possible. Note the `force = true` parameter, because the image
|
||||
// contains "already initialized" tag, which only matters for the main thread.
|
||||
#[allow(static_mut_refs)]
|
||||
if let Err(err) = unsafe { thread_local::init_tls(tls::TLS_IMAGE.as_ref(), true) } {
|
||||
yggdrasil_rt::debug_trace!("thread_entry failed: TLS init error: {err:?}");
|
||||
unsafe { yggdrasil_rt::sys::exit_thread() };
|
||||
}
|
||||
|
||||
{
|
||||
assert_ne!(argument, 0);
|
||||
|
||||
let argument: Box<ThreadArgument> =
|
||||
unsafe { Box::from_raw(ptr::with_exposed_provenance_mut(argument)) };
|
||||
yggdrasil_rt::debug_trace!(
|
||||
"thread_entry entry={:p}, argument={:p}",
|
||||
argument.entry,
|
||||
argument.argument
|
||||
);
|
||||
|
||||
// TODO better way to initialize the thread ID
|
||||
while argument.thread.id.load(Ordering::Acquire) == 0 {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
|
||||
unsafe {
|
||||
Self::setup_self(&argument.thread);
|
||||
}
|
||||
|
||||
let result = (argument.entry)(argument.argument);
|
||||
argument.thread.result.store(result, Ordering::Release);
|
||||
|
||||
unsafe { Self::clear_this() };
|
||||
}
|
||||
|
||||
// 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);
|
||||
pub fn this() -> EResult<pthread_t> {
|
||||
EResult::Ok(unsafe { rt::Thread::<*mut c_void>::current().id() })
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Thread {
|
||||
fn drop(&mut self) {
|
||||
yggdrasil_rt::debug_trace!("Drop thread {:?}", self.id);
|
||||
if let &ThreadStack::Owned(base, size) = &self.stack {
|
||||
yggdrasil_rt::debug_trace!("Drop stack {:#x?}", base..base + size);
|
||||
unsafe { yggdrasil_rt::sys::unmap_memory(base, size) }.ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
// #[thread_local]
|
||||
// static SELF: OnceCell<Arc<Thread>> = OnceCell::new();
|
||||
//
|
||||
// enum ThreadStack {
|
||||
// Owned(usize, usize),
|
||||
// Borrowed,
|
||||
// }
|
||||
//
|
||||
// struct ThreadArgument {
|
||||
// entry: extern "C" fn(*mut c_void) -> *mut c_void,
|
||||
// argument: *mut c_void,
|
||||
// thread: Arc<Thread>,
|
||||
// }
|
||||
//
|
||||
// #[repr(C)]
|
||||
// pub struct Thread {
|
||||
// id: AtomicU32,
|
||||
// stack: ThreadStack,
|
||||
// result: AtomicPtr<c_void>,
|
||||
// }
|
||||
//
|
||||
// impl Thread {
|
||||
// fn main() -> Self {
|
||||
// Self {
|
||||
// id: AtomicU32::new(0),
|
||||
// stack: ThreadStack::Borrowed,
|
||||
// result: AtomicPtr::new(null_mut()),
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// pub fn is_main(&self) -> bool {
|
||||
// self.id.load(Ordering::Acquire) == 0
|
||||
// }
|
||||
//
|
||||
// pub fn spawn(
|
||||
// attr: Option<&pthread_attr_t>,
|
||||
// entry: extern "C" fn(*mut c_void) -> *mut c_void,
|
||||
// argument: *mut c_void,
|
||||
// ) -> EResult<pthread_t> {
|
||||
// // TODO move defaults somewhere else
|
||||
// let stack_size = attr
|
||||
// .as_ref()
|
||||
// .map(|attr| attr.stack_size)
|
||||
// .unwrap_or(4096 * 16);
|
||||
// // If no custom stack was specified via pthread_attr_setstack, allocate owned stack
|
||||
// let stack_base = attr.as_ref().map(|attr| attr.stack).unwrap_or(null_mut());
|
||||
//
|
||||
// let stack_size = stack_size.max(4096 * 8);
|
||||
//
|
||||
// let (stack, stack_top) = match stack_base.is_null() {
|
||||
// // program-supplied stack
|
||||
// false => todo!(),
|
||||
// // NULL or unspecified stack
|
||||
// true => {
|
||||
// let base = unsafe {
|
||||
// yggdrasil_rt::sys::map_memory(
|
||||
// None,
|
||||
// stack_size,
|
||||
// MappingFlags::WRITE,
|
||||
// &MappingSource::Anonymous,
|
||||
// )
|
||||
// }?;
|
||||
// let top = base + stack_size;
|
||||
// yggdrasil_rt::debug_trace!("Allocate stack: {:#x?}", base..top);
|
||||
// (ThreadStack::Owned(base, stack_size), top)
|
||||
// }
|
||||
// };
|
||||
//
|
||||
// let thread = Arc::new(Self {
|
||||
// id: AtomicU32::new(0),
|
||||
// result: AtomicPtr::new(null_mut()),
|
||||
// stack,
|
||||
// });
|
||||
// let argument = Box::into_raw(Box::new(ThreadArgument {
|
||||
// entry,
|
||||
// argument,
|
||||
// thread: thread.clone(),
|
||||
// }));
|
||||
//
|
||||
// let options = ThreadSpawnOptions {
|
||||
// entry: Self::thread_entry,
|
||||
// argument: argument.addr(),
|
||||
// stack_top,
|
||||
// };
|
||||
//
|
||||
// let id = unsafe { yggdrasil_rt::sys::spawn_thread(&options) }?;
|
||||
// thread.id.store(id, Ordering::Release);
|
||||
//
|
||||
// THREADS.lock().insert(id, thread);
|
||||
//
|
||||
// EResult::Ok(id)
|
||||
// }
|
||||
//
|
||||
// pub fn join(id: pthread_t) -> EResult<Arc<Thread>> {
|
||||
// unsafe { yggdrasil_rt::sys::wait_thread(id) }?;
|
||||
// let thread = THREADS
|
||||
// .lock()
|
||||
// .remove(&id)
|
||||
// .expect("wait_thread() succeeded but thread not registered?");
|
||||
// EResult::Ok(thread)
|
||||
// }
|
||||
//
|
||||
// pub fn id(&self) -> pthread_t {
|
||||
// self.id.load(Ordering::Acquire)
|
||||
// }
|
||||
//
|
||||
// pub fn this() -> EResult<Arc<Thread>> {
|
||||
// SELF.get().cloned().e_ok_or(errno::EINVAL)
|
||||
// }
|
||||
//
|
||||
// unsafe fn set_this(thread: Arc<Self>) {
|
||||
// if SELF.set(thread).is_err() {
|
||||
// unreachable!("pthread_self already initialized?")
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// unsafe fn clear_this() {
|
||||
// let this = SELF.get().expect("pthread_self == NULL");
|
||||
// Arc::decrement_strong_count(this);
|
||||
// }
|
||||
//
|
||||
// pub fn result(&self) -> *mut c_void {
|
||||
// self.result.load(Ordering::Acquire)
|
||||
// }
|
||||
//
|
||||
// pub fn abort_current() -> ! {
|
||||
// let this = Self::this();
|
||||
// if let EResult::Ok(this) = this
|
||||
// && !this.is_main()
|
||||
// {
|
||||
// this.result.store(null_mut(), Ordering::Release);
|
||||
// unsafe { yggdrasil_rt::sys::exit_thread() };
|
||||
// } else {
|
||||
// process::abort();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// extern "C" fn thread_entry(argument: usize) -> ! {
|
||||
// // Set up TLS as soon as possible. Note the `force = true` parameter, because the image
|
||||
// // contains "already initialized" tag, which only matters for the main thread.
|
||||
// #[allow(static_mut_refs)]
|
||||
// if let Err(err) = unsafe { thread_local::init_tls(tls::TLS_IMAGE.as_ref(), true) } {
|
||||
// yggdrasil_rt::debug_trace!("thread_entry failed: TLS init error: {err:?}");
|
||||
// unsafe { yggdrasil_rt::sys::exit_thread() };
|
||||
// }
|
||||
//
|
||||
// {
|
||||
// assert_ne!(argument, 0);
|
||||
//
|
||||
// let argument: Box<ThreadArgument> =
|
||||
// unsafe { Box::from_raw(ptr::with_exposed_provenance_mut(argument)) };
|
||||
// yggdrasil_rt::debug_trace!(
|
||||
// "thread_entry entry={:p}, argument={:p}",
|
||||
// argument.entry,
|
||||
// argument.argument
|
||||
// );
|
||||
//
|
||||
// // TODO better way to initialize the thread ID
|
||||
// while argument.thread.id.load(Ordering::Acquire) == 0 {
|
||||
// core::hint::spin_loop();
|
||||
// }
|
||||
//
|
||||
// unsafe {
|
||||
// Self::setup_self(&argument.thread);
|
||||
// }
|
||||
//
|
||||
// let result = (argument.entry)(argument.argument);
|
||||
// argument.thread.result.store(result, Ordering::Release);
|
||||
//
|
||||
// unsafe { Self::clear_this() };
|
||||
// }
|
||||
//
|
||||
// // 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 {
|
||||
// fn drop(&mut self) {
|
||||
// yggdrasil_rt::debug_trace!("Drop thread {:?}", self.id);
|
||||
// if let &ThreadStack::Owned(base, size) = &self.stack {
|
||||
// yggdrasil_rt::debug_trace!("Drop stack {:#x?}", base..base + size);
|
||||
// unsafe { yggdrasil_rt::sys::unmap_memory(base, size) }.ok();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
pub fn init_main_thread(arg: &ProgramArgumentInner) {
|
||||
// Will create a TLS for the main thread (if not done already).
|
||||
@ -231,9 +270,5 @@ pub fn init_main_thread(arg: &ProgramArgumentInner) {
|
||||
tls::TLS_IMAGE = tls_image;
|
||||
}
|
||||
|
||||
// TODO set thread ID for main
|
||||
let main = Arc::new(Thread::main());
|
||||
unsafe {
|
||||
Thread::set_this(main);
|
||||
}
|
||||
unsafe { rt::track_main::<*mut c_void>() };
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user