use alloc::boxed::Box;
use core::any::Any;
use core::ffi::c_void;
use core::ptr::{from_exposed_addr_mut, null_mut};
use rustix::io;
use rustix::process::Pid;
extern "C" {
fn __cxa_thread_atexit_impl(
func: unsafe extern "C" fn(*mut c_void),
obj: *mut c_void,
_dso_symbol: *mut c_void,
) -> libc::c_int;
fn __tls_get_addr(p: &[usize; 2]) -> *mut c_void;
fn pthread_attr_getstacksize(
attr: *const libc::pthread_attr_t,
stacksize: &mut libc::size_t,
) -> libc::c_int;
fn pthread_attr_setguardsize(
attr: *const libc::pthread_attr_t,
guardsize: libc::size_t,
) -> libc::c_int;
static __dso_handle: *const c_void;
}
pub struct Thread(libc::pthread_t);
impl Thread {
#[inline]
pub fn from_raw(raw: *mut c_void) -> Self {
Self(raw.expose_addr() as libc::pthread_t)
}
#[inline]
pub fn to_raw(self) -> *mut c_void {
from_exposed_addr_mut(self.0 as usize)
}
}
#[inline]
pub fn current_thread() -> Thread {
unsafe { Thread(libc::pthread_self()) }
}
#[inline]
pub fn current_thread_id() -> Pid {
rustix::thread::gettid()
}
#[cfg(feature = "set_thread_id")]
#[doc(hidden)]
#[inline]
pub unsafe fn set_current_thread_id_after_a_fork(tid: Pid) {
let _ = tid;
}
#[inline]
pub fn current_thread_tls_addr(offset: usize) -> *mut c_void {
let p = [1, offset];
unsafe { __tls_get_addr(&p) }
}
#[inline]
pub unsafe fn thread_stack(thread: Thread) -> (*mut c_void, usize, usize) {
let thread = thread.0;
let mut attr: libc::pthread_attr_t = core::mem::zeroed();
assert_eq!(libc::pthread_getattr_np(thread, &mut attr), 0);
let mut stack_size = 0;
let mut stack_addr = null_mut();
assert_eq!(
libc::pthread_attr_getstack(&attr, &mut stack_addr, &mut stack_size),
0
);
let mut guard_size = 0;
assert_eq!(libc::pthread_attr_getguardsize(&attr, &mut guard_size), 0);
(stack_addr, stack_size, guard_size)
}
pub fn at_thread_exit(func: Box<dyn FnOnce()>) {
extern "C" fn call(arg: *mut c_void) {
unsafe {
let arg = arg.cast::<Box<dyn FnOnce()>>();
let arg = Box::from_raw(arg);
arg()
}
}
unsafe {
let arg = Box::into_raw(Box::new(func));
assert_eq!(__cxa_thread_atexit_impl(call, arg.cast(), dso_handle()), 0);
}
}
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub fn create_thread(
fn_: Box<dyn FnOnce() -> Option<Box<dyn Any>>>,
stack_size: usize,
guard_size: usize,
) -> io::Result<Thread> {
extern "C" fn start(arg: *mut c_void) -> *mut c_void {
unsafe {
let arg = arg.cast::<Box<dyn FnOnce() -> Option<Box<dyn Any>>>>();
let arg = Box::from_raw(arg);
match arg() {
None => null_mut(),
Some(ret) => Box::into_raw(ret).cast(),
}
}
}
unsafe {
let mut attr: libc::pthread_attr_t = core::mem::zeroed();
match libc::pthread_attr_init(&mut attr) {
0 => (),
err => return Err(io::Errno::from_raw_os_error(err)),
}
match libc::pthread_attr_setstacksize(&mut attr, stack_size) {
0 => (),
err => return Err(io::Errno::from_raw_os_error(err)),
}
match pthread_attr_setguardsize(&mut attr, guard_size) {
0 => (),
err => return Err(io::Errno::from_raw_os_error(err)),
}
let mut new_thread = core::mem::zeroed();
let arg = Box::into_raw(Box::new(fn_));
match libc::pthread_create(&mut new_thread, &attr, start, arg.cast()) {
0 => (),
err => return Err(io::Errno::from_raw_os_error(err)),
}
Ok(Thread(new_thread))
}
}
#[inline]
pub unsafe fn detach_thread(thread: Thread) {
let thread = thread.0;
assert_eq!(libc::pthread_detach(thread), 0);
}
pub unsafe fn join_thread(thread: Thread) {
let thread = thread.0;
let mut retval: *mut c_void = null_mut();
assert_eq!(libc::pthread_join(thread, &mut retval), 0);
}
#[inline]
pub fn default_stack_size() -> usize {
unsafe {
let mut attr: libc::pthread_attr_t = core::mem::zeroed();
assert_eq!(libc::pthread_getattr_np(libc::pthread_self(), &mut attr), 0);
let mut stack_size = 0;
assert_eq!(pthread_attr_getstacksize(&attr, &mut stack_size), 0);
stack_size
}
}
#[inline]
pub fn default_guard_size() -> usize {
unsafe {
let mut attr: libc::pthread_attr_t = core::mem::zeroed();
assert_eq!(libc::pthread_getattr_np(libc::pthread_self(), &mut attr), 0);
let mut guard_size = 0;
assert_eq!(libc::pthread_attr_getguardsize(&attr, &mut guard_size), 0);
guard_size
}
}
unsafe fn dso_handle() -> *mut c_void {
let dso_handle: *const *const c_void = &__dso_handle;
dso_handle as *mut c_void
}