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