]> git.proxmox.com Git - rustc.git/blame - library/std/src/sys/unix/thread.rs
New upstream version 1.55.0+dfsg1
[rustc.git] / library / std / src / sys / unix / thread.rs
CommitLineData
532ac7d7
XL
1use crate::cmp;
2use crate::ffi::CStr;
3use crate::io;
4use crate::mem;
136023e0 5use crate::num::NonZeroUsize;
532ac7d7 6use crate::ptr;
ba9703b0 7use crate::sys::{os, stack_overflow};
532ac7d7
XL
8use crate::time::Duration;
9
136023e0
XL
10#[cfg(any(target_os = "linux", target_os = "solaris", target_os = "illumos"))]
11use crate::sys::weak::weak;
29967ef6 12#[cfg(not(any(target_os = "l4re", target_os = "vxworks")))]
ea8adc8c
XL
13pub const DEFAULT_MIN_STACK_SIZE: usize = 2 * 1024 * 1024;
14#[cfg(target_os = "l4re")]
15pub const DEFAULT_MIN_STACK_SIZE: usize = 1024 * 1024;
29967ef6
XL
16#[cfg(target_os = "vxworks")]
17pub const DEFAULT_MIN_STACK_SIZE: usize = 256 * 1024;
ea8adc8c 18
d9579d0f
AL
19pub struct Thread {
20 id: libc::pthread_t,
21}
22
23// Some platforms may have pthread_t as a pointer in which case we still want
24// a thread to be Send/Sync
25unsafe impl Send for Thread {}
26unsafe impl Sync for Thread {}
27
28impl Thread {
0731742a 29 // unsafe: see thread::Builder::spawn_unchecked for safety requirements
60c5eb7d 30 pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
ba9703b0 31 let p = Box::into_raw(box p);
d9579d0f
AL
32 let mut native: libc::pthread_t = mem::zeroed();
33 let mut attr: libc::pthread_attr_t = mem::zeroed();
92a42be0 34 assert_eq!(libc::pthread_attr_init(&mut attr), 0);
d9579d0f 35
e9174d1e 36 let stack_size = cmp::max(stack, min_stack_size(&attr));
ea8adc8c 37
29967ef6 38 match libc::pthread_attr_setstacksize(&mut attr, stack_size) {
d9579d0f
AL
39 0 => {}
40 n => {
41 assert_eq!(n, libc::EINVAL);
42 // EINVAL means |stack_size| is either too small or not a
43 // multiple of the system page size. Because it's definitely
44 // >= PTHREAD_STACK_MIN, it must be an alignment issue.
45 // Round up to the nearest page and try again.
46 let page_size = os::page_size();
60c5eb7d
XL
47 let stack_size =
48 (stack_size + page_size - 1) & (-(page_size as isize - 1) as usize - 1);
49 assert_eq!(libc::pthread_attr_setstacksize(&mut attr, stack_size), 0);
d9579d0f
AL
50 }
51 };
52
ba9703b0
XL
53 let ret = libc::pthread_create(&mut native, &attr, thread_start, p as *mut _);
54 // Note: if the thread creation fails and this assert fails, then p will
55 // be leaked. However, an alternative design could cause double-free
56 // which is clearly worse.
92a42be0 57 assert_eq!(libc::pthread_attr_destroy(&mut attr), 0);
d9579d0f
AL
58
59 return if ret != 0 {
ba9703b0
XL
60 // The thread failed to start and as a result p was not consumed. Therefore, it is
61 // safe to reconstruct the box so that it gets deallocated.
62 drop(Box::from_raw(p));
d9579d0f
AL
63 Err(io::Error::from_raw_os_error(ret))
64 } else {
d9579d0f
AL
65 Ok(Thread { id: native })
66 };
67
60c5eb7d
XL
68 extern "C" fn thread_start(main: *mut libc::c_void) -> *mut libc::c_void {
69 unsafe {
ba9703b0
XL
70 // Next, set up our stack overflow handler which may get triggered if we run
71 // out of stack.
72 let _handler = stack_overflow::Handler::new();
73 // Finally, let's run some code.
74 Box::from_raw(main as *mut Box<dyn FnOnce()>)();
60c5eb7d 75 }
e9174d1e 76 ptr::null_mut()
d9579d0f
AL
77 }
78 }
79
80 pub fn yield_now() {
92a42be0 81 let ret = unsafe { libc::sched_yield() };
d9579d0f
AL
82 debug_assert_eq!(ret, 0);
83 }
84
60c5eb7d 85 #[cfg(any(target_os = "linux", target_os = "android"))]
54a0048b 86 pub fn set_name(name: &CStr) {
d9579d0f 87 const PR_SET_NAME: libc::c_int = 15;
92a42be0
SL
88 // pthread wrapper only appeared in glibc 2.12, so we use syscall
89 // directly.
d9579d0f 90 unsafe {
54a0048b 91 libc::prctl(PR_SET_NAME, name.as_ptr() as libc::c_ulong, 0, 0, 0);
d9579d0f
AL
92 }
93 }
94
60c5eb7d 95 #[cfg(any(target_os = "freebsd", target_os = "dragonfly", target_os = "openbsd"))]
54a0048b 96 pub fn set_name(name: &CStr) {
d9579d0f 97 unsafe {
54a0048b 98 libc::pthread_set_name_np(libc::pthread_self(), name.as_ptr());
d9579d0f
AL
99 }
100 }
101
102 #[cfg(any(target_os = "macos", target_os = "ios"))]
54a0048b 103 pub fn set_name(name: &CStr) {
d9579d0f 104 unsafe {
54a0048b 105 libc::pthread_setname_np(name.as_ptr());
d9579d0f
AL
106 }
107 }
108
b039eaaf 109 #[cfg(target_os = "netbsd")]
54a0048b 110 pub fn set_name(name: &CStr) {
532ac7d7 111 use crate::ffi::CString;
b039eaaf 112 let cname = CString::new(&b"%s"[..]).unwrap();
b039eaaf 113 unsafe {
60c5eb7d
XL
114 libc::pthread_setname_np(
115 libc::pthread_self(),
116 cname.as_ptr(),
117 name.as_ptr() as *mut libc::c_void,
118 );
b039eaaf
SL
119 }
120 }
416331ca 121
ba9703b0 122 #[cfg(any(target_os = "solaris", target_os = "illumos"))]
416331ca
XL
123 pub fn set_name(name: &CStr) {
124 weak! {
125 fn pthread_setname_np(
126 libc::pthread_t, *const libc::c_char
127 ) -> libc::c_int
128 }
129
130 if let Some(f) = pthread_setname_np.get() {
60c5eb7d
XL
131 unsafe {
132 f(libc::pthread_self(), name.as_ptr());
133 }
416331ca
XL
134 }
135 }
136
60c5eb7d
XL
137 #[cfg(any(
138 target_env = "newlib",
139 target_os = "haiku",
140 target_os = "l4re",
141 target_os = "emscripten",
29967ef6
XL
142 target_os = "redox",
143 target_os = "vxworks"
60c5eb7d 144 ))]
54a0048b 145 pub fn set_name(_name: &CStr) {
29967ef6 146 // Newlib, Haiku, Emscripten, and VxWorks have no way to set a thread name.
92a42be0 147 }
c30ab7b3
SL
148 #[cfg(target_os = "fuchsia")]
149 pub fn set_name(_name: &CStr) {
150 // FIXME: determine whether Fuchsia has a way to set a thread name.
151 }
b039eaaf 152
d9579d0f 153 pub fn sleep(dur: Duration) {
3157f602 154 let mut secs = dur.as_secs();
abe05a73 155 let mut nsecs = dur.subsec_nanos() as _;
d9579d0f
AL
156
157 // If we're awoken with a signal then the return value will be -1 and
158 // nanosleep will fill in `ts` with the remaining time.
159 unsafe {
3157f602
XL
160 while secs > 0 || nsecs > 0 {
161 let mut ts = libc::timespec {
f035d41b 162 tv_sec: cmp::min(libc::time_t::MAX as u64, secs) as libc::time_t,
3157f602
XL
163 tv_nsec: nsecs,
164 };
165 secs -= ts.tv_sec as u64;
29967ef6
XL
166 let ts_ptr = &mut ts as *mut _;
167 if libc::nanosleep(ts_ptr, ts_ptr) == -1 {
3157f602
XL
168 assert_eq!(os::errno(), libc::EINTR);
169 secs += ts.tv_sec as u64;
170 nsecs = ts.tv_nsec;
171 } else {
172 nsecs = 0;
173 }
d9579d0f
AL
174 }
175 }
176 }
177
178 pub fn join(self) {
179 unsafe {
92a42be0 180 let ret = libc::pthread_join(self.id, ptr::null_mut());
d9579d0f 181 mem::forget(self);
60c5eb7d 182 assert!(ret == 0, "failed to join thread: {}", io::Error::from_raw_os_error(ret));
d9579d0f
AL
183 }
184 }
92a42be0 185
60c5eb7d
XL
186 pub fn id(&self) -> libc::pthread_t {
187 self.id
188 }
92a42be0
SL
189
190 pub fn into_id(self) -> libc::pthread_t {
191 let id = self.id;
192 mem::forget(self);
193 id
194 }
d9579d0f
AL
195}
196
197impl Drop for Thread {
198 fn drop(&mut self) {
92a42be0 199 let ret = unsafe { libc::pthread_detach(self.id) };
d9579d0f
AL
200 debug_assert_eq!(ret, 0);
201 }
202}
1a4d82fc 203
136023e0
XL
204pub fn available_concurrency() -> io::Result<NonZeroUsize> {
205 cfg_if::cfg_if! {
206 if #[cfg(any(
207 target_os = "android",
208 target_os = "emscripten",
209 target_os = "fuchsia",
210 target_os = "ios",
211 target_os = "linux",
212 target_os = "macos",
213 target_os = "solaris",
214 target_os = "illumos",
215 ))] {
216 match unsafe { libc::sysconf(libc::_SC_NPROCESSORS_ONLN) } {
217 -1 => Err(io::Error::last_os_error()),
218 0 => Err(io::Error::new_const(io::ErrorKind::NotFound, &"The number of hardware threads is not known for the target platform")),
219 cpus => Ok(unsafe { NonZeroUsize::new_unchecked(cpus as usize) }),
220 }
221 } else if #[cfg(any(target_os = "freebsd", target_os = "dragonfly", target_os = "netbsd"))] {
222 use crate::ptr;
223
224 let mut cpus: libc::c_uint = 0;
225 let mut cpus_size = crate::mem::size_of_val(&cpus);
226
227 unsafe {
228 cpus = libc::sysconf(libc::_SC_NPROCESSORS_ONLN) as libc::c_uint;
229 }
230
231 // Fallback approach in case of errors or no hardware threads.
232 if cpus < 1 {
233 let mut mib = [libc::CTL_HW, libc::HW_NCPU, 0, 0];
234 let res = unsafe {
235 libc::sysctl(
236 mib.as_mut_ptr(),
237 2,
238 &mut cpus as *mut _ as *mut _,
239 &mut cpus_size as *mut _ as *mut _,
240 ptr::null_mut(),
241 0,
242 )
243 };
244
245 // Handle errors if any.
246 if res == -1 {
247 return Err(io::Error::last_os_error());
248 } else if cpus == 0 {
249 return Err(io::Error::new_const(io::ErrorKind::NotFound, &"The number of hardware threads is not known for the target platform"));
250 }
251 }
252 Ok(unsafe { NonZeroUsize::new_unchecked(cpus as usize) })
253 } else if #[cfg(target_os = "openbsd")] {
254 use crate::ptr;
255
256 let mut cpus: libc::c_uint = 0;
257 let mut cpus_size = crate::mem::size_of_val(&cpus);
258 let mut mib = [libc::CTL_HW, libc::HW_NCPU, 0, 0];
259
260 let res = unsafe {
261 libc::sysctl(
262 mib.as_mut_ptr(),
263 2,
264 &mut cpus as *mut _ as *mut _,
265 &mut cpus_size as *mut _ as *mut _,
266 ptr::null_mut(),
267 0,
268 )
269 };
270
271 // Handle errors if any.
272 if res == -1 {
273 return Err(io::Error::last_os_error());
274 } else if cpus == 0 {
275 return Err(io::Error::new_const(io::ErrorKind::NotFound, &"The number of hardware threads is not known for the target platform"));
276 }
277
278 Ok(unsafe { NonZeroUsize::new_unchecked(cpus as usize) })
279 } else {
280 // FIXME: implement on vxWorks, Redox, Haiku, l4re
281 Err(io::Error::new_const(io::ErrorKind::Unsupported, &"Getting the number of hardware threads is not supported on the target platform"))
282 }
283 }
284}
285
60c5eb7d 286#[cfg(all(
3dfed10e 287 not(target_os = "linux"),
60c5eb7d
XL
288 not(target_os = "freebsd"),
289 not(target_os = "macos"),
6a06907d 290 not(target_os = "netbsd"),
60c5eb7d
XL
291 not(target_os = "openbsd"),
292 not(target_os = "solaris")
293))]
7453a54e 294#[cfg_attr(test, allow(dead_code))]
1a4d82fc 295pub mod guard {
532ac7d7 296 use crate::ops::Range;
2c00a5a8 297 pub type Guard = Range<usize>;
60c5eb7d
XL
298 pub unsafe fn current() -> Option<Guard> {
299 None
300 }
301 pub unsafe fn init() -> Option<Guard> {
302 None
303 }
1a4d82fc
JJ
304}
305
60c5eb7d 306#[cfg(any(
3dfed10e 307 target_os = "linux",
60c5eb7d
XL
308 target_os = "freebsd",
309 target_os = "macos",
6a06907d 310 target_os = "netbsd",
60c5eb7d
XL
311 target_os = "openbsd",
312 target_os = "solaris"
313))]
7453a54e 314#[cfg_attr(test, allow(dead_code))]
1a4d82fc 315pub mod guard {
83c7162d 316 use libc::{mmap, mprotect};
60c5eb7d 317 use libc::{MAP_ANON, MAP_FAILED, MAP_FIXED, MAP_PRIVATE, PROT_NONE, PROT_READ, PROT_WRITE};
532ac7d7 318
6a06907d 319 use crate::io;
532ac7d7 320 use crate::ops::Range;
f035d41b 321 use crate::sync::atomic::{AtomicUsize, Ordering};
532ac7d7 322 use crate::sys::os;
1a4d82fc 323
2c00a5a8 324 // This is initialized in init() and only read from after
f035d41b 325 static PAGE_SIZE: AtomicUsize = AtomicUsize::new(0);
2c00a5a8
XL
326
327 pub type Guard = Range<usize>;
328
329 #[cfg(target_os = "solaris")]
330 unsafe fn get_stack_start() -> Option<*mut libc::c_void> {
532ac7d7 331 let mut current_stack: libc::stack_t = crate::mem::zeroed();
2c00a5a8
XL
332 assert_eq!(libc::stack_getbounds(&mut current_stack), 0);
333 Some(current_stack.ss_sp)
334 }
335
336 #[cfg(target_os = "macos")]
c1a9b12d 337 unsafe fn get_stack_start() -> Option<*mut libc::c_void> {
74b04a01
XL
338 let th = libc::pthread_self();
339 let stackaddr =
340 libc::pthread_get_stackaddr_np(th) as usize - libc::pthread_get_stacksize_np(th);
2c00a5a8
XL
341 Some(stackaddr as *mut libc::c_void)
342 }
343
48663c56 344 #[cfg(target_os = "openbsd")]
2c00a5a8 345 unsafe fn get_stack_start() -> Option<*mut libc::c_void> {
532ac7d7 346 let mut current_stack: libc::stack_t = crate::mem::zeroed();
60c5eb7d 347 assert_eq!(libc::pthread_stackseg_np(libc::pthread_self(), &mut current_stack), 0);
2c00a5a8 348
2c00a5a8
XL
349 let stackaddr = if libc::pthread_main_np() == 1 {
350 // main thread
f035d41b 351 current_stack.ss_sp as usize - current_stack.ss_size + PAGE_SIZE.load(Ordering::Relaxed)
2c00a5a8
XL
352 } else {
353 // new thread
354 current_stack.ss_sp as usize - current_stack.ss_size
355 };
356 Some(stackaddr as *mut libc::c_void)
1a4d82fc
JJ
357 }
358
60c5eb7d
XL
359 #[cfg(any(
360 target_os = "android",
361 target_os = "freebsd",
362 target_os = "linux",
363 target_os = "netbsd",
364 target_os = "l4re"
365 ))]
c1a9b12d 366 unsafe fn get_stack_start() -> Option<*mut libc::c_void> {
c1a9b12d 367 let mut ret = None;
532ac7d7 368 let mut attr: libc::pthread_attr_t = crate::mem::zeroed();
1b1a35ee 369 #[cfg(target_os = "freebsd")]
92a42be0 370 assert_eq!(libc::pthread_attr_init(&mut attr), 0);
a7813a04 371 #[cfg(target_os = "freebsd")]
60c5eb7d 372 let e = libc::pthread_attr_get_np(libc::pthread_self(), &mut attr);
a7813a04 373 #[cfg(not(target_os = "freebsd"))]
60c5eb7d 374 let e = libc::pthread_getattr_np(libc::pthread_self(), &mut attr);
a7813a04 375 if e == 0 {
532ac7d7 376 let mut stackaddr = crate::ptr::null_mut();
c1a9b12d 377 let mut stacksize = 0;
60c5eb7d 378 assert_eq!(libc::pthread_attr_getstack(&attr, &mut stackaddr, &mut stacksize), 0);
c1a9b12d
SL
379 ret = Some(stackaddr);
380 }
1b1a35ee
XL
381 if e == 0 || cfg!(target_os = "freebsd") {
382 assert_eq!(libc::pthread_attr_destroy(&mut attr), 0);
383 }
c1a9b12d 384 ret
1a4d82fc
JJ
385 }
386
83c7162d
XL
387 // Precondition: PAGE_SIZE is initialized.
388 unsafe fn get_stack_start_aligned() -> Option<*mut libc::c_void> {
f035d41b
XL
389 let page_size = PAGE_SIZE.load(Ordering::Relaxed);
390 assert!(page_size != 0);
83c7162d 391 let stackaddr = get_stack_start()?;
85aaf69f
SL
392
393 // Ensure stackaddr is page aligned! A parent process might
394 // have reset RLIMIT_STACK to be non-page aligned. The
395 // pthread_attr_getstack() reports the usable stack area
396 // stackaddr < stackaddr + stacksize, so if stackaddr is not
397 // page-aligned, calculate the fix such that stackaddr <
398 // new_page_aligned_stackaddr < stackaddr + stacksize
f035d41b 399 let remainder = (stackaddr as usize) % page_size;
83c7162d
XL
400 Some(if remainder == 0 {
401 stackaddr
402 } else {
f035d41b 403 ((stackaddr as usize) + page_size - remainder) as *mut libc::c_void
83c7162d
XL
404 })
405 }
406
407 pub unsafe fn init() -> Option<Guard> {
f035d41b
XL
408 let page_size = os::page_size();
409 PAGE_SIZE.store(page_size, Ordering::Relaxed);
83c7162d 410
3dfed10e 411 if cfg!(all(target_os = "linux", not(target_env = "musl"))) {
041b39d2
XL
412 // Linux doesn't allocate the whole stack right away, and
413 // the kernel has its own stack-guard mechanism to fault
414 // when growing too close to an existing mapping. If we map
415 // our own guard, then the kernel starts enforcing a rather
416 // large gap above that, rendering much of the possible
417 // stack space useless. See #43052.
418 //
419 // Instead, we'll just note where we expect rlimit to start
420 // faulting, so our handler can report "stack overflow", and
421 // trust that the kernel's own stack guard will work.
3dfed10e 422 let stackaddr = get_stack_start_aligned()?;
2c00a5a8 423 let stackaddr = stackaddr as usize;
f035d41b 424 Some(stackaddr - page_size..stackaddr)
3dfed10e
XL
425 } else if cfg!(all(target_os = "linux", target_env = "musl")) {
426 // For the main thread, the musl's pthread_attr_getstack
427 // returns the current stack size, rather than maximum size
428 // it can eventually grow to. It cannot be used to determine
429 // the position of kernel's stack guard.
430 None
cdc7bbd5
XL
431 } else if cfg!(target_os = "freebsd") {
432 // FreeBSD's stack autogrows, and optionally includes a guard page
433 // at the bottom. If we try to remap the bottom of the stack
434 // ourselves, FreeBSD's guard page moves upwards. So we'll just use
435 // the builtin guard page.
436 let stackaddr = get_stack_start_aligned()?;
437 let guardaddr = stackaddr as usize;
438 // Technically the number of guard pages is tunable and controlled
439 // by the security.bsd.stack_guard_page sysctl, but there are
440 // few reasons to change it from the default. The default value has
441 // been 1 ever since FreeBSD 11.1 and 10.4.
442 const GUARD_PAGES: usize = 1;
443 let guard = guardaddr..guardaddr + GUARD_PAGES * page_size;
444 Some(guard)
a7813a04 445 } else {
041b39d2
XL
446 // Reallocate the last page of the stack.
447 // This ensures SIGBUS will be raised on
448 // stack overflow.
94b46f34
XL
449 // Systems which enforce strict PAX MPROTECT do not allow
450 // to mprotect() a mapping with less restrictive permissions
451 // than the initial mmap() used, so we mmap() here with
452 // read/write permissions and only then mprotect() it to
453 // no permissions at all. See issue #50313.
3dfed10e 454 let stackaddr = get_stack_start_aligned()?;
60c5eb7d
XL
455 let result = mmap(
456 stackaddr,
f035d41b 457 page_size,
60c5eb7d
XL
458 PROT_READ | PROT_WRITE,
459 MAP_PRIVATE | MAP_ANON | MAP_FIXED,
460 -1,
461 0,
462 );
041b39d2 463 if result != stackaddr || result == MAP_FAILED {
6a06907d 464 panic!("failed to allocate a guard page: {}", io::Error::last_os_error());
041b39d2
XL
465 }
466
f035d41b 467 let result = mprotect(stackaddr, page_size, PROT_NONE);
94b46f34 468 if result != 0 {
6a06907d 469 panic!("failed to protect the guard page: {}", io::Error::last_os_error());
94b46f34
XL
470 }
471
2c00a5a8 472 let guardaddr = stackaddr as usize;
1a4d82fc 473
cdc7bbd5 474 Some(guardaddr..guardaddr + page_size)
041b39d2 475 }
1a4d82fc
JJ
476 }
477
60c5eb7d 478 #[cfg(any(target_os = "macos", target_os = "openbsd", target_os = "solaris"))]
2c00a5a8
XL
479 pub unsafe fn current() -> Option<Guard> {
480 let stackaddr = get_stack_start()? as usize;
f035d41b 481 Some(stackaddr - PAGE_SIZE.load(Ordering::Relaxed)..stackaddr)
85aaf69f
SL
482 }
483
60c5eb7d
XL
484 #[cfg(any(
485 target_os = "android",
486 target_os = "freebsd",
487 target_os = "linux",
488 target_os = "netbsd",
489 target_os = "l4re"
490 ))]
2c00a5a8 491 pub unsafe fn current() -> Option<Guard> {
c1a9b12d 492 let mut ret = None;
532ac7d7 493 let mut attr: libc::pthread_attr_t = crate::mem::zeroed();
1b1a35ee 494 #[cfg(target_os = "freebsd")]
92a42be0 495 assert_eq!(libc::pthread_attr_init(&mut attr), 0);
a7813a04 496 #[cfg(target_os = "freebsd")]
60c5eb7d 497 let e = libc::pthread_attr_get_np(libc::pthread_self(), &mut attr);
a7813a04 498 #[cfg(not(target_os = "freebsd"))]
60c5eb7d 499 let e = libc::pthread_getattr_np(libc::pthread_self(), &mut attr);
a7813a04 500 if e == 0 {
c1a9b12d 501 let mut guardsize = 0;
92a42be0 502 assert_eq!(libc::pthread_attr_getguardsize(&attr, &mut guardsize), 0);
c1a9b12d 503 if guardsize == 0 {
3dfed10e
XL
504 if cfg!(all(target_os = "linux", target_env = "musl")) {
505 // musl versions before 1.1.19 always reported guard
506 // size obtained from pthread_attr_get_np as zero.
507 // Use page size as a fallback.
508 guardsize = PAGE_SIZE.load(Ordering::Relaxed);
509 } else {
510 panic!("there is no guard page");
511 }
c1a9b12d 512 }
532ac7d7 513 let mut stackaddr = crate::ptr::null_mut();
c1a9b12d 514 let mut size = 0;
60c5eb7d 515 assert_eq!(libc::pthread_attr_getstack(&attr, &mut stackaddr, &mut size), 0);
c1a9b12d 516
2c00a5a8 517 let stackaddr = stackaddr as usize;
cdc7bbd5 518 ret = if cfg!(any(target_os = "freebsd", target_os = "netbsd")) {
2c00a5a8 519 Some(stackaddr - guardsize..stackaddr)
3dfed10e
XL
520 } else if cfg!(all(target_os = "linux", target_env = "musl")) {
521 Some(stackaddr - guardsize..stackaddr)
2c00a5a8
XL
522 } else if cfg!(all(target_os = "linux", target_env = "gnu")) {
523 // glibc used to include the guard area within the stack, as noted in the BUGS
524 // section of `man pthread_attr_getguardsize`. This has been corrected starting
525 // with glibc 2.27, and in some distro backports, so the guard is now placed at the
526 // end (below) the stack. There's no easy way for us to know which we have at
527 // runtime, so we'll just match any fault in the range right above or below the
528 // stack base to call that fault a stack overflow.
529 Some(stackaddr - guardsize..stackaddr + guardsize)
b039eaaf 530 } else {
2c00a5a8 531 Some(stackaddr..stackaddr + guardsize)
b039eaaf 532 };
1a4d82fc 533 }
1b1a35ee
XL
534 if e == 0 || cfg!(target_os = "freebsd") {
535 assert_eq!(libc::pthread_attr_destroy(&mut attr), 0);
536 }
e9174d1e 537 ret
c34b1796 538 }
1a4d82fc
JJ
539}
540
1a4d82fc 541// glibc >= 2.15 has a __pthread_get_minstack() function that returns
74b04a01
XL
542// PTHREAD_STACK_MIN plus bytes needed for thread-local storage.
543// We need that information to avoid blowing up when a small stack
1a4d82fc
JJ
544// is created in an application with big thread-local storage requirements.
545// See #6233 for rationale and details.
1a4d82fc 546#[cfg(target_os = "linux")]
b039eaaf 547#[allow(deprecated)]
d9579d0f 548fn min_stack_size(attr: *const libc::pthread_attr_t) -> usize {
7453a54e 549 weak!(fn __pthread_get_minstack(*const libc::pthread_attr_t) -> libc::size_t);
c34b1796 550
7453a54e 551 match __pthread_get_minstack.get() {
c30ab7b3
SL
552 None => libc::PTHREAD_STACK_MIN,
553 Some(f) => unsafe { f(attr) },
1a4d82fc
JJ
554 }
555}
556
c34b1796
AL
557// No point in looking up __pthread_get_minstack() on non-glibc
558// platforms.
60c5eb7d 559#[cfg(all(not(target_os = "linux"), not(target_os = "netbsd")))]
7453a54e 560fn min_stack_size(_: *const libc::pthread_attr_t) -> usize {
c30ab7b3 561 libc::PTHREAD_STACK_MIN
7453a54e
SL
562}
563
564#[cfg(target_os = "netbsd")]
d9579d0f 565fn min_stack_size(_: *const libc::pthread_attr_t) -> usize {
7453a54e 566 2048 // just a guess
1a4d82fc 567}