1 // Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
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.
17 use libc::consts::os::posix01::{PTHREAD_CREATE_JOINABLE, PTHREAD_STACK_MIN};
22 use sys_common::stack::RED_ZONE;
23 use sys_common::thread::*;
25 pub type rust_thread = libc::pthread_t;
26 pub type rust_thread_return = *mut u8;
27 pub type StartFn = extern "C" fn(*mut libc::c_void) -> rust_thread_return;
30 pub extern fn thread_start(main: *mut libc::c_void) -> rust_thread_return {
31 return start_thread(main);
34 #[cfg(all(not(target_os = "linux"), not(target_os = "macos")))]
36 pub unsafe fn current() -> uint {
40 pub unsafe fn main() -> uint {
44 pub unsafe fn init() {
48 #[cfg(any(target_os = "linux", target_os = "macos"))]
51 #[cfg(any(target_os = "linux", target_os = "android"))]
53 #[cfg(any(target_os = "linux", target_os = "android"))]
56 use libc::funcs::posix88::mman::{mmap};
57 use libc::consts::os::posix88::{PROT_NONE,
63 // These are initialized in init() and only read from after
64 static mut PAGE_SIZE: uint = 0;
65 static mut GUARD_PAGE: uint = 0;
67 #[cfg(target_os = "macos")]
68 unsafe fn get_stack_start() -> *mut libc::c_void {
69 current() as *mut libc::c_void
72 #[cfg(any(target_os = "linux", target_os = "android"))]
73 unsafe fn get_stack_start() -> *mut libc::c_void {
74 let mut attr: libc::pthread_attr_t = mem::zeroed();
75 if pthread_getattr_np(pthread_self(), &mut attr) != 0 {
76 panic!("failed to get thread attributes");
78 let mut stackaddr = ptr::null_mut();
79 let mut stacksize = 0;
80 if pthread_attr_getstack(&attr, &mut stackaddr, &mut stacksize) != 0 {
81 panic!("failed to get stack information");
83 if pthread_attr_destroy(&mut attr) != 0 {
84 panic!("failed to destroy thread attributes");
89 pub unsafe fn init() {
90 let psize = libc::sysconf(libc::consts::os::sysconf::_SC_PAGESIZE);
92 panic!("failed to get page size");
95 PAGE_SIZE = psize as uint;
97 let mut stackaddr = get_stack_start();
99 // Ensure stackaddr is page aligned! A parent process might
100 // have reset RLIMIT_STACK to be non-page aligned. The
101 // pthread_attr_getstack() reports the usable stack area
102 // stackaddr < stackaddr + stacksize, so if stackaddr is not
103 // page-aligned, calculate the fix such that stackaddr <
104 // new_page_aligned_stackaddr < stackaddr + stacksize
105 let remainder = (stackaddr as usize) % (PAGE_SIZE as usize);
107 stackaddr = ((stackaddr as usize) + (PAGE_SIZE as usize) - remainder)
108 as *mut libc::c_void;
111 // Rellocate the last page of the stack.
112 // This ensures SIGBUS will be raised on
114 let result = mmap(stackaddr,
115 PAGE_SIZE as libc::size_t,
117 MAP_PRIVATE | MAP_ANON | MAP_FIXED,
121 if result != stackaddr || result == MAP_FAILED {
122 panic!("failed to allocate a guard page");
125 let offset = if cfg!(target_os = "linux") {
131 GUARD_PAGE = stackaddr as uint + offset * PAGE_SIZE;
134 pub unsafe fn main() -> uint {
138 #[cfg(target_os = "macos")]
139 pub unsafe fn current() -> uint {
140 (pthread_get_stackaddr_np(pthread_self()) as libc::size_t -
141 pthread_get_stacksize_np(pthread_self())) as uint
144 #[cfg(any(target_os = "linux", target_os = "android"))]
145 pub unsafe fn current() -> uint {
146 let mut attr: libc::pthread_attr_t = mem::zeroed();
147 if pthread_getattr_np(pthread_self(), &mut attr) != 0 {
148 panic!("failed to get thread attributes");
150 let mut guardsize = 0;
151 if pthread_attr_getguardsize(&attr, &mut guardsize) != 0 {
152 panic!("failed to get stack guard page");
155 panic!("there is no guard page");
157 let mut stackaddr = ptr::null_mut();
158 let mut stacksize = 0;
159 if pthread_attr_getstack(&attr, &mut stackaddr, &mut stacksize) != 0 {
160 panic!("failed to get stack information");
162 if pthread_attr_destroy(&mut attr) != 0 {
163 panic!("failed to destroy thread attributes");
166 stackaddr as uint + guardsize as uint
170 pub unsafe fn create(stack: uint, p: Thunk) -> rust_thread {
171 let mut native: libc::pthread_t = mem::zeroed();
172 let mut attr: libc::pthread_attr_t = mem::zeroed();
173 assert_eq!(pthread_attr_init(&mut attr), 0);
174 assert_eq!(pthread_attr_setdetachstate(&mut attr,
175 PTHREAD_CREATE_JOINABLE), 0);
177 // Reserve room for the red zone, the runtime's stack of last resort.
178 let stack_size = cmp::max(stack, RED_ZONE + min_stack_size(&attr) as uint);
179 match pthread_attr_setstacksize(&mut attr, stack_size as libc::size_t) {
183 // EINVAL means |stack_size| is either too small or not a
184 // multiple of the system page size. Because it's definitely
185 // >= PTHREAD_STACK_MIN, it must be an alignment issue.
186 // Round up to the nearest page and try again.
187 let page_size = libc::sysconf(libc::_SC_PAGESIZE) as uint;
188 let stack_size = (stack_size + page_size - 1) &
189 (-(page_size as int - 1) as uint - 1);
190 assert_eq!(pthread_attr_setstacksize(&mut attr, stack_size as libc::size_t), 0);
193 // This cannot really happen.
194 panic!("pthread_attr_setstacksize() error: {}", errno);
198 let arg: *mut libc::c_void = mem::transmute(box p); // must box since sizeof(p)=2*uint
199 let ret = pthread_create(&mut native, &attr, thread_start, arg);
200 assert_eq!(pthread_attr_destroy(&mut attr), 0);
203 // be sure to not leak the closure
204 let _p: Box<Box<FnOnce()+Send>> = mem::transmute(arg);
205 panic!("failed to spawn native thread: {}", ret);
210 #[cfg(any(target_os = "linux", target_os = "android"))]
211 pub unsafe fn set_name(name: &str) {
212 // Using prctl() rather than pthread_setname_np(),
213 // because pthread_setname_np() wasn't added until glibc 2.12
214 // PR_SET_NAME since Linux 2.6.9
215 let cname = CString::from_slice(name.as_bytes());
216 prctl(15i32 /* = PR_SET_NAME */, cname.as_ptr() as u64, 0u64, 0u64, 0u64);
219 #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
220 pub unsafe fn set_name(name: &str) {
221 // pthread_set_name_np() since almost forever on all BSDs
222 let cname = CString::from_slice(name.as_bytes());
223 pthread_set_name_np(pthread_self(), cname.as_ptr());
226 #[cfg(target_os = "macos")]
227 pub unsafe fn set_name(name: &str) {
228 // pthread_setname_np() since OS X 10.6
229 let cname = CString::from_slice(name.as_bytes());
230 pthread_setname_np(cname.as_ptr());
233 pub unsafe fn join(native: rust_thread) {
234 assert_eq!(pthread_join(native, ptr::null_mut()), 0);
237 pub unsafe fn detach(native: rust_thread) {
238 assert_eq!(pthread_detach(native), 0);
241 pub unsafe fn yield_now() { assert_eq!(sched_yield(), 0); }
242 // glibc >= 2.15 has a __pthread_get_minstack() function that returns
243 // PTHREAD_STACK_MIN plus however many bytes are needed for thread-local
244 // storage. We need that information to avoid blowing up when a small stack
245 // is created in an application with big thread-local storage requirements.
246 // See #6233 for rationale and details.
248 // Link weakly to the symbol for compatibility with older versions of glibc.
249 // Assumes that we've been dynamically linked to libpthread but that is
250 // currently always the case. Note that you need to check that the symbol
251 // is non-null before calling it!
252 #[cfg(target_os = "linux")]
253 fn min_stack_size(attr: *const libc::pthread_attr_t) -> libc::size_t {
254 type F = unsafe extern "C" fn(*const libc::pthread_attr_t) -> libc::size_t;
256 #[linkage = "extern_weak"]
257 static __pthread_get_minstack: *const ();
259 if __pthread_get_minstack.is_null() {
262 unsafe { mem::transmute::<*const (), F>(__pthread_get_minstack)(attr) }
266 // __pthread_get_minstack() is marked as weak but extern_weak linkage is
267 // not supported on OS X, hence this kludge...
268 #[cfg(not(target_os = "linux"))]
269 fn min_stack_size(_: *const libc::pthread_attr_t) -> libc::size_t {
273 #[cfg(any(target_os = "linux", target_os = "android"))]
275 fn prctl(option: libc::c_int,
279 arg5: libc::c_ulong) -> libc::c_int;
282 #[cfg(any(target_os = "linux"))]
284 pub fn pthread_self() -> libc::pthread_t;
285 pub fn pthread_getattr_np(native: libc::pthread_t,
286 attr: *mut libc::pthread_attr_t) -> libc::c_int;
287 pub fn pthread_attr_getguardsize(attr: *const libc::pthread_attr_t,
288 guardsize: *mut libc::size_t) -> libc::c_int;
289 pub fn pthread_attr_getstack(attr: *const libc::pthread_attr_t,
290 stackaddr: *mut *mut libc::c_void,
291 stacksize: *mut libc::size_t) -> libc::c_int;
294 #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
296 pub fn pthread_self() -> libc::pthread_t;
297 fn pthread_set_name_np(tid: libc::pthread_t, name: *const c_char);
300 #[cfg(target_os = "macos")]
302 pub fn pthread_self() -> libc::pthread_t;
303 pub fn pthread_get_stackaddr_np(thread: libc::pthread_t) -> *mut libc::c_void;
304 pub fn pthread_get_stacksize_np(thread: libc::pthread_t) -> libc::size_t;
305 fn pthread_setname_np(name: *const c_char) -> libc::c_int;
309 fn pthread_create(native: *mut libc::pthread_t,
310 attr: *const libc::pthread_attr_t,
312 value: *mut libc::c_void) -> libc::c_int;
313 fn pthread_join(native: libc::pthread_t,
314 value: *mut *mut libc::c_void) -> libc::c_int;
315 fn pthread_attr_init(attr: *mut libc::pthread_attr_t) -> libc::c_int;
316 pub fn pthread_attr_destroy(attr: *mut libc::pthread_attr_t) -> libc::c_int;
317 fn pthread_attr_setstacksize(attr: *mut libc::pthread_attr_t,
318 stack_size: libc::size_t) -> libc::c_int;
319 fn pthread_attr_setdetachstate(attr: *mut libc::pthread_attr_t,
320 state: libc::c_int) -> libc::c_int;
321 fn pthread_detach(thread: libc::pthread_t) -> libc::c_int;
322 fn sched_yield() -> libc::c_int;