6 use crate::sys::{os, stack_overflow};
7 use crate::time::Duration;
9 #[cfg(not(any(target_os = "l4re", target_os = "vxworks")))]
10 pub const DEFAULT_MIN_STACK_SIZE: usize = 2 * 1024 * 1024;
11 #[cfg(target_os = "l4re")]
12 pub const DEFAULT_MIN_STACK_SIZE: usize = 1024 * 1024;
13 #[cfg(target_os = "vxworks")]
14 pub const DEFAULT_MIN_STACK_SIZE: usize = 256 * 1024;
20 // Some platforms may have pthread_t as a pointer in which case we still want
21 // a thread to be Send/Sync
22 unsafe impl Send for Thread {}
23 unsafe impl Sync for Thread {}
26 // unsafe: see thread::Builder::spawn_unchecked for safety requirements
27 pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
28 let p = Box::into_raw(box p);
29 let mut native: libc::pthread_t = mem::zeroed();
30 let mut attr: libc::pthread_attr_t = mem::zeroed();
31 assert_eq!(libc::pthread_attr_init(&mut attr), 0);
33 let stack_size = cmp::max(stack, min_stack_size(&attr));
35 match libc::pthread_attr_setstacksize(&mut attr, stack_size) {
38 assert_eq!(n, libc::EINVAL);
39 // EINVAL means |stack_size| is either too small or not a
40 // multiple of the system page size. Because it's definitely
41 // >= PTHREAD_STACK_MIN, it must be an alignment issue.
42 // Round up to the nearest page and try again.
43 let page_size = os::page_size();
45 (stack_size + page_size - 1) & (-(page_size as isize - 1) as usize - 1);
46 assert_eq!(libc::pthread_attr_setstacksize(&mut attr, stack_size), 0);
50 let ret = libc::pthread_create(&mut native, &attr, thread_start, p as *mut _);
51 // Note: if the thread creation fails and this assert fails, then p will
52 // be leaked. However, an alternative design could cause double-free
53 // which is clearly worse.
54 assert_eq!(libc::pthread_attr_destroy(&mut attr), 0);
57 // The thread failed to start and as a result p was not consumed. Therefore, it is
58 // safe to reconstruct the box so that it gets deallocated.
59 drop(Box::from_raw(p));
60 Err(io::Error::from_raw_os_error(ret))
62 Ok(Thread { id: native })
65 extern "C" fn thread_start(main: *mut libc::c_void) -> *mut libc::c_void {
67 // Next, set up our stack overflow handler which may get triggered if we run
69 let _handler = stack_overflow::Handler::new();
70 // Finally, let's run some code.
71 Box::from_raw(main as *mut Box<dyn FnOnce()>)();
78 let ret = unsafe { libc::sched_yield() };
79 debug_assert_eq!(ret, 0);
82 #[cfg(any(target_os = "linux", target_os = "android"))]
83 pub fn set_name(name: &CStr) {
84 const PR_SET_NAME: libc::c_int = 15;
85 // pthread wrapper only appeared in glibc 2.12, so we use syscall
88 libc::prctl(PR_SET_NAME, name.as_ptr() as libc::c_ulong, 0, 0, 0);
92 #[cfg(any(target_os = "freebsd", target_os = "dragonfly", target_os = "openbsd"))]
93 pub fn set_name(name: &CStr) {
95 libc::pthread_set_name_np(libc::pthread_self(), name.as_ptr());
99 #[cfg(any(target_os = "macos", target_os = "ios"))]
100 pub fn set_name(name: &CStr) {
102 libc::pthread_setname_np(name.as_ptr());
106 #[cfg(target_os = "netbsd")]
107 pub fn set_name(name: &CStr) {
108 use crate::ffi::CString;
109 let cname = CString::new(&b"%s"[..]).unwrap();
111 libc::pthread_setname_np(
112 libc::pthread_self(),
114 name.as_ptr() as *mut libc::c_void,
119 #[cfg(any(target_os = "solaris", target_os = "illumos"))]
120 pub fn set_name(name: &CStr) {
122 fn pthread_setname_np(
123 libc::pthread_t, *const libc::c_char
127 if let Some(f) = pthread_setname_np.get() {
129 f(libc::pthread_self(), name.as_ptr());
135 target_env = "newlib",
138 target_os = "emscripten",
140 target_os = "vxworks"
142 pub fn set_name(_name: &CStr) {
143 // Newlib, Haiku, Emscripten, and VxWorks have no way to set a thread name.
145 #[cfg(target_os = "fuchsia")]
146 pub fn set_name(_name: &CStr) {
147 // FIXME: determine whether Fuchsia has a way to set a thread name.
150 pub fn sleep(dur: Duration) {
151 let mut secs = dur.as_secs();
152 let mut nsecs = dur.subsec_nanos() as _;
154 // If we're awoken with a signal then the return value will be -1 and
155 // nanosleep will fill in `ts` with the remaining time.
157 while secs > 0 || nsecs > 0 {
158 let mut ts = libc::timespec {
159 tv_sec: cmp::min(libc::time_t::MAX as u64, secs) as libc::time_t,
162 secs -= ts.tv_sec as u64;
163 let ts_ptr = &mut ts as *mut _;
164 if libc::nanosleep(ts_ptr, ts_ptr) == -1 {
165 assert_eq!(os::errno(), libc::EINTR);
166 secs += ts.tv_sec as u64;
177 let ret = libc::pthread_join(self.id, ptr::null_mut());
179 assert!(ret == 0, "failed to join thread: {}", io::Error::from_raw_os_error(ret));
183 pub fn id(&self) -> libc::pthread_t {
187 pub fn into_id(self) -> libc::pthread_t {
194 impl Drop for Thread {
196 let ret = unsafe { libc::pthread_detach(self.id) };
197 debug_assert_eq!(ret, 0);
202 not(target_os = "linux"),
203 not(target_os = "freebsd"),
204 not(target_os = "macos"),
205 not(target_os = "netbsd"),
206 not(target_os = "openbsd"),
207 not(target_os = "solaris")
209 #[cfg_attr(test, allow(dead_code))]
211 use crate::ops::Range;
212 pub type Guard = Range<usize>;
213 pub unsafe fn current() -> Option<Guard> {
216 pub unsafe fn init() -> Option<Guard> {
223 target_os = "freebsd",
225 target_os = "netbsd",
226 target_os = "openbsd",
227 target_os = "solaris"
229 #[cfg_attr(test, allow(dead_code))]
231 use libc::{mmap, mprotect};
232 use libc::{MAP_ANON, MAP_FAILED, MAP_FIXED, MAP_PRIVATE, PROT_NONE, PROT_READ, PROT_WRITE};
235 use crate::ops::Range;
236 use crate::sync::atomic::{AtomicUsize, Ordering};
239 // This is initialized in init() and only read from after
240 static PAGE_SIZE: AtomicUsize = AtomicUsize::new(0);
242 pub type Guard = Range<usize>;
244 #[cfg(target_os = "solaris")]
245 unsafe fn get_stack_start() -> Option<*mut libc::c_void> {
246 let mut current_stack: libc::stack_t = crate::mem::zeroed();
247 assert_eq!(libc::stack_getbounds(&mut current_stack), 0);
248 Some(current_stack.ss_sp)
251 #[cfg(target_os = "macos")]
252 unsafe fn get_stack_start() -> Option<*mut libc::c_void> {
253 let th = libc::pthread_self();
255 libc::pthread_get_stackaddr_np(th) as usize - libc::pthread_get_stacksize_np(th);
256 Some(stackaddr as *mut libc::c_void)
259 #[cfg(target_os = "openbsd")]
260 unsafe fn get_stack_start() -> Option<*mut libc::c_void> {
261 let mut current_stack: libc::stack_t = crate::mem::zeroed();
262 assert_eq!(libc::pthread_stackseg_np(libc::pthread_self(), &mut current_stack), 0);
264 let stackaddr = if libc::pthread_main_np() == 1 {
266 current_stack.ss_sp as usize - current_stack.ss_size + PAGE_SIZE.load(Ordering::Relaxed)
269 current_stack.ss_sp as usize - current_stack.ss_size
271 Some(stackaddr as *mut libc::c_void)
275 target_os = "android",
276 target_os = "freebsd",
278 target_os = "netbsd",
281 unsafe fn get_stack_start() -> Option<*mut libc::c_void> {
283 let mut attr: libc::pthread_attr_t = crate::mem::zeroed();
284 #[cfg(target_os = "freebsd")]
285 assert_eq!(libc::pthread_attr_init(&mut attr), 0);
286 #[cfg(target_os = "freebsd")]
287 let e = libc::pthread_attr_get_np(libc::pthread_self(), &mut attr);
288 #[cfg(not(target_os = "freebsd"))]
289 let e = libc::pthread_getattr_np(libc::pthread_self(), &mut attr);
291 let mut stackaddr = crate::ptr::null_mut();
292 let mut stacksize = 0;
293 assert_eq!(libc::pthread_attr_getstack(&attr, &mut stackaddr, &mut stacksize), 0);
294 ret = Some(stackaddr);
296 if e == 0 || cfg!(target_os = "freebsd") {
297 assert_eq!(libc::pthread_attr_destroy(&mut attr), 0);
302 // Precondition: PAGE_SIZE is initialized.
303 unsafe fn get_stack_start_aligned() -> Option<*mut libc::c_void> {
304 let page_size = PAGE_SIZE.load(Ordering::Relaxed);
305 assert!(page_size != 0);
306 let stackaddr = get_stack_start()?;
308 // Ensure stackaddr is page aligned! A parent process might
309 // have reset RLIMIT_STACK to be non-page aligned. The
310 // pthread_attr_getstack() reports the usable stack area
311 // stackaddr < stackaddr + stacksize, so if stackaddr is not
312 // page-aligned, calculate the fix such that stackaddr <
313 // new_page_aligned_stackaddr < stackaddr + stacksize
314 let remainder = (stackaddr as usize) % page_size;
315 Some(if remainder == 0 {
318 ((stackaddr as usize) + page_size - remainder) as *mut libc::c_void
322 pub unsafe fn init() -> Option<Guard> {
323 let page_size = os::page_size();
324 PAGE_SIZE.store(page_size, Ordering::Relaxed);
326 if cfg!(all(target_os = "linux", not(target_env = "musl"))) {
327 // Linux doesn't allocate the whole stack right away, and
328 // the kernel has its own stack-guard mechanism to fault
329 // when growing too close to an existing mapping. If we map
330 // our own guard, then the kernel starts enforcing a rather
331 // large gap above that, rendering much of the possible
332 // stack space useless. See #43052.
334 // Instead, we'll just note where we expect rlimit to start
335 // faulting, so our handler can report "stack overflow", and
336 // trust that the kernel's own stack guard will work.
337 let stackaddr = get_stack_start_aligned()?;
338 let stackaddr = stackaddr as usize;
339 Some(stackaddr - page_size..stackaddr)
340 } else if cfg!(all(target_os = "linux", target_env = "musl")) {
341 // For the main thread, the musl's pthread_attr_getstack
342 // returns the current stack size, rather than maximum size
343 // it can eventually grow to. It cannot be used to determine
344 // the position of kernel's stack guard.
346 } else if cfg!(target_os = "freebsd") {
347 // FreeBSD's stack autogrows, and optionally includes a guard page
348 // at the bottom. If we try to remap the bottom of the stack
349 // ourselves, FreeBSD's guard page moves upwards. So we'll just use
350 // the builtin guard page.
351 let stackaddr = get_stack_start_aligned()?;
352 let guardaddr = stackaddr as usize;
353 // Technically the number of guard pages is tunable and controlled
354 // by the security.bsd.stack_guard_page sysctl, but there are
355 // few reasons to change it from the default. The default value has
356 // been 1 ever since FreeBSD 11.1 and 10.4.
357 const GUARD_PAGES: usize = 1;
358 let guard = guardaddr..guardaddr + GUARD_PAGES * page_size;
361 // Reallocate the last page of the stack.
362 // This ensures SIGBUS will be raised on
364 // Systems which enforce strict PAX MPROTECT do not allow
365 // to mprotect() a mapping with less restrictive permissions
366 // than the initial mmap() used, so we mmap() here with
367 // read/write permissions and only then mprotect() it to
368 // no permissions at all. See issue #50313.
369 let stackaddr = get_stack_start_aligned()?;
373 PROT_READ | PROT_WRITE,
374 MAP_PRIVATE | MAP_ANON | MAP_FIXED,
378 if result != stackaddr || result == MAP_FAILED {
379 panic!("failed to allocate a guard page: {}", io::Error::last_os_error());
382 let result = mprotect(stackaddr, page_size, PROT_NONE);
384 panic!("failed to protect the guard page: {}", io::Error::last_os_error());
387 let guardaddr = stackaddr as usize;
389 Some(guardaddr..guardaddr + page_size)
393 #[cfg(any(target_os = "macos", target_os = "openbsd", target_os = "solaris"))]
394 pub unsafe fn current() -> Option<Guard> {
395 let stackaddr = get_stack_start()? as usize;
396 Some(stackaddr - PAGE_SIZE.load(Ordering::Relaxed)..stackaddr)
400 target_os = "android",
401 target_os = "freebsd",
403 target_os = "netbsd",
406 pub unsafe fn current() -> Option<Guard> {
408 let mut attr: libc::pthread_attr_t = crate::mem::zeroed();
409 #[cfg(target_os = "freebsd")]
410 assert_eq!(libc::pthread_attr_init(&mut attr), 0);
411 #[cfg(target_os = "freebsd")]
412 let e = libc::pthread_attr_get_np(libc::pthread_self(), &mut attr);
413 #[cfg(not(target_os = "freebsd"))]
414 let e = libc::pthread_getattr_np(libc::pthread_self(), &mut attr);
416 let mut guardsize = 0;
417 assert_eq!(libc::pthread_attr_getguardsize(&attr, &mut guardsize), 0);
419 if cfg!(all(target_os = "linux", target_env = "musl")) {
420 // musl versions before 1.1.19 always reported guard
421 // size obtained from pthread_attr_get_np as zero.
422 // Use page size as a fallback.
423 guardsize = PAGE_SIZE.load(Ordering::Relaxed);
425 panic!("there is no guard page");
428 let mut stackaddr = crate::ptr::null_mut();
430 assert_eq!(libc::pthread_attr_getstack(&attr, &mut stackaddr, &mut size), 0);
432 let stackaddr = stackaddr as usize;
433 ret = if cfg!(any(target_os = "freebsd", target_os = "netbsd")) {
434 Some(stackaddr - guardsize..stackaddr)
435 } else if cfg!(all(target_os = "linux", target_env = "musl")) {
436 Some(stackaddr - guardsize..stackaddr)
437 } else if cfg!(all(target_os = "linux", target_env = "gnu")) {
438 // glibc used to include the guard area within the stack, as noted in the BUGS
439 // section of `man pthread_attr_getguardsize`. This has been corrected starting
440 // with glibc 2.27, and in some distro backports, so the guard is now placed at the
441 // end (below) the stack. There's no easy way for us to know which we have at
442 // runtime, so we'll just match any fault in the range right above or below the
443 // stack base to call that fault a stack overflow.
444 Some(stackaddr - guardsize..stackaddr + guardsize)
446 Some(stackaddr..stackaddr + guardsize)
449 if e == 0 || cfg!(target_os = "freebsd") {
450 assert_eq!(libc::pthread_attr_destroy(&mut attr), 0);
456 // glibc >= 2.15 has a __pthread_get_minstack() function that returns
457 // PTHREAD_STACK_MIN plus bytes needed for thread-local storage.
458 // We need that information to avoid blowing up when a small stack
459 // is created in an application with big thread-local storage requirements.
460 // See #6233 for rationale and details.
461 #[cfg(target_os = "linux")]
463 fn min_stack_size(attr: *const libc::pthread_attr_t) -> usize {
464 weak!(fn __pthread_get_minstack(*const libc::pthread_attr_t) -> libc::size_t);
466 match __pthread_get_minstack.get() {
467 None => libc::PTHREAD_STACK_MIN,
468 Some(f) => unsafe { f(attr) },
472 // No point in looking up __pthread_get_minstack() on non-glibc
474 #[cfg(all(not(target_os = "linux"), not(target_os = "netbsd")))]
475 fn min_stack_size(_: *const libc::pthread_attr_t) -> usize {
476 libc::PTHREAD_STACK_MIN
479 #[cfg(target_os = "netbsd")]
480 fn min_stack_size(_: *const libc::pthread_attr_t) -> usize {