]>
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 | ||
c34b1796 AL |
11 | #![allow(dead_code)] |
12 | ||
d9579d0f | 13 | use prelude::v1::*; |
1a4d82fc | 14 | |
d9579d0f | 15 | use alloc::boxed::FnBox; |
1a4d82fc | 16 | use cmp; |
c34b1796 AL |
17 | use ffi::CString; |
18 | use io; | |
19 | use libc::consts::os::posix01::PTHREAD_STACK_MIN; | |
20 | use libc; | |
1a4d82fc JJ |
21 | use mem; |
22 | use ptr; | |
c34b1796 | 23 | use sys::os; |
c34b1796 | 24 | use time::Duration; |
1a4d82fc JJ |
25 | |
26 | use sys_common::stack::RED_ZONE; | |
27 | use sys_common::thread::*; | |
28 | ||
d9579d0f AL |
29 | pub struct Thread { |
30 | id: libc::pthread_t, | |
31 | } | |
32 | ||
33 | // Some platforms may have pthread_t as a pointer in which case we still want | |
34 | // a thread to be Send/Sync | |
35 | unsafe impl Send for Thread {} | |
36 | unsafe impl Sync for Thread {} | |
37 | ||
38 | impl Thread { | |
39 | pub unsafe fn new<'a>(stack: usize, p: Box<FnBox() + 'a>) | |
40 | -> io::Result<Thread> { | |
41 | let p = box p; | |
42 | let mut native: libc::pthread_t = mem::zeroed(); | |
43 | let mut attr: libc::pthread_attr_t = mem::zeroed(); | |
44 | assert_eq!(pthread_attr_init(&mut attr), 0); | |
45 | ||
46 | // Reserve room for the red zone, the runtime's stack of last resort. | |
47 | let stack_size = cmp::max(stack, RED_ZONE + min_stack_size(&attr)); | |
48 | match pthread_attr_setstacksize(&mut attr, stack_size as libc::size_t) { | |
49 | 0 => {} | |
50 | n => { | |
51 | assert_eq!(n, libc::EINVAL); | |
52 | // EINVAL means |stack_size| is either too small or not a | |
53 | // multiple of the system page size. Because it's definitely | |
54 | // >= PTHREAD_STACK_MIN, it must be an alignment issue. | |
55 | // Round up to the nearest page and try again. | |
56 | let page_size = os::page_size(); | |
57 | let stack_size = (stack_size + page_size - 1) & | |
58 | (-(page_size as isize - 1) as usize - 1); | |
59 | let stack_size = stack_size as libc::size_t; | |
60 | assert_eq!(pthread_attr_setstacksize(&mut attr, stack_size), 0); | |
61 | } | |
62 | }; | |
63 | ||
64 | let ret = pthread_create(&mut native, &attr, thread_start, | |
65 | &*p as *const _ as *mut _); | |
66 | assert_eq!(pthread_attr_destroy(&mut attr), 0); | |
67 | ||
68 | return if ret != 0 { | |
69 | Err(io::Error::from_raw_os_error(ret)) | |
70 | } else { | |
71 | mem::forget(p); // ownership passed to pthread_create | |
72 | Ok(Thread { id: native }) | |
73 | }; | |
74 | ||
75 | #[no_stack_check] | |
76 | extern fn thread_start(main: *mut libc::c_void) -> *mut libc::c_void { | |
77 | unsafe { start_thread(main); } | |
78 | 0 as *mut _ | |
79 | } | |
80 | } | |
81 | ||
82 | pub fn yield_now() { | |
83 | let ret = unsafe { sched_yield() }; | |
84 | debug_assert_eq!(ret, 0); | |
85 | } | |
86 | ||
87 | #[cfg(any(target_os = "linux", target_os = "android"))] | |
88 | pub fn set_name(name: &str) { | |
89 | // pthread wrapper only appeared in glibc 2.12, so we use syscall | |
90 | // directly. | |
91 | extern { | |
92 | fn prctl(option: libc::c_int, arg2: libc::c_ulong, | |
93 | arg3: libc::c_ulong, arg4: libc::c_ulong, | |
94 | arg5: libc::c_ulong) -> libc::c_int; | |
95 | } | |
96 | const PR_SET_NAME: libc::c_int = 15; | |
97 | let cname = CString::new(name).unwrap_or_else(|_| { | |
98 | panic!("thread name may not contain interior null bytes") | |
99 | }); | |
100 | unsafe { | |
101 | prctl(PR_SET_NAME, cname.as_ptr() as libc::c_ulong, 0, 0, 0); | |
102 | } | |
103 | } | |
104 | ||
105 | #[cfg(any(target_os = "freebsd", | |
106 | target_os = "dragonfly", | |
107 | target_os = "bitrig", | |
c1a9b12d | 108 | target_os = "netbsd", |
d9579d0f AL |
109 | target_os = "openbsd"))] |
110 | pub fn set_name(name: &str) { | |
111 | extern { | |
112 | fn pthread_set_name_np(tid: libc::pthread_t, | |
113 | name: *const libc::c_char); | |
114 | } | |
115 | let cname = CString::new(name).unwrap(); | |
116 | unsafe { | |
117 | pthread_set_name_np(pthread_self(), cname.as_ptr()); | |
118 | } | |
119 | } | |
120 | ||
121 | #[cfg(any(target_os = "macos", target_os = "ios"))] | |
122 | pub fn set_name(name: &str) { | |
123 | extern { | |
124 | fn pthread_setname_np(name: *const libc::c_char) -> libc::c_int; | |
125 | } | |
126 | let cname = CString::new(name).unwrap(); | |
127 | unsafe { | |
128 | pthread_setname_np(cname.as_ptr()); | |
129 | } | |
130 | } | |
131 | ||
132 | pub fn sleep(dur: Duration) { | |
133 | let mut ts = libc::timespec { | |
c1a9b12d SL |
134 | tv_sec: dur.as_secs() as libc::time_t, |
135 | tv_nsec: dur.subsec_nanos() as libc::c_long, | |
d9579d0f AL |
136 | }; |
137 | ||
138 | // If we're awoken with a signal then the return value will be -1 and | |
139 | // nanosleep will fill in `ts` with the remaining time. | |
140 | unsafe { | |
141 | while libc::nanosleep(&ts, &mut ts) == -1 { | |
142 | assert_eq!(os::errno(), libc::EINTR); | |
143 | } | |
144 | } | |
145 | } | |
146 | ||
147 | pub fn join(self) { | |
148 | unsafe { | |
149 | let ret = pthread_join(self.id, ptr::null_mut()); | |
150 | mem::forget(self); | |
151 | debug_assert_eq!(ret, 0); | |
152 | } | |
153 | } | |
154 | } | |
155 | ||
156 | impl Drop for Thread { | |
157 | fn drop(&mut self) { | |
158 | let ret = unsafe { pthread_detach(self.id) }; | |
159 | debug_assert_eq!(ret, 0); | |
160 | } | |
161 | } | |
1a4d82fc | 162 | |
85aaf69f SL |
163 | #[cfg(all(not(target_os = "linux"), |
164 | not(target_os = "macos"), | |
c34b1796 | 165 | not(target_os = "bitrig"), |
c1a9b12d | 166 | not(target_os = "netbsd"), |
85aaf69f | 167 | not(target_os = "openbsd")))] |
1a4d82fc | 168 | pub mod guard { |
c1a9b12d SL |
169 | use prelude::v1::*; |
170 | ||
171 | pub unsafe fn current() -> Option<usize> { None } | |
172 | pub unsafe fn init() -> Option<usize> { None } | |
1a4d82fc JJ |
173 | } |
174 | ||
85aaf69f SL |
175 | |
176 | #[cfg(any(target_os = "linux", | |
177 | target_os = "macos", | |
c34b1796 | 178 | target_os = "bitrig", |
c1a9b12d | 179 | target_os = "netbsd", |
85aaf69f | 180 | target_os = "openbsd"))] |
c34b1796 | 181 | #[allow(unused_imports)] |
1a4d82fc | 182 | pub mod guard { |
c1a9b12d SL |
183 | use prelude::v1::*; |
184 | ||
c34b1796 AL |
185 | use libc::{self, pthread_t}; |
186 | use libc::funcs::posix88::mman::mmap; | |
1a4d82fc JJ |
187 | use libc::consts::os::posix88::{PROT_NONE, |
188 | MAP_PRIVATE, | |
189 | MAP_ANON, | |
190 | MAP_FAILED, | |
191 | MAP_FIXED}; | |
c34b1796 AL |
192 | use mem; |
193 | use ptr; | |
194 | use super::{pthread_self, pthread_attr_destroy}; | |
195 | use sys::os; | |
1a4d82fc | 196 | |
c34b1796 AL |
197 | #[cfg(any(target_os = "macos", |
198 | target_os = "bitrig", | |
c1a9b12d | 199 | target_os = "netbsd", |
c34b1796 | 200 | target_os = "openbsd"))] |
c1a9b12d SL |
201 | unsafe fn get_stack_start() -> Option<*mut libc::c_void> { |
202 | current().map(|s| s as *mut libc::c_void) | |
1a4d82fc JJ |
203 | } |
204 | ||
205 | #[cfg(any(target_os = "linux", target_os = "android"))] | |
c1a9b12d SL |
206 | unsafe fn get_stack_start() -> Option<*mut libc::c_void> { |
207 | use super::pthread_attr_init; | |
208 | ||
209 | let mut ret = None; | |
1a4d82fc | 210 | let mut attr: libc::pthread_attr_t = mem::zeroed(); |
c1a9b12d SL |
211 | assert_eq!(pthread_attr_init(&mut attr), 0); |
212 | if pthread_getattr_np(pthread_self(), &mut attr) == 0 { | |
213 | let mut stackaddr = ptr::null_mut(); | |
214 | let mut stacksize = 0; | |
215 | assert_eq!(pthread_attr_getstack(&attr, &mut stackaddr, | |
216 | &mut stacksize), 0); | |
217 | ret = Some(stackaddr); | |
218 | } | |
c34b1796 | 219 | assert_eq!(pthread_attr_destroy(&mut attr), 0); |
c1a9b12d | 220 | ret |
1a4d82fc JJ |
221 | } |
222 | ||
c1a9b12d | 223 | pub unsafe fn init() -> Option<usize> { |
c34b1796 | 224 | let psize = os::page_size(); |
c1a9b12d SL |
225 | let mut stackaddr = match get_stack_start() { |
226 | Some(addr) => addr, | |
227 | None => return None, | |
228 | }; | |
85aaf69f SL |
229 | |
230 | // Ensure stackaddr is page aligned! A parent process might | |
231 | // have reset RLIMIT_STACK to be non-page aligned. The | |
232 | // pthread_attr_getstack() reports the usable stack area | |
233 | // stackaddr < stackaddr + stacksize, so if stackaddr is not | |
234 | // page-aligned, calculate the fix such that stackaddr < | |
235 | // new_page_aligned_stackaddr < stackaddr + stacksize | |
c34b1796 | 236 | let remainder = (stackaddr as usize) % psize; |
85aaf69f | 237 | if remainder != 0 { |
c34b1796 | 238 | stackaddr = ((stackaddr as usize) + psize - remainder) |
85aaf69f SL |
239 | as *mut libc::c_void; |
240 | } | |
1a4d82fc JJ |
241 | |
242 | // Rellocate the last page of the stack. | |
243 | // This ensures SIGBUS will be raised on | |
244 | // stack overflow. | |
245 | let result = mmap(stackaddr, | |
c34b1796 | 246 | psize as libc::size_t, |
1a4d82fc JJ |
247 | PROT_NONE, |
248 | MAP_PRIVATE | MAP_ANON | MAP_FIXED, | |
249 | -1, | |
250 | 0); | |
251 | ||
252 | if result != stackaddr || result == MAP_FAILED { | |
253 | panic!("failed to allocate a guard page"); | |
254 | } | |
255 | ||
c34b1796 | 256 | let offset = if cfg!(target_os = "linux") {2} else {1}; |
1a4d82fc | 257 | |
c1a9b12d | 258 | Some(stackaddr as usize + offset * psize) |
1a4d82fc JJ |
259 | } |
260 | ||
261 | #[cfg(target_os = "macos")] | |
c1a9b12d | 262 | pub unsafe fn current() -> Option<usize> { |
c34b1796 AL |
263 | extern { |
264 | fn pthread_get_stackaddr_np(thread: pthread_t) -> *mut libc::c_void; | |
265 | fn pthread_get_stacksize_np(thread: pthread_t) -> libc::size_t; | |
266 | } | |
c1a9b12d SL |
267 | Some((pthread_get_stackaddr_np(pthread_self()) as libc::size_t - |
268 | pthread_get_stacksize_np(pthread_self())) as usize) | |
1a4d82fc JJ |
269 | } |
270 | ||
c1a9b12d SL |
271 | #[cfg(any(target_os = "openbsd", target_os = "netbsd", target_os = "bitrig"))] |
272 | pub unsafe fn current() -> Option<usize> { | |
c34b1796 AL |
273 | #[repr(C)] |
274 | struct stack_t { | |
275 | ss_sp: *mut libc::c_void, | |
276 | ss_size: libc::size_t, | |
277 | ss_flags: libc::c_int, | |
278 | } | |
279 | extern { | |
280 | fn pthread_main_np() -> libc::c_uint; | |
281 | fn pthread_stackseg_np(thread: pthread_t, | |
282 | sinfo: *mut stack_t) -> libc::c_uint; | |
85aaf69f SL |
283 | } |
284 | ||
c34b1796 AL |
285 | let mut current_stack: stack_t = mem::zeroed(); |
286 | assert_eq!(pthread_stackseg_np(pthread_self(), &mut current_stack), 0); | |
287 | ||
288 | let extra = if cfg!(target_os = "bitrig") {3} else {1} * os::page_size(); | |
c1a9b12d | 289 | Some(if pthread_main_np() == 1 { |
85aaf69f | 290 | // main thread |
c34b1796 | 291 | current_stack.ss_sp as usize - current_stack.ss_size as usize + extra |
85aaf69f SL |
292 | } else { |
293 | // new thread | |
c34b1796 | 294 | current_stack.ss_sp as usize - current_stack.ss_size as usize |
c1a9b12d | 295 | }) |
85aaf69f SL |
296 | } |
297 | ||
1a4d82fc | 298 | #[cfg(any(target_os = "linux", target_os = "android"))] |
c1a9b12d SL |
299 | pub unsafe fn current() -> Option<usize> { |
300 | use super::pthread_attr_init; | |
301 | ||
302 | let mut ret = None; | |
1a4d82fc | 303 | let mut attr: libc::pthread_attr_t = mem::zeroed(); |
c1a9b12d SL |
304 | assert_eq!(pthread_attr_init(&mut attr), 0); |
305 | if pthread_getattr_np(pthread_self(), &mut attr) == 0 { | |
306 | let mut guardsize = 0; | |
307 | assert_eq!(pthread_attr_getguardsize(&attr, &mut guardsize), 0); | |
308 | if guardsize == 0 { | |
309 | panic!("there is no guard page"); | |
310 | } | |
311 | let mut stackaddr = ptr::null_mut(); | |
312 | let mut size = 0; | |
313 | assert_eq!(pthread_attr_getstack(&attr, &mut stackaddr, &mut size), 0); | |
314 | ||
315 | ret = Some(stackaddr as usize + guardsize as usize); | |
1a4d82fc | 316 | } |
c34b1796 | 317 | assert_eq!(pthread_attr_destroy(&mut attr), 0); |
c1a9b12d | 318 | return ret |
c34b1796 AL |
319 | } |
320 | ||
321 | #[cfg(any(target_os = "linux", target_os = "android"))] | |
322 | extern { | |
323 | fn pthread_getattr_np(native: libc::pthread_t, | |
324 | attr: *mut libc::pthread_attr_t) -> libc::c_int; | |
325 | fn pthread_attr_getguardsize(attr: *const libc::pthread_attr_t, | |
326 | guardsize: *mut libc::size_t) -> libc::c_int; | |
327 | fn pthread_attr_getstack(attr: *const libc::pthread_attr_t, | |
328 | stackaddr: *mut *mut libc::c_void, | |
329 | stacksize: *mut libc::size_t) -> libc::c_int; | |
1a4d82fc JJ |
330 | } |
331 | } | |
332 | ||
1a4d82fc JJ |
333 | // glibc >= 2.15 has a __pthread_get_minstack() function that returns |
334 | // PTHREAD_STACK_MIN plus however many bytes are needed for thread-local | |
335 | // storage. We need that information to avoid blowing up when a small stack | |
336 | // is created in an application with big thread-local storage requirements. | |
337 | // See #6233 for rationale and details. | |
338 | // | |
c34b1796 AL |
339 | // Use dlsym to get the symbol value at runtime, both for |
340 | // compatibility with older versions of glibc, and to avoid creating | |
341 | // dependencies on GLIBC_PRIVATE symbols. Assumes that we've been | |
342 | // dynamically linked to libpthread but that is currently always the | |
343 | // case. We previously used weak linkage (under the same assumption), | |
344 | // but that caused Debian to detect an unnecessarily strict versioned | |
345 | // dependency on libc6 (#23628). | |
1a4d82fc | 346 | #[cfg(target_os = "linux")] |
d9579d0f | 347 | fn min_stack_size(attr: *const libc::pthread_attr_t) -> usize { |
c34b1796 | 348 | use dynamic_lib::DynamicLibrary; |
62682a34 | 349 | use sync::Once; |
c34b1796 | 350 | |
1a4d82fc | 351 | type F = unsafe extern "C" fn(*const libc::pthread_attr_t) -> libc::size_t; |
62682a34 | 352 | static INIT: Once = Once::new(); |
c34b1796 AL |
353 | static mut __pthread_get_minstack: Option<F> = None; |
354 | ||
355 | INIT.call_once(|| { | |
d9579d0f AL |
356 | let lib = match DynamicLibrary::open(None) { |
357 | Ok(l) => l, | |
358 | Err(..) => return, | |
359 | }; | |
c34b1796 AL |
360 | unsafe { |
361 | if let Ok(f) = lib.symbol("__pthread_get_minstack") { | |
362 | __pthread_get_minstack = Some(mem::transmute::<*const (), F>(f)); | |
363 | } | |
364 | } | |
365 | }); | |
366 | ||
367 | match unsafe { __pthread_get_minstack } { | |
d9579d0f AL |
368 | None => PTHREAD_STACK_MIN as usize, |
369 | Some(f) => unsafe { f(attr) as usize }, | |
1a4d82fc JJ |
370 | } |
371 | } | |
372 | ||
c34b1796 AL |
373 | // No point in looking up __pthread_get_minstack() on non-glibc |
374 | // platforms. | |
1a4d82fc | 375 | #[cfg(not(target_os = "linux"))] |
d9579d0f AL |
376 | fn min_stack_size(_: *const libc::pthread_attr_t) -> usize { |
377 | PTHREAD_STACK_MIN as usize | |
1a4d82fc JJ |
378 | } |
379 | ||
1a4d82fc | 380 | extern { |
c34b1796 | 381 | fn pthread_self() -> libc::pthread_t; |
1a4d82fc JJ |
382 | fn pthread_create(native: *mut libc::pthread_t, |
383 | attr: *const libc::pthread_attr_t, | |
c34b1796 | 384 | f: extern fn(*mut libc::c_void) -> *mut libc::c_void, |
1a4d82fc JJ |
385 | value: *mut libc::c_void) -> libc::c_int; |
386 | fn pthread_join(native: libc::pthread_t, | |
387 | value: *mut *mut libc::c_void) -> libc::c_int; | |
388 | fn pthread_attr_init(attr: *mut libc::pthread_attr_t) -> libc::c_int; | |
c34b1796 | 389 | fn pthread_attr_destroy(attr: *mut libc::pthread_attr_t) -> libc::c_int; |
1a4d82fc JJ |
390 | fn pthread_attr_setstacksize(attr: *mut libc::pthread_attr_t, |
391 | stack_size: libc::size_t) -> libc::c_int; | |
392 | fn pthread_attr_setdetachstate(attr: *mut libc::pthread_attr_t, | |
393 | state: libc::c_int) -> libc::c_int; | |
394 | fn pthread_detach(thread: libc::pthread_t) -> libc::c_int; | |
395 | fn sched_yield() -> libc::c_int; | |
396 | } |