]> git.lizzy.rs Git - rust.git/blob - src/libstd/sys/unix/thread.rs
Thread native name setting, fix #10302
[rust.git] / src / libstd / sys / unix / thread.rs
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.
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
11 use core::prelude::*;
12
13 use boxed::Box;
14 use cmp;
15 use mem;
16 use ptr;
17 use libc::consts::os::posix01::{PTHREAD_CREATE_JOINABLE, PTHREAD_STACK_MIN};
18 use libc;
19 use thunk::Thunk;
20 use ffi::CString;
21
22 use sys_common::stack::RED_ZONE;
23 use sys_common::thread::*;
24
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;
28
29 #[no_stack_check]
30 pub extern fn thread_start(main: *mut libc::c_void) -> rust_thread_return {
31     return start_thread(main);
32 }
33
34 #[cfg(all(not(target_os = "linux"), not(target_os = "macos")))]
35 pub mod guard {
36     pub unsafe fn current() -> uint {
37         0
38     }
39
40     pub unsafe fn main() -> uint {
41         0
42     }
43
44     pub unsafe fn init() {
45     }
46 }
47
48 #[cfg(any(target_os = "linux", target_os = "macos"))]
49 pub mod guard {
50     use super::*;
51     #[cfg(any(target_os = "linux", target_os = "android"))]
52     use mem;
53     #[cfg(any(target_os = "linux", target_os = "android"))]
54     use ptr;
55     use libc;
56     use libc::funcs::posix88::mman::{mmap};
57     use libc::consts::os::posix88::{PROT_NONE,
58                                     MAP_PRIVATE,
59                                     MAP_ANON,
60                                     MAP_FAILED,
61                                     MAP_FIXED};
62
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;
66
67     #[cfg(target_os = "macos")]
68     unsafe fn get_stack_start() -> *mut libc::c_void {
69         current() as *mut libc::c_void
70     }
71
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");
77         }
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");
82         }
83         if pthread_attr_destroy(&mut attr) != 0 {
84             panic!("failed to destroy thread attributes");
85         }
86         stackaddr
87     }
88
89     pub unsafe fn init() {
90         let psize = libc::sysconf(libc::consts::os::sysconf::_SC_PAGESIZE);
91         if psize == -1 {
92             panic!("failed to get page size");
93         }
94
95         PAGE_SIZE = psize as uint;
96
97         let mut stackaddr = get_stack_start();
98
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);
106         if remainder != 0 {
107             stackaddr = ((stackaddr as usize) + (PAGE_SIZE as usize) - remainder)
108                 as *mut libc::c_void;
109         }
110
111         // Rellocate the last page of the stack.
112         // This ensures SIGBUS will be raised on
113         // stack overflow.
114         let result = mmap(stackaddr,
115                           PAGE_SIZE as libc::size_t,
116                           PROT_NONE,
117                           MAP_PRIVATE | MAP_ANON | MAP_FIXED,
118                           -1,
119                           0);
120
121         if result != stackaddr || result == MAP_FAILED {
122             panic!("failed to allocate a guard page");
123         }
124
125         let offset = if cfg!(target_os = "linux") {
126             2
127         } else {
128             1
129         };
130
131         GUARD_PAGE = stackaddr as uint + offset * PAGE_SIZE;
132     }
133
134     pub unsafe fn main() -> uint {
135         GUARD_PAGE
136     }
137
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
142     }
143
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");
149         }
150         let mut guardsize = 0;
151         if pthread_attr_getguardsize(&attr, &mut guardsize) != 0 {
152             panic!("failed to get stack guard page");
153         }
154         if guardsize == 0 {
155             panic!("there is no guard page");
156         }
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");
161         }
162         if pthread_attr_destroy(&mut attr) != 0 {
163             panic!("failed to destroy thread attributes");
164         }
165
166         stackaddr as uint + guardsize as uint
167     }
168 }
169
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);
176
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) {
180         0 => {
181         },
182         libc::EINVAL => {
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);
191         },
192         errno => {
193             // This cannot really happen.
194             panic!("pthread_attr_setstacksize() error: {}", errno);
195         },
196     };
197
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);
201
202     if ret != 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);
206     }
207     native
208 }
209
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);
217 }
218
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());
224 }
225
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());
231 }
232
233 pub unsafe fn join(native: rust_thread) {
234     assert_eq!(pthread_join(native, ptr::null_mut()), 0);
235 }
236
237 pub unsafe fn detach(native: rust_thread) {
238     assert_eq!(pthread_detach(native), 0);
239 }
240
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.
247 //
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;
255     extern {
256         #[linkage = "extern_weak"]
257         static __pthread_get_minstack: *const ();
258     }
259     if __pthread_get_minstack.is_null() {
260         PTHREAD_STACK_MIN
261     } else {
262         unsafe { mem::transmute::<*const (), F>(__pthread_get_minstack)(attr) }
263     }
264 }
265
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 {
270     PTHREAD_STACK_MIN
271 }
272
273 #[cfg(any(target_os = "linux", target_os = "android"))]
274 extern {
275     fn prctl(option: libc::c_int,
276         arg2: libc::c_ulong,
277         arg3: libc::c_ulong,
278         arg4: libc::c_ulong,
279         arg5: libc::c_ulong) -> libc::c_int;
280 }
281
282 #[cfg(any(target_os = "linux"))]
283 extern {
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;
292 }
293
294 #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
295 extern {
296     pub fn pthread_self() -> libc::pthread_t;
297     fn pthread_set_name_np(tid: libc::pthread_t, name: *const c_char);
298 }
299
300 #[cfg(target_os = "macos")]
301 extern {
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;
306 }
307
308 extern {
309     fn pthread_create(native: *mut libc::pthread_t,
310                       attr: *const libc::pthread_attr_t,
311                       f: StartFn,
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;
323 }