Skip to content
Snippets Groups Projects
Verified Commit fedd8ba2 authored by Jeremy Soller's avatar Jeremy Soller
Browse files

Use pthreads for Redox thread

parent 01e82971
No related branches found
No related tags found
No related merge requests found
use crate::boxed::FnBox; use crate::boxed::FnBox;
use crate::cmp;
use crate::ffi::CStr; use crate::ffi::CStr;
use crate::io; use crate::io;
use crate::mem; use crate::mem;
use crate::ptr;
use crate::sys::os;
use crate::sys_common::thread::start_thread; use crate::sys_common::thread::start_thread;
use crate::sys::{cvt, syscall};
use crate::time::Duration; use crate::time::Duration;
pub const DEFAULT_MIN_STACK_SIZE: usize = 2 * 1024 * 1024; //TODO: Move these to libc crate
const EINTR: libc::c_int = 4;
const EINVAL: libc::c_int = 22;
pub type pthread_t = *mut libc::c_void;
type pthread_attr_t = *mut libc::c_void;
extern "C" {
fn nanosleep(
rqtp: *const libc::timespec,
rmtp: *mut libc::timespec
) -> libc::c_int;
fn pthread_attr_destroy(attr: *mut pthread_attr_t) -> libc::c_int;
fn pthread_attr_init(attr: *mut pthread_attr_t) -> libc::c_int;
fn pthread_attr_setstacksize(
attr: *mut pthread_attr_t,
stacksize: libc::size_t
) -> libc::c_int;
fn pthread_create(
native: *mut pthread_t,
attr: *const pthread_attr_t,
f: extern "C" fn(_: *mut libc::c_void) -> *mut libc::c_void,
value: *mut libc::c_void
) -> libc::c_int;
fn pthread_detach(thread: pthread_t) -> libc::c_int;
fn pthread_join(
native: pthread_t,
value: *mut *mut libc::c_void
) -> libc::c_int;
fn sched_yield() -> libc::c_int;
}
pub const DEFAULT_MIN_STACK_SIZE: usize = 1024 * 1024;
pub struct Thread { pub struct Thread {
id: usize, id: pthread_t,
} }
// Some platforms may have pthread_t as a pointer in which case we still want // Some platforms may have pthread_t as a pointer in which case we still want
...@@ -19,65 +54,107 @@ unsafe impl Sync for Thread {} ...@@ -19,65 +54,107 @@ unsafe impl Sync for Thread {}
impl Thread { impl Thread {
// unsafe: see thread::Builder::spawn_unchecked for safety requirements // unsafe: see thread::Builder::spawn_unchecked for safety requirements
pub unsafe fn new(_stack: usize, p: Box<dyn FnBox()>) -> io::Result<Thread> { pub unsafe fn new(stack: usize, p: Box<dyn FnBox()>)
-> io::Result<Thread> {
let p = box p; let p = box p;
let mut native: pthread_t = mem::zeroed();
let mut attr: pthread_attr_t = mem::zeroed();
assert_eq!(pthread_attr_init(&mut attr), 0);
let stack_size = cmp::max(stack, 4096);
match pthread_attr_setstacksize(&mut attr,
stack_size) {
0 => {}
n => {
assert_eq!(n, EINVAL);
// EINVAL means |stack_size| is either too small or not a
// multiple of the system page size. Because it's definitely
// >= PTHREAD_STACK_MIN, it must be an alignment issue.
// Round up to the nearest page and try again.
let page_size = os::page_size();
let stack_size = (stack_size + page_size - 1) &
(-(page_size as isize - 1) as usize - 1);
assert_eq!(pthread_attr_setstacksize(&mut attr,
stack_size), 0);
}
};
let id = cvt(syscall::clone(syscall::CLONE_VM | syscall::CLONE_FS | syscall::CLONE_FILES))?; let ret = pthread_create(&mut native, &attr, thread_start,
if id == 0 { &*p as *const _ as *mut _);
start_thread(&*p as *const _ as *mut _); assert_eq!(pthread_attr_destroy(&mut attr), 0);
let _ = syscall::exit(0);
panic!("thread failed to exit"); return if ret != 0 {
Err(io::Error::from_raw_os_error(ret))
} else { } else {
mem::forget(p); mem::forget(p); // ownership passed to pthread_create
Ok(Thread { id }) Ok(Thread { id: native })
};
extern fn thread_start(main: *mut libc::c_void) -> *mut libc::c_void {
unsafe { start_thread(main as *mut u8); }
ptr::null_mut()
} }
} }
pub fn yield_now() { pub fn yield_now() {
let ret = syscall::sched_yield().expect("failed to sched_yield"); let ret = unsafe { sched_yield() };
debug_assert_eq!(ret, 0); debug_assert_eq!(ret, 0);
} }
pub fn set_name(_name: &CStr) { pub fn set_name(_name: &CStr) {
// Redox cannot set thread name
} }
pub fn sleep(dur: Duration) { pub fn sleep(dur: Duration) {
let mut secs = dur.as_secs(); let mut secs = dur.as_secs();
let mut nsecs = dur.subsec_nanos() as i32; let mut nsecs = dur.subsec_nanos() as _;
// If we're awoken with a signal then the return value will be -1 and // If we're awoken with a signal then the return value will be -1 and
// nanosleep will fill in `ts` with the remaining time. // nanosleep will fill in `ts` with the remaining time.
while secs > 0 || nsecs > 0 { unsafe {
let req = syscall::TimeSpec { while secs > 0 || nsecs > 0 {
tv_sec: secs as i64, let mut ts = libc::timespec {
tv_nsec: nsecs, tv_sec: cmp::min(libc::time_t::max_value() as u64, secs) as libc::time_t,
}; tv_nsec: nsecs,
secs -= req.tv_sec as u64; };
let mut rem = syscall::TimeSpec::default(); secs -= ts.tv_sec as u64;
if syscall::nanosleep(&req, &mut rem).is_err() { if nanosleep(&ts, &mut ts) == -1 {
secs += rem.tv_sec as u64; assert_eq!(os::errno(), EINTR);
nsecs = rem.tv_nsec; secs += ts.tv_sec as u64;
} else { nsecs = ts.tv_nsec;
nsecs = 0; } else {
nsecs = 0;
}
} }
} }
} }
pub fn join(self) { pub fn join(self) {
let mut status = 0; unsafe {
syscall::waitpid(self.id, &mut status, 0).unwrap(); let ret = pthread_join(self.id, ptr::null_mut());
mem::forget(self);
assert!(ret == 0,
"failed to join thread: {}", io::Error::from_raw_os_error(ret));
}
} }
pub fn id(&self) -> usize { self.id } pub fn id(&self) -> pthread_t { self.id }
pub fn into_id(self) -> usize { pub fn into_id(self) -> pthread_t {
let id = self.id; let id = self.id;
mem::forget(self); mem::forget(self);
id id
} }
} }
impl Drop for Thread {
fn drop(&mut self) {
let ret = unsafe { pthread_detach(self.id) };
debug_assert_eq!(ret, 0);
}
}
pub mod guard { pub mod guard {
pub type Guard = !; pub type Guard = !;
pub unsafe fn current() -> Option<Guard> { None } pub unsafe fn current() -> Option<Guard> { None }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment