rt: unify thread handling in runtime library

This commit is contained in:
Mark Poliakov 2024-11-20 17:55:57 +02:00
parent 09a0b01855
commit 1802c62558
11 changed files with 336 additions and 248 deletions

View File

@ -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);

View File

@ -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 {

View File

@ -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(())
}
}
}

View File

@ -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<()>;

View File

@ -1,4 +1,5 @@
#[derive(Debug)]
pub enum ThreadOption {
pub enum ThreadOption<'a> {
ThreadPointer(usize),
Name(&'a str),
}

View File

@ -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();
}

View File

@ -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
View File

@ -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;
}

View File

@ -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]

View File

@ -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();
}

View File

@ -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>() };
}