libc: basic pthread spawn/join
This commit is contained in:
parent
465fc53e02
commit
17eca4c0c0
27
lib/runtime/src/process/aarch64.rs
Normal file
27
lib/runtime/src/process/aarch64.rs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
#![allow(missing_docs)]
|
||||||
|
|
||||||
|
pub fn get_tls() -> usize {
|
||||||
|
let tp: usize;
|
||||||
|
unsafe {
|
||||||
|
core::arch::asm!("mrs {0}, tpidr_el0", out(reg) tp);
|
||||||
|
}
|
||||||
|
tp
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn set_pthread_self(value: usize) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pthread_self() -> usize {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn thread_local_area_impl() -> &'static mut [*mut u8] {
|
||||||
|
let tp = get_tls();
|
||||||
|
|
||||||
|
// Kernel reserves two words below the TP
|
||||||
|
assert_eq!(tp % size_of::<usize>(), 0);
|
||||||
|
let tp = tp - size_of::<usize>() * 2;
|
||||||
|
|
||||||
|
unsafe { super::thread_local_area_common(tp) }
|
||||||
|
}
|
27
lib/runtime/src/process/i686.rs
Normal file
27
lib/runtime/src/process/i686.rs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
#![allow(missing_docs)]
|
||||||
|
|
||||||
|
pub fn get_tls() -> usize {
|
||||||
|
let tp: usize;
|
||||||
|
unsafe {
|
||||||
|
core::arch::asm!("mov %gs:0, {0}", out(reg) tp, options(att_syntax));
|
||||||
|
}
|
||||||
|
tp
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn set_pthread_self(value: usize) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pthread_self() -> usize {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn thread_local_area_impl() -> &'static mut [*mut u8] {
|
||||||
|
let tp = get_tls();
|
||||||
|
|
||||||
|
// Skip self-pointer
|
||||||
|
assert_eq!(tp % size_of::<usize>(), 0);
|
||||||
|
let tp = tp + size_of::<usize>() * 2;
|
||||||
|
|
||||||
|
unsafe { super::thread_local_area_common(tp) }
|
||||||
|
}
|
@ -11,6 +11,23 @@ use crate::sys;
|
|||||||
|
|
||||||
const THREAD_LOCAL_AREA_SIZE: usize = 4096 / size_of::<usize>();
|
const THREAD_LOCAL_AREA_SIZE: usize = 4096 / size_of::<usize>();
|
||||||
|
|
||||||
|
#[cfg(any(target_arch = "aarch64", rust_analyzer))]
|
||||||
|
mod aarch64;
|
||||||
|
#[cfg(any(target_arch = "aarch64", rust_analyzer))]
|
||||||
|
use aarch64 as imp;
|
||||||
|
|
||||||
|
#[cfg(any(target_arch = "x86_64", rust_analyzer))]
|
||||||
|
mod x86_64;
|
||||||
|
#[cfg(any(target_arch = "x86_64", rust_analyzer))]
|
||||||
|
use x86_64 as imp;
|
||||||
|
|
||||||
|
#[cfg(any(target_arch = "x86", rust_analyzer))]
|
||||||
|
mod i686;
|
||||||
|
#[cfg(any(target_arch = "x86", rust_analyzer))]
|
||||||
|
use i686 as imp;
|
||||||
|
|
||||||
|
pub use imp::{get_tls, pthread_self, set_pthread_self};
|
||||||
|
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// `tp` must be a proper and aligned pointer to a pair of [usize] words.
|
/// `tp` must be a proper and aligned pointer to a pair of [usize] words.
|
||||||
@ -41,49 +58,8 @@ unsafe fn thread_local_area_common(tp: usize) -> &'static mut [*mut u8] {
|
|||||||
unsafe { core::slice::from_raw_parts_mut(base, len) }
|
unsafe { core::slice::from_raw_parts_mut(base, len) }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64", rust_analyzer))]
|
|
||||||
fn thread_local_area_impl() -> &'static mut [*mut u8] {
|
|
||||||
#[cfg(any(target_arch = "x86", rust_analyzer))]
|
|
||||||
fn get_tls() -> usize {
|
|
||||||
let tp: usize;
|
|
||||||
unsafe {
|
|
||||||
core::arch::asm!("mov %gs:0, {0}", out(reg) tp, options(att_syntax));
|
|
||||||
}
|
|
||||||
tp
|
|
||||||
}
|
|
||||||
#[cfg(any(target_arch = "x86_64", rust_analyzer))]
|
|
||||||
fn get_tls() -> usize {
|
|
||||||
let tp: usize;
|
|
||||||
unsafe {
|
|
||||||
core::arch::asm!("mov %fs:0, {0}", out(reg) tp, options(att_syntax));
|
|
||||||
}
|
|
||||||
tp
|
|
||||||
}
|
|
||||||
|
|
||||||
let tp = get_tls();
|
|
||||||
|
|
||||||
// Skip self-pointer
|
|
||||||
assert_eq!(tp % size_of::<usize>(), 0);
|
|
||||||
let tp = tp + size_of::<usize>() * 2;
|
|
||||||
|
|
||||||
unsafe { thread_local_area_common(tp) }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(any(target_arch = "aarch64", rust_analyzer))]
|
|
||||||
fn thread_local_area_impl() -> &'static mut [*mut u8] {
|
|
||||||
let tp: usize;
|
|
||||||
unsafe {
|
|
||||||
core::arch::asm!("mrs {0}, tpidr_el0", out(reg) tp);
|
|
||||||
}
|
|
||||||
// Kernel reserves two words below the TP
|
|
||||||
assert_eq!(tp % size_of::<usize>(), 0);
|
|
||||||
let tp = tp - size_of::<usize>() * 2;
|
|
||||||
|
|
||||||
unsafe { thread_local_area_common(tp) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a reference to the thread-local area, which contains thread-local pointers
|
/// Returns a reference to the thread-local area, which contains thread-local pointers
|
||||||
pub fn thread_local_area() -> &'static mut [*mut u8] {
|
pub fn thread_local_area() -> &'static mut [*mut u8] {
|
||||||
// TODO grow thread local areas based on size requested?
|
// TODO grow thread local areas based on size requested?
|
||||||
thread_local_area_impl()
|
imp::thread_local_area_impl()
|
||||||
}
|
}
|
35
lib/runtime/src/process/x86_64.rs
Normal file
35
lib/runtime/src/process/x86_64.rs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
#![allow(missing_docs)]
|
||||||
|
|
||||||
|
pub fn get_tls() -> usize {
|
||||||
|
let tp: usize;
|
||||||
|
unsafe {
|
||||||
|
core::arch::asm!("mov %fs:0, {0}", out(reg) tp, options(att_syntax));
|
||||||
|
}
|
||||||
|
tp
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn set_pthread_self(value: usize) {
|
||||||
|
let tls: *mut usize = core::ptr::with_exposed_provenance_mut(get_tls());
|
||||||
|
if tls.is_null() {
|
||||||
|
panic!("TLS pointer is NULL");
|
||||||
|
}
|
||||||
|
tls.add(1).write(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pthread_self() -> usize {
|
||||||
|
let tls: *const usize = core::ptr::with_exposed_provenance(get_tls());
|
||||||
|
if tls.is_null() {
|
||||||
|
panic!("TLS pointer is NULL");
|
||||||
|
}
|
||||||
|
unsafe { tls.add(1).read() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn thread_local_area_impl() -> &'static mut [*mut u8] {
|
||||||
|
let tp = get_tls();
|
||||||
|
|
||||||
|
// Skip self-pointer
|
||||||
|
assert_eq!(tp % size_of::<usize>(), 0);
|
||||||
|
let tp = tp + size_of::<usize>() * 2;
|
||||||
|
|
||||||
|
unsafe { super::thread_local_area_common(tp) }
|
||||||
|
}
|
74
test.c
74
test.c
@ -1,73 +1,19 @@
|
|||||||
|
#include <pthread.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <math.h>
|
|
||||||
#include <setjmp.h>
|
static void *function(void *arg) {
|
||||||
#include <stdlib.h>
|
printf("This is thread %u\n", pthread_self());
|
||||||
#include <string.h>
|
printf("argument: %s\n", (const char *) arg);
|
||||||
#include <unistd.h>
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, const char **argv) {
|
int main(int argc, const char **argv) {
|
||||||
char *val;
|
pthread_t thread;
|
||||||
|
|
||||||
assert(environ != NULL);
|
assert(pthread_create(&thread, NULL, function, (void *) "thread1") == 0);
|
||||||
assert(*environ == NULL);
|
|
||||||
|
|
||||||
setenv("key1", "value1", 0);
|
pthread_join(thread, NULL);
|
||||||
assert(environ != NULL);
|
|
||||||
assert(!strcmp(*environ, "key1=value1"));
|
|
||||||
assert(*(environ + 1) == NULL);
|
|
||||||
|
|
||||||
setenv("key1", "value2", 0);
|
|
||||||
assert(environ != NULL);
|
|
||||||
assert(!strcmp(*environ, "key1=value1"));
|
|
||||||
assert(*(environ + 1) == NULL);
|
|
||||||
|
|
||||||
setenv("key1", "value3", 1);
|
|
||||||
assert(environ != NULL);
|
|
||||||
assert(!strcmp(*environ, "key1=value3"));
|
|
||||||
assert(*(environ + 1) == NULL);
|
|
||||||
|
|
||||||
setenv("key1", "longer-value", 1);
|
|
||||||
assert(environ != NULL);
|
|
||||||
assert(!strcmp(*environ, "key1=longer-value"));
|
|
||||||
assert(*(environ + 1) == NULL);
|
|
||||||
|
|
||||||
setenv("key2", "other-value", 0);
|
|
||||||
assert(environ != NULL);
|
|
||||||
assert(!strcmp(environ[0], "key1=longer-value"));
|
|
||||||
assert(!strcmp(environ[1], "key2=other-value"));
|
|
||||||
assert(environ[2] == NULL);
|
|
||||||
|
|
||||||
assert(!strcmp(getenv("key1"), "longer-value"));
|
|
||||||
assert(!strcmp(getenv("key2"), "other-value"));
|
|
||||||
assert(getenv("key3") == NULL);
|
|
||||||
|
|
||||||
unsetenv("key2");
|
|
||||||
|
|
||||||
assert(!strcmp(getenv("key1"), "longer-value"));
|
|
||||||
assert(getenv("key2") == NULL);
|
|
||||||
assert(getenv("key3") == NULL);
|
|
||||||
|
|
||||||
environ[0] = strdup("key2=???");
|
|
||||||
|
|
||||||
assert(!strcmp(getenv("key2"), "???"));
|
|
||||||
|
|
||||||
char *value = strdup("key4=!!!");
|
|
||||||
putenv(value);
|
|
||||||
|
|
||||||
assert(!strcmp(getenv("key2"), "???"));
|
|
||||||
assert(!strcmp(getenv("key4"), "!!!"));
|
|
||||||
|
|
||||||
value[6] = '@';
|
|
||||||
|
|
||||||
assert(!strcmp(getenv("key2"), "???"));
|
|
||||||
assert(!strcmp(getenv("key4"), "!@!"));
|
|
||||||
|
|
||||||
unsetenv("key4");
|
|
||||||
unsetenv("key2");
|
|
||||||
|
|
||||||
assert(environ != NULL);
|
|
||||||
assert(*environ == NULL);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -135,3 +135,4 @@ pub mod sys_wait;
|
|||||||
|
|
||||||
// TODO Generate those as part of dyn-loader (and make dyn-loader a shared library)
|
// TODO Generate those as part of dyn-loader (and make dyn-loader a shared library)
|
||||||
pub mod link;
|
pub mod link;
|
||||||
|
pub mod pthread;
|
||||||
|
134
userspace/lib/ygglibc/src/headers/pthread/attr.rs
Normal file
134
userspace/lib/ygglibc/src/headers/pthread/attr.rs
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
use core::ffi::{c_int, c_void};
|
||||||
|
|
||||||
|
use crate::headers::{sched::__ygg_sched_param_t, sys_types::pthread_attr_t};
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
unsafe extern "C" fn pthread_attr_destroy(_attr: *mut pthread_attr_t) -> c_int {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
unsafe extern "C" fn pthread_attr_getdetachstate(
|
||||||
|
_attr: *const pthread_attr_t,
|
||||||
|
_state: *mut c_int,
|
||||||
|
) -> c_int {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
unsafe extern "C" fn pthread_attr_getguardsize(
|
||||||
|
_attr: *const pthread_attr_t,
|
||||||
|
_size: *mut usize,
|
||||||
|
) -> c_int {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
unsafe extern "C" fn pthread_attr_getinheritsched(
|
||||||
|
_attr: *const pthread_attr_t,
|
||||||
|
_inherit: *mut c_int,
|
||||||
|
) -> c_int {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
unsafe extern "C" fn pthread_attr_getschedparam(
|
||||||
|
_attr: *const pthread_attr_t,
|
||||||
|
_param: *mut __ygg_sched_param_t,
|
||||||
|
) -> c_int {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
unsafe extern "C" fn pthread_attr_getschedpolicy(
|
||||||
|
_attr: *const pthread_attr_t,
|
||||||
|
_policy: *mut c_int,
|
||||||
|
) -> c_int {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
unsafe extern "C" fn pthread_attr_getscope(
|
||||||
|
_attr: *const pthread_attr_t,
|
||||||
|
_scope: *mut c_int,
|
||||||
|
) -> c_int {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
unsafe extern "C" fn pthread_attr_getstack(
|
||||||
|
_attr: *const pthread_attr_t,
|
||||||
|
_stack: *mut *mut c_void,
|
||||||
|
_size: *mut usize,
|
||||||
|
) -> c_int {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
unsafe extern "C" fn pthread_attr_getstacksize(
|
||||||
|
_attr: *const pthread_attr_t,
|
||||||
|
_size: *mut usize,
|
||||||
|
) -> c_int {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
unsafe extern "C" fn pthread_attr_init(_attr: *mut pthread_attr_t) -> c_int {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
unsafe extern "C" fn pthread_attr_setdetachstate(
|
||||||
|
_attr: *mut pthread_attr_t,
|
||||||
|
_detach: c_int,
|
||||||
|
) -> c_int {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
unsafe extern "C" fn pthread_attr_setguardsize(_attr: *mut pthread_attr_t, _size: usize) -> c_int {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
unsafe extern "C" fn pthread_attr_setinheritsched(
|
||||||
|
_attr: *mut pthread_attr_t,
|
||||||
|
_inherit: c_int,
|
||||||
|
) -> c_int {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
unsafe extern "C" fn pthread_attr_setschedparam(
|
||||||
|
_attr: *mut pthread_attr_t,
|
||||||
|
_param: *const __ygg_sched_param_t,
|
||||||
|
) -> c_int {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
unsafe extern "C" fn pthread_attr_setschedpolicy(
|
||||||
|
_attr: *mut pthread_attr_t,
|
||||||
|
_policy: c_int,
|
||||||
|
) -> c_int {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
unsafe extern "C" fn pthread_attr_setscope(_attr: *mut pthread_attr_t, _scope: c_int) -> c_int {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
unsafe extern "C" fn pthread_attr_setstack(
|
||||||
|
_attr: *mut pthread_attr_t,
|
||||||
|
_stack: *mut c_void,
|
||||||
|
_size: usize,
|
||||||
|
) -> c_int {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
unsafe extern "C" fn pthread_attr_setstacksize(_attr: *mut pthread_attr_t, _size: usize) -> c_int {
|
||||||
|
todo!()
|
||||||
|
}
|
9
userspace/lib/ygglibc/src/headers/pthread/barrier.rs
Normal file
9
userspace/lib/ygglibc/src/headers/pthread/barrier.rs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
// PTHREAD_BARRIER_SERIAL_THREAD
|
||||||
|
|
||||||
|
// int pthread_barrier_destroy(pthread_barrier_t *);
|
||||||
|
// int pthread_barrier_init(pthread_barrier_t *restrict, const pthread_barrierattr_t *restrict, unsigned);
|
||||||
|
// int pthread_barrier_wait(pthread_barrier_t *);
|
||||||
|
// int pthread_barrierattr_destroy(pthread_barrierattr_t *);
|
||||||
|
// int pthread_barrierattr_getpshared(const pthread_barrierattr_t *restrict, int *restrict);
|
||||||
|
// int pthread_barrierattr_init(pthread_barrierattr_t *);
|
||||||
|
// int pthread_barrierattr_setpshared(pthread_barrierattr_t *, int);
|
14
userspace/lib/ygglibc/src/headers/pthread/cbindgen.toml
Normal file
14
userspace/lib/ygglibc/src/headers/pthread/cbindgen.toml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
language = "C"
|
||||||
|
style = "Type"
|
||||||
|
|
||||||
|
sys_includes = [
|
||||||
|
"sys/types.h",
|
||||||
|
"sched.h",
|
||||||
|
"time.h"
|
||||||
|
]
|
||||||
|
no_includes = true
|
||||||
|
|
||||||
|
include_guard = "_PTHREAD_H"
|
||||||
|
|
||||||
|
usize_type = "size_t"
|
||||||
|
isize_type = "ssize_t"
|
16
userspace/lib/ygglibc/src/headers/pthread/cond.rs
Normal file
16
userspace/lib/ygglibc/src/headers/pthread/cond.rs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
|
||||||
|
// PTHREAD_COND_INITIALIZER (macro)
|
||||||
|
|
||||||
|
|
||||||
|
// int pthread_cond_broadcast(pthread_cond_t *);
|
||||||
|
// int pthread_cond_destroy(pthread_cond_t *);
|
||||||
|
// int pthread_cond_init(pthread_cond_t *restrict, const pthread_condattr_t *restrict);
|
||||||
|
// int pthread_cond_signal(pthread_cond_t *);
|
||||||
|
// int pthread_cond_timedwait(pthread_cond_t *restrict, pthread_mutex_t *restrict, const struct timespec *restrict);
|
||||||
|
// int pthread_cond_wait(pthread_cond_t *restrict, pthread_mutex_t *restrict);
|
||||||
|
// int pthread_condattr_destroy(pthread_condattr_t *);
|
||||||
|
// int pthread_condattr_getclock(const pthread_condattr_t *restrict, clockid_t *restrict);
|
||||||
|
// int pthread_condattr_getpshared(const pthread_condattr_t *restrict, int *restrict);
|
||||||
|
// int pthread_condattr_init(pthread_condattr_t *);
|
||||||
|
// int pthread_condattr_setclock(pthread_condattr_t *, clockid_t);
|
||||||
|
// int pthread_condattr_setpshared(pthread_condattr_t *, int);
|
51
userspace/lib/ygglibc/src/headers/pthread/mod.rs
Normal file
51
userspace/lib/ygglibc/src/headers/pthread/mod.rs
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
use core::{
|
||||||
|
ffi::c_void,
|
||||||
|
mem::MaybeUninit,
|
||||||
|
ptr::null_mut,
|
||||||
|
sync::atomic::{AtomicPtr, Ordering},
|
||||||
|
};
|
||||||
|
|
||||||
|
use alloc::{boxed::Box, collections::BTreeMap, sync::Arc};
|
||||||
|
use yggdrasil_rt::{
|
||||||
|
mem::{MappingFlags, MappingSource},
|
||||||
|
process::{ThreadId, ThreadSpawnOptions},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
error::{EResult, ResultExt},
|
||||||
|
headers::errno,
|
||||||
|
sync::Mutex,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::sys_types::{pthread_attr_t, pthread_t};
|
||||||
|
|
||||||
|
mod attr;
|
||||||
|
mod barrier;
|
||||||
|
mod cond;
|
||||||
|
mod mutex;
|
||||||
|
mod rwlock;
|
||||||
|
mod spin;
|
||||||
|
mod thread;
|
||||||
|
mod tls;
|
||||||
|
|
||||||
|
// PTHREAD_CANCEL_ASYNCHRONOUS
|
||||||
|
// PTHREAD_CANCEL_ENABLE
|
||||||
|
// PTHREAD_CANCEL_DEFERRED
|
||||||
|
// PTHREAD_CANCEL_DISABLE
|
||||||
|
// PTHREAD_CANCELED
|
||||||
|
// PTHREAD_CREATE_DETACHED
|
||||||
|
// PTHREAD_CREATE_JOINABLE
|
||||||
|
// PTHREAD_EXPLICIT_SCHED
|
||||||
|
// PTHREAD_INHERIT_SCHED
|
||||||
|
// PTHREAD_ONCE_INIT
|
||||||
|
// PTHREAD_PRIO_INHERIT
|
||||||
|
// PTHREAD_PRIO_NONE
|
||||||
|
// PTHREAD_PRIO_PROTECT
|
||||||
|
// PTHREAD_PROCESS_SHARED
|
||||||
|
// PTHREAD_PROCESS_PRIVATE
|
||||||
|
// PTHREAD_SCOPE_PROCESS
|
||||||
|
// PTHREAD_SCOPE_SYSTEM
|
||||||
|
//
|
||||||
|
|
||||||
|
// int pthread_once(pthread_once_t *, void (*)(void));
|
||||||
|
|
31
userspace/lib/ygglibc/src/headers/pthread/mutex.rs
Normal file
31
userspace/lib/ygglibc/src/headers/pthread/mutex.rs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
// PTHREAD_MUTEX_DEFAULT
|
||||||
|
// PTHREAD_MUTEX_ERRORCHECK
|
||||||
|
// PTHREAD_MUTEX_NORMAL
|
||||||
|
// PTHREAD_MUTEX_RECURSIVE
|
||||||
|
// PTHREAD_MUTEX_ROBUST
|
||||||
|
// PTHREAD_MUTEX_STALLED
|
||||||
|
//
|
||||||
|
// PTHREAD_MUTEX_INITIALIZER (macro)
|
||||||
|
|
||||||
|
|
||||||
|
// int pthread_mutex_consistent(pthread_mutex_t *);
|
||||||
|
// int pthread_mutex_destroy(pthread_mutex_t *);
|
||||||
|
// int pthread_mutex_getprioceiling(const pthread_mutex_t *restrict, int *restrict);
|
||||||
|
// int pthread_mutex_init(pthread_mutex_t *restrict, const pthread_mutexattr_t *restrict);
|
||||||
|
// int pthread_mutex_lock(pthread_mutex_t *);
|
||||||
|
// int pthread_mutex_setprioceiling(pthread_mutex_t *restrict, int, int *restrict);
|
||||||
|
// int pthread_mutex_timedlock(pthread_mutex_t *restrict, const struct timespec *restrict);
|
||||||
|
// int pthread_mutex_trylock(pthread_mutex_t *);
|
||||||
|
// int pthread_mutex_unlock(pthread_mutex_t *);
|
||||||
|
// int pthread_mutexattr_destroy(pthread_mutexattr_t *);
|
||||||
|
// int pthread_mutexattr_getprioceiling( const pthread_mutexattr_t *restrict, int *restrict);
|
||||||
|
// int pthread_mutexattr_getprotocol(const pthread_mutexattr_t *restrict, int *restrict);
|
||||||
|
// int pthread_mutexattr_getpshared(const pthread_mutexattr_t *restrict, int *restrict);
|
||||||
|
// int pthread_mutexattr_getrobust(const pthread_mutexattr_t *restrict, int *restrict);
|
||||||
|
// int pthread_mutexattr_gettype(const pthread_mutexattr_t *restrict, int *restrict);
|
||||||
|
// int pthread_mutexattr_init(pthread_mutexattr_t *);
|
||||||
|
// int pthread_mutexattr_setprioceiling(pthread_mutexattr_t *, int);
|
||||||
|
// int pthread_mutexattr_setprotocol(pthread_mutexattr_t *, int);
|
||||||
|
// int pthread_mutexattr_setpshared(pthread_mutexattr_t *, int);
|
||||||
|
// int pthread_mutexattr_setrobust(pthread_mutexattr_t *, int);
|
||||||
|
// int pthread_mutexattr_settype(pthread_mutexattr_t *, int);
|
15
userspace/lib/ygglibc/src/headers/pthread/rwlock.rs
Normal file
15
userspace/lib/ygglibc/src/headers/pthread/rwlock.rs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// PTHREAD_RWLOCK_INITIALIZER (macro)
|
||||||
|
|
||||||
|
// int pthread_rwlock_destroy(pthread_rwlock_t *);
|
||||||
|
// int pthread_rwlock_init(pthread_rwlock_t *restrict, const pthread_rwlockattr_t *restrict);
|
||||||
|
// int pthread_rwlock_rdlock(pthread_rwlock_t *);
|
||||||
|
// int pthread_rwlock_timedrdlock(pthread_rwlock_t *restrict, const struct timespec *restrict);
|
||||||
|
// int pthread_rwlock_timedwrlock(pthread_rwlock_t *restrict, const struct timespec *restrict);
|
||||||
|
// int pthread_rwlock_tryrdlock(pthread_rwlock_t *);
|
||||||
|
// int pthread_rwlock_trywrlock(pthread_rwlock_t *);
|
||||||
|
// int pthread_rwlock_unlock(pthread_rwlock_t *);
|
||||||
|
// int pthread_rwlock_wrlock(pthread_rwlock_t *);
|
||||||
|
// int pthread_rwlockattr_destroy(pthread_rwlockattr_t *);
|
||||||
|
// int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *restrict, int *restrict);
|
||||||
|
// int pthread_rwlockattr_init(pthread_rwlockattr_t *);
|
||||||
|
// int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *, int);
|
6
userspace/lib/ygglibc/src/headers/pthread/spin.rs
Normal file
6
userspace/lib/ygglibc/src/headers/pthread/spin.rs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
|
||||||
|
// int pthread_spin_destroy(pthread_spinlock_t *);
|
||||||
|
// int pthread_spin_init(pthread_spinlock_t *, int);
|
||||||
|
// int pthread_spin_lock(pthread_spinlock_t *);
|
||||||
|
// int pthread_spin_trylock(pthread_spinlock_t *);
|
||||||
|
// int pthread_spin_unlock(pthread_spinlock_t *);
|
119
userspace/lib/ygglibc/src/headers/pthread/thread.rs
Normal file
119
userspace/lib/ygglibc/src/headers/pthread/thread.rs
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
use core::ffi::{c_int, c_void};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
error::CIntZeroResult,
|
||||||
|
headers::{
|
||||||
|
sched::__ygg_sched_param_t,
|
||||||
|
sys_types::{clockid_t, pthread_attr_t, pthread_t},
|
||||||
|
},
|
||||||
|
thread::Thread,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
unsafe extern "C" fn pthread_atfork(
|
||||||
|
_prepare: extern "C" fn(),
|
||||||
|
_parent: extern "C" fn(),
|
||||||
|
_child: extern "C" fn(),
|
||||||
|
) -> c_int {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
unsafe extern "C" fn pthread_cancel(_thread: pthread_t) -> c_int {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
unsafe extern "C" fn pthread_create(
|
||||||
|
result: *mut pthread_t,
|
||||||
|
attr: *const pthread_attr_t,
|
||||||
|
entry: extern "C" fn(*mut c_void) -> *mut c_void,
|
||||||
|
argument: *mut c_void,
|
||||||
|
) -> CIntZeroResult {
|
||||||
|
let attr = attr.as_ref();
|
||||||
|
let id = Thread::spawn(attr, entry, argument)?;
|
||||||
|
if let Some(result) = result.as_mut() {
|
||||||
|
*result = id;
|
||||||
|
}
|
||||||
|
CIntZeroResult::SUCCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
unsafe extern "C" fn pthread_detach(_thread: pthread_t) -> c_int {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
unsafe extern "C" fn pthread_equal(_thread1: pthread_t, _thread2: pthread_t) -> c_int {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
unsafe extern "C" fn pthread_exit(_result: *mut c_void) -> c_void {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
unsafe extern "C" fn pthread_getconcurrency() -> c_int {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
unsafe extern "C" fn pthread_getcpuclockid(_thread: pthread_t, _clock_id: *mut clockid_t) -> c_int {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
unsafe extern "C" fn pthread_getschedparam(
|
||||||
|
_thread: pthread_t,
|
||||||
|
_policy: *mut c_int,
|
||||||
|
_param: *mut __ygg_sched_param_t,
|
||||||
|
) -> c_int {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
unsafe extern "C" fn pthread_join(thread: pthread_t, result: *mut *mut c_void) -> CIntZeroResult {
|
||||||
|
let thread = Thread::join(thread)?;
|
||||||
|
// TODO write result
|
||||||
|
CIntZeroResult::SUCCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
unsafe extern "C" fn pthread_self() -> pthread_t {
|
||||||
|
Thread::this().expect("pthread_self() failed").id()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
unsafe extern "C" fn pthread_setcancelstate(_state: c_int, _old_state: *mut c_int) -> c_int {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
unsafe extern "C" fn pthread_setcanceltype(_ty: c_int, _old_ty: *mut c_int) -> c_int {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
unsafe extern "C" fn pthread_setconcurrency(_c: c_int) -> c_int {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
unsafe extern "C" fn pthread_setschedparam(
|
||||||
|
_thread: pthread_t,
|
||||||
|
_policy: c_int,
|
||||||
|
_param: *const __ygg_sched_param_t,
|
||||||
|
) -> c_int {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
unsafe extern "C" fn pthread_setschedprio(_thread: pthread_t, _priority: c_int) -> c_int {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
unsafe extern "C" fn pthread_testcancel() {
|
||||||
|
todo!()
|
||||||
|
}
|
5
userspace/lib/ygglibc/src/headers/pthread/tls.rs
Normal file
5
userspace/lib/ygglibc/src/headers/pthread/tls.rs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
|
||||||
|
// void *pthread_getspecific(pthread_key_t);
|
||||||
|
// int pthread_key_create(pthread_key_t *, void (*)(void*));
|
||||||
|
// int pthread_key_delete(pthread_key_t);
|
||||||
|
// int pthread_setspecific(pthread_key_t, const void *);
|
@ -26,5 +26,19 @@ include = [
|
|||||||
"suseconds_t",
|
"suseconds_t",
|
||||||
"time_t",
|
"time_t",
|
||||||
"timer_t",
|
"timer_t",
|
||||||
"uid_t"
|
"uid_t",
|
||||||
|
|
||||||
|
"pthread_attr_t",
|
||||||
|
"pthread_barrier_t",
|
||||||
|
"pthread_barrierattr_t",
|
||||||
|
"pthread_cond_t",
|
||||||
|
"pthread_condattr_t",
|
||||||
|
"pthread_key_t",
|
||||||
|
"pthread_mutex_t",
|
||||||
|
"pthread_mutexattr_t",
|
||||||
|
"pthread_once_t",
|
||||||
|
"pthread_rwlock_t",
|
||||||
|
"pthread_rwlockattr_t",
|
||||||
|
"pthread_spinlock_t",
|
||||||
|
"pthread_t"
|
||||||
]
|
]
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use core::ffi::{c_int, c_ulong};
|
use core::ffi::{c_int, c_ulong, c_void};
|
||||||
|
|
||||||
pub type pid_t = i32;
|
pub type pid_t = i32;
|
||||||
pub type uid_t = i32;
|
pub type uid_t = i32;
|
||||||
@ -36,3 +36,40 @@ pub type suseconds_t = c_ulong;
|
|||||||
impl time_t {
|
impl time_t {
|
||||||
pub const INVALID: Self = Self(i64::MAX);
|
pub const INVALID: Self = Self(i64::MAX);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// pthread types
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct pthread_attr_t {
|
||||||
|
pub stack: *mut c_void,
|
||||||
|
pub stack_size: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct pthread_barrier_t {}
|
||||||
|
pub struct pthread_barrierattr_t {}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct pthread_cond_t {
|
||||||
|
__dummy: c_int
|
||||||
|
}
|
||||||
|
pub struct pthread_condattr_t {}
|
||||||
|
|
||||||
|
pub struct pthread_key_t {}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct pthread_mutex_t {
|
||||||
|
__dummy: c_int
|
||||||
|
}
|
||||||
|
pub struct pthread_mutexattr_t {}
|
||||||
|
|
||||||
|
pub struct pthread_once_t {}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct pthread_rwlock_t {
|
||||||
|
__dummy: c_int
|
||||||
|
}
|
||||||
|
pub struct pthread_rwlockattr_t {}
|
||||||
|
|
||||||
|
pub struct pthread_spinlock_t {}
|
||||||
|
|
||||||
|
pub type pthread_t = u32;
|
||||||
|
@ -39,6 +39,7 @@ mod ssp;
|
|||||||
mod sync;
|
mod sync;
|
||||||
mod types;
|
mod types;
|
||||||
mod util;
|
mod util;
|
||||||
|
mod thread;
|
||||||
|
|
||||||
pub mod headers;
|
pub mod headers;
|
||||||
|
|
||||||
|
178
userspace/lib/ygglibc/src/thread/mod.rs
Normal file
178
userspace/lib/ygglibc/src/thread/mod.rs
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
use core::{
|
||||||
|
ffi::c_void,
|
||||||
|
mem,
|
||||||
|
pin::Pin,
|
||||||
|
ptr::{self, null_mut},
|
||||||
|
sync::atomic::{AtomicPtr, AtomicU32, Ordering},
|
||||||
|
};
|
||||||
|
|
||||||
|
use alloc::{boxed::Box, collections::BTreeMap, sync::Arc};
|
||||||
|
use yggdrasil_rt::{
|
||||||
|
mem::{MappingFlags, MappingSource},
|
||||||
|
process::{get_tls, pthread_self, set_pthread_self, ThreadId, ThreadSpawnOptions},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
error::EResult,
|
||||||
|
headers::sys_types::{pthread_attr_t, pthread_t},
|
||||||
|
sync::Mutex,
|
||||||
|
};
|
||||||
|
|
||||||
|
mod tls;
|
||||||
|
|
||||||
|
static THREADS: Mutex<BTreeMap<pthread_t, Arc<Thread>>> = Mutex::new(BTreeMap::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 {
|
||||||
|
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>> {
|
||||||
|
let pthread_self: *const Self = ptr::with_exposed_provenance(pthread_self());
|
||||||
|
if !pthread_self.is_null() {
|
||||||
|
// Arc::from_raw creates an owned Arc, which will decrement refcount when dropped,
|
||||||
|
// prevent this by incrementing the count once more
|
||||||
|
unsafe { Arc::increment_strong_count(pthread_self) };
|
||||||
|
let arc = unsafe { Arc::from_raw(pthread_self) };
|
||||||
|
|
||||||
|
EResult::Ok(arc)
|
||||||
|
} else {
|
||||||
|
panic!("pthread_self is NULL")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn result(&self) -> *mut c_void {
|
||||||
|
self.result.load(Ordering::Acquire)
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" fn thread_entry(argument: usize) -> ! {
|
||||||
|
{
|
||||||
|
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
|
||||||
|
);
|
||||||
|
yggdrasil_rt::debug_trace!("tls pointer: {:#x}", get_tls());
|
||||||
|
|
||||||
|
// TODO better way to initialize the thread ID
|
||||||
|
while argument.thread.id.load(Ordering::Acquire) == 0 {
|
||||||
|
core::hint::spin_loop();
|
||||||
|
}
|
||||||
|
argument.thread.clone().setup();
|
||||||
|
let result = (argument.entry)(argument.argument);
|
||||||
|
argument.thread.result.store(result, Ordering::Release);
|
||||||
|
|
||||||
|
// Drop the Arc<Self> written to TLS
|
||||||
|
let this = Self::this().expect("pthread_self() is NULL");
|
||||||
|
unsafe { Arc::decrement_strong_count(Arc::as_ptr(&this)) };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exit the thread when everything's dropped
|
||||||
|
unsafe { yggdrasil_rt::sys::exit_thread() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup(self: Arc<Self>) {
|
||||||
|
// Setup self-pointer
|
||||||
|
let pthread_self = Arc::into_raw(self);
|
||||||
|
unsafe { set_pthread_self(pthread_self.addr()) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1
userspace/lib/ygglibc/src/thread/tls.rs
Normal file
1
userspace/lib/ygglibc/src/thread/tls.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
|
Loading…
x
Reference in New Issue
Block a user