]>
Commit | Line | Data |
---|---|---|
85aaf69f | 1 | // Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT |
1a4d82fc JJ |
2 | // file at the top-level directory of this distribution and at |
3 | // http://rust-lang.org/COPYRIGHT. | |
4 | // | |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
8 | // option. This file may not be copied, modified, or distributed | |
9 | // except according to those terms. | |
10 | ||
d9579d0f | 11 | use alloc::boxed::FnBox; |
1a4d82fc | 12 | use cmp; |
54a0048b | 13 | use ffi::CStr; |
c34b1796 | 14 | use io; |
c34b1796 | 15 | use libc; |
1a4d82fc JJ |
16 | use mem; |
17 | use ptr; | |
c34b1796 | 18 | use sys::os; |
c34b1796 | 19 | use time::Duration; |
1a4d82fc | 20 | |
1a4d82fc JJ |
21 | use sys_common::thread::*; |
22 | ||
d9579d0f AL |
23 | pub struct Thread { |
24 | id: libc::pthread_t, | |
25 | } | |
26 | ||
27 | // Some platforms may have pthread_t as a pointer in which case we still want | |
28 | // a thread to be Send/Sync | |
29 | unsafe impl Send for Thread {} | |
30 | unsafe impl Sync for Thread {} | |
31 | ||
c30ab7b3 SL |
32 | // The pthread_attr_setstacksize symbol doesn't exist in the emscripten libc, |
33 | // so we have to not link to it to satisfy emcc's ERROR_ON_UNDEFINED_SYMBOLS. | |
34 | #[cfg(not(target_os = "emscripten"))] | |
35 | unsafe fn pthread_attr_setstacksize(attr: *mut libc::pthread_attr_t, | |
36 | stack_size: libc::size_t) -> libc::c_int { | |
37 | libc::pthread_attr_setstacksize(attr, stack_size) | |
38 | } | |
39 | ||
40 | #[cfg(target_os = "emscripten")] | |
41 | unsafe fn pthread_attr_setstacksize(_attr: *mut libc::pthread_attr_t, | |
42 | _stack_size: libc::size_t) -> libc::c_int { | |
43 | panic!() | |
44 | } | |
45 | ||
d9579d0f AL |
46 | impl Thread { |
47 | pub unsafe fn new<'a>(stack: usize, p: Box<FnBox() + 'a>) | |
48 | -> io::Result<Thread> { | |
49 | let p = box p; | |
50 | let mut native: libc::pthread_t = mem::zeroed(); | |
51 | let mut attr: libc::pthread_attr_t = mem::zeroed(); | |
92a42be0 | 52 | assert_eq!(libc::pthread_attr_init(&mut attr), 0); |
d9579d0f | 53 | |
e9174d1e | 54 | let stack_size = cmp::max(stack, min_stack_size(&attr)); |
c30ab7b3 SL |
55 | match pthread_attr_setstacksize(&mut attr, |
56 | stack_size) { | |
d9579d0f AL |
57 | 0 => {} |
58 | n => { | |
59 | assert_eq!(n, libc::EINVAL); | |
60 | // EINVAL means |stack_size| is either too small or not a | |
61 | // multiple of the system page size. Because it's definitely | |
62 | // >= PTHREAD_STACK_MIN, it must be an alignment issue. | |
63 | // Round up to the nearest page and try again. | |
64 | let page_size = os::page_size(); | |
65 | let stack_size = (stack_size + page_size - 1) & | |
66 | (-(page_size as isize - 1) as usize - 1); | |
92a42be0 SL |
67 | assert_eq!(libc::pthread_attr_setstacksize(&mut attr, |
68 | stack_size), 0); | |
d9579d0f AL |
69 | } |
70 | }; | |
71 | ||
92a42be0 SL |
72 | let ret = libc::pthread_create(&mut native, &attr, thread_start, |
73 | &*p as *const _ as *mut _); | |
74 | assert_eq!(libc::pthread_attr_destroy(&mut attr), 0); | |
d9579d0f AL |
75 | |
76 | return if ret != 0 { | |
77 | Err(io::Error::from_raw_os_error(ret)) | |
78 | } else { | |
79 | mem::forget(p); // ownership passed to pthread_create | |
80 | Ok(Thread { id: native }) | |
81 | }; | |
82 | ||
d9579d0f AL |
83 | extern fn thread_start(main: *mut libc::c_void) -> *mut libc::c_void { |
84 | unsafe { start_thread(main); } | |
e9174d1e | 85 | ptr::null_mut() |
d9579d0f AL |
86 | } |
87 | } | |
88 | ||
89 | pub fn yield_now() { | |
92a42be0 | 90 | let ret = unsafe { libc::sched_yield() }; |
d9579d0f AL |
91 | debug_assert_eq!(ret, 0); |
92 | } | |
93 | ||
7453a54e | 94 | #[cfg(any(target_os = "linux", |
5bcae85e | 95 | target_os = "android"))] |
54a0048b | 96 | pub fn set_name(name: &CStr) { |
d9579d0f | 97 | const PR_SET_NAME: libc::c_int = 15; |
92a42be0 SL |
98 | // pthread wrapper only appeared in glibc 2.12, so we use syscall |
99 | // directly. | |
d9579d0f | 100 | unsafe { |
54a0048b | 101 | libc::prctl(PR_SET_NAME, name.as_ptr() as libc::c_ulong, 0, 0, 0); |
d9579d0f AL |
102 | } |
103 | } | |
104 | ||
105 | #[cfg(any(target_os = "freebsd", | |
106 | target_os = "dragonfly", | |
107 | target_os = "bitrig", | |
108 | target_os = "openbsd"))] | |
54a0048b | 109 | pub fn set_name(name: &CStr) { |
d9579d0f | 110 | unsafe { |
54a0048b | 111 | libc::pthread_set_name_np(libc::pthread_self(), name.as_ptr()); |
d9579d0f AL |
112 | } |
113 | } | |
114 | ||
115 | #[cfg(any(target_os = "macos", target_os = "ios"))] | |
54a0048b | 116 | pub fn set_name(name: &CStr) { |
d9579d0f | 117 | unsafe { |
54a0048b | 118 | libc::pthread_setname_np(name.as_ptr()); |
d9579d0f AL |
119 | } |
120 | } | |
121 | ||
b039eaaf | 122 | #[cfg(target_os = "netbsd")] |
54a0048b SL |
123 | pub fn set_name(name: &CStr) { |
124 | use ffi::CString; | |
b039eaaf | 125 | let cname = CString::new(&b"%s"[..]).unwrap(); |
b039eaaf | 126 | unsafe { |
92a42be0 | 127 | libc::pthread_setname_np(libc::pthread_self(), cname.as_ptr(), |
54a0048b | 128 | name.as_ptr() as *mut libc::c_void); |
b039eaaf SL |
129 | } |
130 | } | |
9e0c209e SL |
131 | #[cfg(any(target_env = "newlib", |
132 | target_os = "solaris", | |
133 | target_os = "haiku", | |
134 | target_os = "emscripten"))] | |
54a0048b | 135 | pub fn set_name(_name: &CStr) { |
9e0c209e | 136 | // Newlib, Illumos, Haiku, and Emscripten have no way to set a thread name. |
92a42be0 | 137 | } |
c30ab7b3 SL |
138 | #[cfg(target_os = "fuchsia")] |
139 | pub fn set_name(_name: &CStr) { | |
140 | // FIXME: determine whether Fuchsia has a way to set a thread name. | |
141 | } | |
b039eaaf | 142 | |
d9579d0f | 143 | pub fn sleep(dur: Duration) { |
3157f602 XL |
144 | let mut secs = dur.as_secs(); |
145 | let mut nsecs = dur.subsec_nanos() as libc::c_long; | |
d9579d0f AL |
146 | |
147 | // If we're awoken with a signal then the return value will be -1 and | |
148 | // nanosleep will fill in `ts` with the remaining time. | |
149 | unsafe { | |
3157f602 XL |
150 | while secs > 0 || nsecs > 0 { |
151 | let mut ts = libc::timespec { | |
152 | tv_sec: cmp::min(libc::time_t::max_value() as u64, secs) as libc::time_t, | |
153 | tv_nsec: nsecs, | |
154 | }; | |
155 | secs -= ts.tv_sec as u64; | |
156 | if libc::nanosleep(&ts, &mut ts) == -1 { | |
157 | assert_eq!(os::errno(), libc::EINTR); | |
158 | secs += ts.tv_sec as u64; | |
159 | nsecs = ts.tv_nsec; | |
160 | } else { | |
161 | nsecs = 0; | |
162 | } | |
d9579d0f AL |
163 | } |
164 | } | |
165 | } | |
166 | ||
167 | pub fn join(self) { | |
168 | unsafe { | |
92a42be0 | 169 | let ret = libc::pthread_join(self.id, ptr::null_mut()); |
d9579d0f AL |
170 | mem::forget(self); |
171 | debug_assert_eq!(ret, 0); | |
172 | } | |
173 | } | |
92a42be0 SL |
174 | |
175 | pub fn id(&self) -> libc::pthread_t { self.id } | |
176 | ||
177 | pub fn into_id(self) -> libc::pthread_t { | |
178 | let id = self.id; | |
179 | mem::forget(self); | |
180 | id | |
181 | } | |
d9579d0f AL |
182 | } |
183 | ||
184 | impl Drop for Thread { | |
185 | fn drop(&mut self) { | |
92a42be0 | 186 | let ret = unsafe { libc::pthread_detach(self.id) }; |
d9579d0f AL |
187 | debug_assert_eq!(ret, 0); |
188 | } | |
189 | } | |
1a4d82fc | 190 | |
7453a54e | 191 | #[cfg(all(not(all(target_os = "linux", not(target_env = "musl"))), |
a7813a04 | 192 | not(target_os = "freebsd"), |
85aaf69f | 193 | not(target_os = "macos"), |
c34b1796 | 194 | not(target_os = "bitrig"), |
b039eaaf | 195 | not(all(target_os = "netbsd", not(target_vendor = "rumprun"))), |
7453a54e SL |
196 | not(target_os = "openbsd"), |
197 | not(target_os = "solaris")))] | |
198 | #[cfg_attr(test, allow(dead_code))] | |
1a4d82fc | 199 | pub mod guard { |
c1a9b12d SL |
200 | pub unsafe fn current() -> Option<usize> { None } |
201 | pub unsafe fn init() -> Option<usize> { None } | |
1a4d82fc JJ |
202 | } |
203 | ||
85aaf69f | 204 | |
7453a54e | 205 | #[cfg(any(all(target_os = "linux", not(target_env = "musl")), |
a7813a04 | 206 | target_os = "freebsd", |
85aaf69f | 207 | target_os = "macos", |
c34b1796 | 208 | target_os = "bitrig", |
b039eaaf | 209 | all(target_os = "netbsd", not(target_vendor = "rumprun")), |
7453a54e SL |
210 | target_os = "openbsd", |
211 | target_os = "solaris"))] | |
212 | #[cfg_attr(test, allow(dead_code))] | |
1a4d82fc | 213 | pub mod guard { |
7453a54e | 214 | use libc; |
92a42be0 SL |
215 | use libc::mmap; |
216 | use libc::{PROT_NONE, MAP_PRIVATE, MAP_ANON, MAP_FAILED, MAP_FIXED}; | |
c34b1796 | 217 | use sys::os; |
1a4d82fc | 218 | |
c34b1796 AL |
219 | #[cfg(any(target_os = "macos", |
220 | target_os = "bitrig", | |
7453a54e SL |
221 | target_os = "openbsd", |
222 | target_os = "solaris"))] | |
c1a9b12d SL |
223 | unsafe fn get_stack_start() -> Option<*mut libc::c_void> { |
224 | current().map(|s| s as *mut libc::c_void) | |
1a4d82fc JJ |
225 | } |
226 | ||
a7813a04 XL |
227 | #[cfg(any(target_os = "android", target_os = "freebsd", |
228 | target_os = "linux", target_os = "netbsd"))] | |
c1a9b12d | 229 | unsafe fn get_stack_start() -> Option<*mut libc::c_void> { |
c1a9b12d | 230 | let mut ret = None; |
7453a54e | 231 | let mut attr: libc::pthread_attr_t = ::mem::zeroed(); |
92a42be0 | 232 | assert_eq!(libc::pthread_attr_init(&mut attr), 0); |
a7813a04 XL |
233 | #[cfg(target_os = "freebsd")] |
234 | let e = libc::pthread_attr_get_np(libc::pthread_self(), &mut attr); | |
235 | #[cfg(not(target_os = "freebsd"))] | |
236 | let e = libc::pthread_getattr_np(libc::pthread_self(), &mut attr); | |
237 | if e == 0 { | |
7453a54e | 238 | let mut stackaddr = ::ptr::null_mut(); |
c1a9b12d | 239 | let mut stacksize = 0; |
92a42be0 SL |
240 | assert_eq!(libc::pthread_attr_getstack(&attr, &mut stackaddr, |
241 | &mut stacksize), 0); | |
c1a9b12d SL |
242 | ret = Some(stackaddr); |
243 | } | |
92a42be0 | 244 | assert_eq!(libc::pthread_attr_destroy(&mut attr), 0); |
c1a9b12d | 245 | ret |
1a4d82fc JJ |
246 | } |
247 | ||
c1a9b12d | 248 | pub unsafe fn init() -> Option<usize> { |
c34b1796 | 249 | let psize = os::page_size(); |
c1a9b12d SL |
250 | let mut stackaddr = match get_stack_start() { |
251 | Some(addr) => addr, | |
252 | None => return None, | |
253 | }; | |
85aaf69f SL |
254 | |
255 | // Ensure stackaddr is page aligned! A parent process might | |
256 | // have reset RLIMIT_STACK to be non-page aligned. The | |
257 | // pthread_attr_getstack() reports the usable stack area | |
258 | // stackaddr < stackaddr + stacksize, so if stackaddr is not | |
259 | // page-aligned, calculate the fix such that stackaddr < | |
260 | // new_page_aligned_stackaddr < stackaddr + stacksize | |
c34b1796 | 261 | let remainder = (stackaddr as usize) % psize; |
85aaf69f | 262 | if remainder != 0 { |
c34b1796 | 263 | stackaddr = ((stackaddr as usize) + psize - remainder) |
85aaf69f SL |
264 | as *mut libc::c_void; |
265 | } | |
1a4d82fc | 266 | |
041b39d2 XL |
267 | if cfg!(target_os = "linux") { |
268 | // Linux doesn't allocate the whole stack right away, and | |
269 | // the kernel has its own stack-guard mechanism to fault | |
270 | // when growing too close to an existing mapping. If we map | |
271 | // our own guard, then the kernel starts enforcing a rather | |
272 | // large gap above that, rendering much of the possible | |
273 | // stack space useless. See #43052. | |
274 | // | |
275 | // Instead, we'll just note where we expect rlimit to start | |
276 | // faulting, so our handler can report "stack overflow", and | |
277 | // trust that the kernel's own stack guard will work. | |
278 | Some(stackaddr as usize) | |
a7813a04 | 279 | } else { |
041b39d2 XL |
280 | // Reallocate the last page of the stack. |
281 | // This ensures SIGBUS will be raised on | |
282 | // stack overflow. | |
283 | let result = mmap(stackaddr, psize, PROT_NONE, | |
284 | MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0); | |
285 | ||
286 | if result != stackaddr || result == MAP_FAILED { | |
287 | panic!("failed to allocate a guard page"); | |
288 | } | |
289 | ||
290 | let offset = if cfg!(target_os = "freebsd") { | |
291 | 2 | |
292 | } else { | |
293 | 1 | |
294 | }; | |
1a4d82fc | 295 | |
041b39d2 XL |
296 | Some(stackaddr as usize + offset * psize) |
297 | } | |
1a4d82fc JJ |
298 | } |
299 | ||
7453a54e SL |
300 | #[cfg(target_os = "solaris")] |
301 | pub unsafe fn current() -> Option<usize> { | |
302 | let mut current_stack: libc::stack_t = ::mem::zeroed(); | |
303 | assert_eq!(libc::stack_getbounds(&mut current_stack), 0); | |
304 | Some(current_stack.ss_sp as usize) | |
305 | } | |
306 | ||
1a4d82fc | 307 | #[cfg(target_os = "macos")] |
c1a9b12d | 308 | pub unsafe fn current() -> Option<usize> { |
c30ab7b3 SL |
309 | Some((libc::pthread_get_stackaddr_np(libc::pthread_self()) as usize - |
310 | libc::pthread_get_stacksize_np(libc::pthread_self()))) | |
1a4d82fc JJ |
311 | } |
312 | ||
b039eaaf | 313 | #[cfg(any(target_os = "openbsd", target_os = "bitrig"))] |
c1a9b12d | 314 | pub unsafe fn current() -> Option<usize> { |
7453a54e | 315 | let mut current_stack: libc::stack_t = ::mem::zeroed(); |
92a42be0 SL |
316 | assert_eq!(libc::pthread_stackseg_np(libc::pthread_self(), |
317 | &mut current_stack), 0); | |
c34b1796 AL |
318 | |
319 | let extra = if cfg!(target_os = "bitrig") {3} else {1} * os::page_size(); | |
92a42be0 | 320 | Some(if libc::pthread_main_np() == 1 { |
85aaf69f | 321 | // main thread |
c30ab7b3 | 322 | current_stack.ss_sp as usize - current_stack.ss_size + extra |
85aaf69f SL |
323 | } else { |
324 | // new thread | |
c30ab7b3 | 325 | current_stack.ss_sp as usize - current_stack.ss_size |
c1a9b12d | 326 | }) |
85aaf69f SL |
327 | } |
328 | ||
a7813a04 XL |
329 | #[cfg(any(target_os = "android", target_os = "freebsd", |
330 | target_os = "linux", target_os = "netbsd"))] | |
c1a9b12d | 331 | pub unsafe fn current() -> Option<usize> { |
c1a9b12d | 332 | let mut ret = None; |
7453a54e | 333 | let mut attr: libc::pthread_attr_t = ::mem::zeroed(); |
92a42be0 | 334 | assert_eq!(libc::pthread_attr_init(&mut attr), 0); |
a7813a04 XL |
335 | #[cfg(target_os = "freebsd")] |
336 | let e = libc::pthread_attr_get_np(libc::pthread_self(), &mut attr); | |
337 | #[cfg(not(target_os = "freebsd"))] | |
338 | let e = libc::pthread_getattr_np(libc::pthread_self(), &mut attr); | |
339 | if e == 0 { | |
c1a9b12d | 340 | let mut guardsize = 0; |
92a42be0 | 341 | assert_eq!(libc::pthread_attr_getguardsize(&attr, &mut guardsize), 0); |
c1a9b12d SL |
342 | if guardsize == 0 { |
343 | panic!("there is no guard page"); | |
344 | } | |
7453a54e | 345 | let mut stackaddr = ::ptr::null_mut(); |
c1a9b12d | 346 | let mut size = 0; |
92a42be0 SL |
347 | assert_eq!(libc::pthread_attr_getstack(&attr, &mut stackaddr, |
348 | &mut size), 0); | |
c1a9b12d | 349 | |
a7813a04 | 350 | ret = if cfg!(target_os = "freebsd") { |
c30ab7b3 | 351 | Some(stackaddr as usize - guardsize) |
a7813a04 | 352 | } else if cfg!(target_os = "netbsd") { |
b039eaaf SL |
353 | Some(stackaddr as usize) |
354 | } else { | |
c30ab7b3 | 355 | Some(stackaddr as usize + guardsize) |
b039eaaf | 356 | }; |
1a4d82fc | 357 | } |
92a42be0 | 358 | assert_eq!(libc::pthread_attr_destroy(&mut attr), 0); |
e9174d1e | 359 | ret |
c34b1796 | 360 | } |
1a4d82fc JJ |
361 | } |
362 | ||
1a4d82fc JJ |
363 | // glibc >= 2.15 has a __pthread_get_minstack() function that returns |
364 | // PTHREAD_STACK_MIN plus however many bytes are needed for thread-local | |
365 | // storage. We need that information to avoid blowing up when a small stack | |
366 | // is created in an application with big thread-local storage requirements. | |
367 | // See #6233 for rationale and details. | |
1a4d82fc | 368 | #[cfg(target_os = "linux")] |
b039eaaf | 369 | #[allow(deprecated)] |
d9579d0f | 370 | fn min_stack_size(attr: *const libc::pthread_attr_t) -> usize { |
7453a54e | 371 | weak!(fn __pthread_get_minstack(*const libc::pthread_attr_t) -> libc::size_t); |
c34b1796 | 372 | |
7453a54e | 373 | match __pthread_get_minstack.get() { |
c30ab7b3 SL |
374 | None => libc::PTHREAD_STACK_MIN, |
375 | Some(f) => unsafe { f(attr) }, | |
1a4d82fc JJ |
376 | } |
377 | } | |
378 | ||
c34b1796 AL |
379 | // No point in looking up __pthread_get_minstack() on non-glibc |
380 | // platforms. | |
7453a54e SL |
381 | #[cfg(all(not(target_os = "linux"), |
382 | not(target_os = "netbsd")))] | |
383 | fn min_stack_size(_: *const libc::pthread_attr_t) -> usize { | |
c30ab7b3 | 384 | libc::PTHREAD_STACK_MIN |
7453a54e SL |
385 | } |
386 | ||
387 | #[cfg(target_os = "netbsd")] | |
d9579d0f | 388 | fn min_stack_size(_: *const libc::pthread_attr_t) -> usize { |
7453a54e | 389 | 2048 // just a guess |
1a4d82fc | 390 | } |