From fedd8ba2339b4f1d91434c997b9f73ea012eb51d Mon Sep 17 00:00:00 2001
From: Jeremy Soller <jeremy@system76.com>
Date: Sun, 14 Apr 2019 15:13:32 -0600
Subject: [PATCH] Use pthreads for Redox thread

---
 src/libstd/sys/redox/thread.rs | 137 +++++++++++++++++++++++++--------
 1 file changed, 107 insertions(+), 30 deletions(-)

diff --git a/src/libstd/sys/redox/thread.rs b/src/libstd/sys/redox/thread.rs
index ae0b91b4d6c7..8474d14d50d3 100644
--- a/src/libstd/sys/redox/thread.rs
+++ b/src/libstd/sys/redox/thread.rs
@@ -1,15 +1,50 @@
 use crate::boxed::FnBox;
+use crate::cmp;
 use crate::ffi::CStr;
 use crate::io;
 use crate::mem;
+use crate::ptr;
+use crate::sys::os;
 use crate::sys_common::thread::start_thread;
-use crate::sys::{cvt, syscall};
 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 {
-    id: usize,
+    id: pthread_t,
 }
 
 // Some platforms may have pthread_t as a pointer in which case we still want
@@ -19,65 +54,107 @@ unsafe impl Sync for Thread {}
 
 impl Thread {
     // 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 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))?;
-        if id == 0 {
-            start_thread(&*p as *const _ as *mut _);
-            let _ = syscall::exit(0);
-            panic!("thread failed to exit");
+        let ret = pthread_create(&mut native, &attr, thread_start,
+                                       &*p as *const _ as *mut _);
+        assert_eq!(pthread_attr_destroy(&mut attr), 0);
+
+        return if ret != 0 {
+            Err(io::Error::from_raw_os_error(ret))
         } else {
-            mem::forget(p);
-            Ok(Thread { id })
+            mem::forget(p); // ownership passed to pthread_create
+            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() {
-        let ret = syscall::sched_yield().expect("failed to sched_yield");
+        let ret = unsafe { sched_yield() };
         debug_assert_eq!(ret, 0);
     }
 
     pub fn set_name(_name: &CStr) {
-
+        // Redox cannot set thread name
     }
 
     pub fn sleep(dur: Duration) {
         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
         // nanosleep will fill in `ts` with the remaining time.
-        while secs > 0 || nsecs > 0 {
-            let req = syscall::TimeSpec {
-                tv_sec: secs as i64,
-                tv_nsec: nsecs,
-            };
-            secs -= req.tv_sec as u64;
-            let mut rem = syscall::TimeSpec::default();
-            if syscall::nanosleep(&req, &mut rem).is_err() {
-                secs += rem.tv_sec as u64;
-                nsecs = rem.tv_nsec;
-            } else {
-                nsecs = 0;
+        unsafe {
+            while secs > 0 || nsecs > 0 {
+                let mut ts = libc::timespec {
+                    tv_sec: cmp::min(libc::time_t::max_value() as u64, secs) as libc::time_t,
+                    tv_nsec: nsecs,
+                };
+                secs -= ts.tv_sec as u64;
+                if nanosleep(&ts, &mut ts) == -1 {
+                    assert_eq!(os::errno(), EINTR);
+                    secs += ts.tv_sec as u64;
+                    nsecs = ts.tv_nsec;
+                } else {
+                    nsecs = 0;
+                }
             }
         }
     }
 
     pub fn join(self) {
-        let mut status = 0;
-        syscall::waitpid(self.id, &mut status, 0).unwrap();
+        unsafe {
+            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;
         mem::forget(self);
         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 type Guard = !;
     pub unsafe fn current() -> Option<Guard> { None }
-- 
GitLab