mod key;
mod mutex;
mod once;
mod rwlock;
mod spinlock;
use alloc::boxed::Box;
use alloc::format;
use core::ffi::c_void;
use core::mem::{transmute, zeroed, MaybeUninit};
use core::ptr::{self, copy_nonoverlapping, null_mut, NonNull};
use core::slice;
use origin::thread::{self, Thread};
use rustix::fs::{Mode, OFlags};
use rustix::runtime::KernelSigSet;
use libc::{c_char, c_int, size_t};
#[allow(non_camel_case_types)]
type PthreadT = *mut c_void;
libc_type!(PthreadT, pthread_t);
#[cfg(not(target_env = "musl"))]
unsafe fn from_libc(thread: libc::pthread_t) -> PthreadT {
ptr::with_exposed_provenance_mut(thread as _)
}
#[cfg(target_env = "musl")]
unsafe fn from_libc(thread: libc::pthread_t) -> PthreadT {
thread
}
#[cfg(not(target_env = "musl"))]
fn to_libc(thread: PthreadT) -> libc::pthread_t {
thread.expose_provenance() as _
}
#[cfg(target_env = "musl")]
fn to_libc(thread: libc::pthread_t) -> PthreadT {
thread
}
bitflags::bitflags! {
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct PthreadAttrFlags: usize {
const DETACHSTATE = 0x1;
const _ = !0;
}
}
#[allow(non_camel_case_types)]
#[repr(C)]
#[derive(Clone)]
struct PthreadAttrT {
stack_addr: *mut c_void,
stack_size: usize,
guard_size: usize,
flags: PthreadAttrFlags,
pad0: usize,
pad1: usize,
pad2: usize,
#[cfg(any(target_arch = "aarch64", target_arch = "x86"))]
pad3: usize,
#[cfg(target_arch = "x86")]
pad4: usize,
}
libc_type!(PthreadAttrT, pthread_attr_t);
impl Default for PthreadAttrT {
fn default() -> Self {
Self {
stack_addr: null_mut(),
stack_size: thread::default_stack_size(),
guard_size: thread::default_guard_size(),
flags: PthreadAttrFlags::empty(),
pad0: 0,
pad1: 0,
pad2: 0,
#[cfg(any(target_arch = "aarch64", target_arch = "x86"))]
pad3: 0,
#[cfg(target_arch = "x86")]
pad4: 0,
}
}
}
#[no_mangle]
unsafe extern "C" fn pthread_self() -> PthreadT {
libc!(from_libc(libc::pthread_self()));
thread::current().to_raw().cast()
}
#[no_mangle]
unsafe extern "C" fn pthread_getattr_np(thread: PthreadT, attr: *mut PthreadAttrT) -> c_int {
libc!(libc::pthread_getattr_np(
to_libc(thread),
checked_cast!(attr)
));
let (stack_addr, stack_size, guard_size) =
thread::stack(Thread::from_raw_unchecked(thread.cast()));
ptr::write(
attr,
PthreadAttrT {
stack_addr,
stack_size,
guard_size,
flags: PthreadAttrFlags::empty(),
pad0: 0,
pad1: 0,
pad2: 0,
#[cfg(any(target_arch = "aarch64", target_arch = "x86"))]
pad3: 0,
#[cfg(target_arch = "x86")]
pad4: 0,
},
);
0
}
#[no_mangle]
unsafe extern "C" fn pthread_attr_init(attr: *mut PthreadAttrT) -> c_int {
libc!(libc::pthread_attr_init(checked_cast!(attr)));
ptr::write(attr, PthreadAttrT::default());
0
}
#[no_mangle]
unsafe extern "C" fn pthread_attr_destroy(attr: *mut PthreadAttrT) -> c_int {
libc!(libc::pthread_attr_destroy(checked_cast!(attr)));
ptr::drop_in_place(attr);
0
}
#[no_mangle]
unsafe extern "C" fn pthread_attr_getstack(
attr: *const PthreadAttrT,
stackaddr: *mut *mut c_void,
stacksize: *mut usize,
) -> c_int {
libc!(libc::pthread_attr_getstack(
checked_cast!(attr),
stackaddr,
stacksize
));
*stackaddr = (*attr).stack_addr;
*stacksize = (*attr).stack_size;
0
}
#[no_mangle]
unsafe extern "C" fn pthread_attr_setstack(
attr: *mut PthreadAttrT,
stackaddr: *mut c_void,
stacksize: usize,
) -> c_int {
if stacksize < libc::PTHREAD_STACK_MIN {
return libc::EINVAL;
}
(*attr).stack_addr = stackaddr;
(*attr).stack_size = stacksize;
0
}
#[no_mangle]
unsafe extern "C" fn pthread_attr_setdetachstate(
attr: *mut PthreadAttrT,
detachstate: c_int,
) -> c_int {
libc!(libc::pthread_attr_setdetachstate(
checked_cast!(attr),
detachstate
));
let value = match detachstate {
libc::PTHREAD_CREATE_DETACHED => true,
libc::PTHREAD_CREATE_JOINABLE => false,
_ => return libc::EINVAL,
};
(*attr).flags.set(PthreadAttrFlags::DETACHSTATE, value);
0
}
#[no_mangle]
unsafe extern "C" fn pthread_attr_getdetachstate(
attr: *const PthreadAttrT,
detachstate: *mut c_int,
) -> c_int {
let newstate = if (*attr).flags.contains(PthreadAttrFlags::DETACHSTATE) {
libc::PTHREAD_CREATE_DETACHED
} else {
libc::PTHREAD_CREATE_JOINABLE
};
*detachstate = newstate;
0
}
#[no_mangle]
unsafe extern "C" fn pthread_attr_getguardsize(
attr: *const PthreadAttrT,
guardsize: *mut usize,
) -> c_int {
libc!(libc::pthread_attr_getguardsize(
checked_cast!(attr),
guardsize
));
*guardsize = (*attr).guard_size;
0
}
#[no_mangle]
unsafe extern "C" fn pthread_attr_setguardsize(attr: *mut PthreadAttrT, guardsize: usize) -> c_int {
(*attr).guard_size = guardsize;
0
}
#[no_mangle]
unsafe extern "C" fn pthread_create(
pthread: *mut PthreadT,
attr: *const PthreadAttrT,
fn_: unsafe extern "C" fn(*mut c_void) -> *mut c_void,
arg: *mut c_void,
) -> c_int {
libc!(libc::pthread_create(
pthread as _,
checked_cast!(attr),
core::mem::transmute(fn_),
arg
));
let PthreadAttrT {
stack_addr,
stack_size,
guard_size,
flags,
pad0: _,
pad1: _,
pad2: _,
#[cfg(any(target_arch = "aarch64", target_arch = "x86"))]
pad3: _,
#[cfg(target_arch = "x86")]
pad4: _,
} = if attr.is_null() {
PthreadAttrT::default()
} else {
ptr::read(attr)
};
assert!(
stack_addr.is_null(),
"custom thread stacks not supported yet"
);
let args = [NonNull::new(fn_ as *mut c_void), NonNull::new(arg)];
unsafe fn call(args: &mut [Option<NonNull<c_void>>]) -> Option<NonNull<c_void>> {
let fn_ = match args[0] {
Some(fn_) => fn_.as_ptr(),
None => null_mut(),
};
let fn_: unsafe extern "C" fn(*mut c_void) -> *mut c_void = transmute(fn_);
let arg = match args[1] {
Some(arg) => arg.as_ptr(),
None => null_mut(),
};
let return_value = fn_(arg);
NonNull::new(return_value)
}
let thread = match thread::create(call, &args, stack_size, guard_size) {
Ok(thread) => thread,
Err(e) => return e.raw_os_error(),
};
if flags.contains(PthreadAttrFlags::DETACHSTATE) {
thread::detach(thread);
}
pthread.write(thread.to_raw().cast());
0
}
#[no_mangle]
unsafe extern "C" fn pthread_detach(pthread: PthreadT) -> c_int {
libc!(libc::pthread_detach(to_libc(pthread)));
thread::detach(Thread::from_raw_unchecked(pthread.cast()));
0
}
#[no_mangle]
unsafe extern "C" fn pthread_join(pthread: PthreadT, retval: *mut *mut c_void) -> c_int {
libc!(libc::pthread_join(to_libc(pthread), retval));
let return_value = thread::join(Thread::from_raw_unchecked(pthread.cast()));
if !retval.is_null() {
*retval = match return_value {
Some(return_value) => return_value.as_ptr(),
None => null_mut(),
};
}
0
}
#[no_mangle]
unsafe extern "C" fn pthread_equal(a: libc::pthread_t, b: libc::pthread_t) -> c_int {
i32::from(a == b)
}
#[no_mangle]
unsafe extern "C" fn pthread_sigmask(
how: c_int,
set: *const libc::sigset_t,
oldset: *mut libc::sigset_t,
) -> c_int {
libc!(libc::pthread_sigmask(how, set, oldset));
let how = match how {
libc::SIG_BLOCK => rustix::runtime::How::BLOCK,
libc::SIG_UNBLOCK => rustix::runtime::How::UNBLOCK,
libc::SIG_SETMASK => rustix::runtime::How::SETMASK,
_ => return libc::EINVAL,
};
if !oldset.is_null() {
oldset.write(zeroed());
}
let set = if set.is_null() {
None
} else {
Some(&*set.cast::<KernelSigSet>())
};
match rustix::runtime::kernel_sigprocmask(how, set) {
Ok(old) => {
if !oldset.is_null() {
oldset.write(crate::expand_sigset(old));
}
0
}
Err(e) => e.raw_os_error(),
}
}
#[no_mangle]
unsafe extern "C" fn pthread_attr_getstacksize(
attr: *const PthreadAttrT,
stacksize: *mut usize,
) -> c_int {
libc!(libc::pthread_attr_getstacksize(
checked_cast!(attr),
stacksize
));
*stacksize = (*attr).stack_size;
0
}
#[no_mangle]
unsafe extern "C" fn pthread_attr_setstacksize(attr: *mut PthreadAttrT, stacksize: usize) -> c_int {
libc!(libc::pthread_attr_setstacksize(
checked_cast!(attr),
stacksize
));
if stacksize < libc::PTHREAD_STACK_MIN {
return libc::EINVAL;
}
(*attr).stack_size = stacksize;
0
}
#[no_mangle]
unsafe extern "C" fn pthread_atfork(
prepare: Option<unsafe extern "C" fn()>,
parent: Option<unsafe extern "C" fn()>,
child: Option<unsafe extern "C" fn()>,
) -> c_int {
libc!(libc::pthread_atfork(prepare, parent, child));
crate::at_fork::at_fork(prepare, parent, child);
0
}
#[no_mangle]
unsafe extern "C" fn pthread_getname_np(
pthread: PthreadT,
name: *mut c_char,
len: size_t,
) -> c_int {
libc!(libc::pthread_getname_np(to_libc(pthread), name, len));
if len < 16 {
return libc::ERANGE;
}
let origin_thread = Thread::from_raw_unchecked(pthread.cast());
if origin_thread == thread::current() {
let prctl_name = match rustix::thread::name() {
Ok(prctl_name) => prctl_name,
Err(err) => return err.raw_os_error(),
};
let bytes = prctl_name.to_bytes_with_nul();
debug_assert!(bytes.len() <= len);
copy_nonoverlapping(bytes.as_ptr().cast(), name, bytes.len());
return 0;
}
let path = format!(
"/proc/self/task/{}/comm",
thread::id(origin_thread).unwrap().as_raw_nonzero()
);
let fd = match rustix::fs::open(
path,
OFlags::RDONLY | OFlags::CLOEXEC | OFlags::NOCTTY,
Mode::empty(),
) {
Ok(fd) => fd,
Err(err) => return err.raw_os_error(),
};
loop {
let buf = slice::from_raw_parts_mut(name.cast::<MaybeUninit<u8>>(), len);
match rustix::io::read(&fd, buf) {
Ok((init, _uninit)) if init.is_empty() => return libc::EIO,
Ok((init, _uninit)) if init.len() <= len => {
*name.add(init.len() - 1) = 0;
break;
}
Ok(_) => return libc::EIO,
Err(rustix::io::Errno::INTR) => continue,
Err(err) => return err.raw_os_error(),
}
}
0
}
#[cfg(target_os = "linux")]
#[no_mangle]
unsafe extern "C" fn pthread_setname_np(pthread: PthreadT, name: *const libc::c_char) -> c_int {
libc!(libc::pthread_setname_np(to_libc(pthread), name));
let name = core::ffi::CStr::from_ptr(name);
let bytes = name.to_bytes();
if bytes.len() >= 16 {
return libc::ERANGE;
}
let origin_thread = Thread::from_raw_unchecked(pthread.cast());
if origin_thread == thread::current() {
return match rustix::thread::set_name(name) {
Ok(()) => 0,
Err(err) => err.raw_os_error(),
};
}
let path = format!(
"/proc/self/task/{}/comm",
thread::id(origin_thread).unwrap().as_raw_nonzero()
);
let fd = match rustix::fs::open(
path,
OFlags::WRONLY | OFlags::CLOEXEC | OFlags::NOCTTY,
Mode::empty(),
) {
Ok(fd) => fd,
Err(err) => return err.raw_os_error(),
};
loop {
match rustix::io::write(&fd, bytes) {
Ok(n) if n == bytes.len() => return 0,
Ok(_) => return libc::EIO,
Err(rustix::io::Errno::INTR) => continue,
Err(err) => return err.raw_os_error(),
}
}
}
#[no_mangle]
unsafe extern "C" fn __cxa_thread_atexit_impl(
func: unsafe extern "C" fn(*mut c_void),
obj: *mut c_void,
_dso_symbol: *mut c_void,
) -> c_int {
thread::at_exit(Box::new(move || func(obj)));
0
}
#[cfg(feature = "thread")]
#[no_mangle]
unsafe extern "C" fn __tls_get_addr(p: &[usize; 2]) -> *mut c_void {
let [module, offset] = *p;
thread::current_tls_addr(module, offset)
}
#[cfg(target_arch = "x86")]
#[no_mangle]
unsafe extern "C" fn ___tls_get_addr() {
todo!("___tls_get_addr")
}