]> git.lizzy.rs Git - rust.git/blob - src/libstd/sys/unix/thread.rs
433c37a97f396223a26bd5c1d4d0036ec46a48de
[rust.git] / src / libstd / sys / unix / thread.rs
1 // Copyright 2014-2015 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"),
35           not(target_os = "macos"),
36           not(target_os = "openbsd")))]
37 pub mod guard {
38     pub unsafe fn current() -> uint {
39         0
40     }
41
42     pub unsafe fn main() -> uint {
43         0
44     }
45
46     pub unsafe fn init() {
47     }
48 }
49
50
51 #[cfg(any(target_os = "linux",
52           target_os = "macos",
53           target_os = "openbsd"))]
54 pub mod guard {
55     use super::*;
56     #[cfg(any(target_os = "linux",
57               target_os = "android",
58               target_os = "openbsd"))]
59     use mem;
60     #[cfg(any(target_os = "linux", target_os = "android"))]
61     use ptr;
62     use libc;
63     use libc::funcs::posix88::mman::{mmap};
64     use libc::consts::os::posix88::{PROT_NONE,
65                                     MAP_PRIVATE,
66                                     MAP_ANON,
67                                     MAP_FAILED,
68                                     MAP_FIXED};
69
70     // These are initialized in init() and only read from after
71     static mut PAGE_SIZE: uint = 0;
72     static mut GUARD_PAGE: uint = 0;
73
74     #[cfg(any(target_os = "macos", target_os = "openbsd"))]
75     unsafe fn get_stack_start() -> *mut libc::c_void {
76         current() as *mut libc::c_void
77     }
78
79     #[cfg(any(target_os = "linux", target_os = "android"))]
80     unsafe fn get_stack_start() -> *mut libc::c_void {
81         let mut attr: libc::pthread_attr_t = mem::zeroed();
82         if pthread_getattr_np(pthread_self(), &mut attr) != 0 {
83             panic!("failed to get thread attributes");
84         }
85         let mut stackaddr = ptr::null_mut();
86         let mut stacksize = 0;
87         if pthread_attr_getstack(&attr, &mut stackaddr, &mut stacksize) != 0 {
88             panic!("failed to get stack information");
89         }
90         if pthread_attr_destroy(&mut attr) != 0 {
91             panic!("failed to destroy thread attributes");
92         }
93         stackaddr
94     }
95
96     pub unsafe fn init() {
97         let psize = libc::sysconf(libc::consts::os::sysconf::_SC_PAGESIZE);
98         if psize == -1 {
99             panic!("failed to get page size");
100         }
101
102         PAGE_SIZE = psize as uint;
103
104         let mut stackaddr = get_stack_start();
105
106         // Ensure stackaddr is page aligned! A parent process might
107         // have reset RLIMIT_STACK to be non-page aligned. The
108         // pthread_attr_getstack() reports the usable stack area
109         // stackaddr < stackaddr + stacksize, so if stackaddr is not
110         // page-aligned, calculate the fix such that stackaddr <
111         // new_page_aligned_stackaddr < stackaddr + stacksize
112         let remainder = (stackaddr as usize) % (PAGE_SIZE as usize);
113         if remainder != 0 {
114             stackaddr = ((stackaddr as usize) + (PAGE_SIZE as usize) - remainder)
115                 as *mut libc::c_void;
116         }
117
118         // Rellocate the last page of the stack.
119         // This ensures SIGBUS will be raised on
120         // stack overflow.
121         let result = mmap(stackaddr,
122                           PAGE_SIZE as libc::size_t,
123                           PROT_NONE,
124                           MAP_PRIVATE | MAP_ANON | MAP_FIXED,
125                           -1,
126                           0);
127
128         if result != stackaddr || result == MAP_FAILED {
129             panic!("failed to allocate a guard page");
130         }
131
132         let offset = if cfg!(target_os = "linux") {
133             2
134         } else {
135             1
136         };
137
138         GUARD_PAGE = stackaddr as uint + offset * PAGE_SIZE;
139     }
140
141     pub unsafe fn main() -> uint {
142         GUARD_PAGE
143     }
144
145     #[cfg(target_os = "macos")]
146     pub unsafe fn current() -> uint {
147         (pthread_get_stackaddr_np(pthread_self()) as libc::size_t -
148          pthread_get_stacksize_np(pthread_self())) as uint
149     }
150
151     #[cfg(target_os = "openbsd")]
152     pub unsafe fn current() -> uint {
153         let mut current_stack: stack_t = mem::zeroed();
154         if pthread_stackseg_np(pthread_self(), &mut current_stack) != 0 {
155             panic!("failed to get current stack: pthread_stackseg_np")
156         }
157
158         if pthread_main_np() == 1 {
159             // main thread
160             current_stack.ss_sp as uint - current_stack.ss_size as uint + 3 * PAGE_SIZE as uint
161
162         } else {
163             // new thread
164             current_stack.ss_sp as uint - current_stack.ss_size as uint
165         }
166     }
167
168     #[cfg(any(target_os = "linux", target_os = "android"))]
169     pub unsafe fn current() -> uint {
170         let mut attr: libc::pthread_attr_t = mem::zeroed();
171         if pthread_getattr_np(pthread_self(), &mut attr) != 0 {
172             panic!("failed to get thread attributes");
173         }
174         let mut guardsize = 0;
175         if pthread_attr_getguardsize(&attr, &mut guardsize) != 0 {
176             panic!("failed to get stack guard page");
177         }
178         if guardsize == 0 {
179             panic!("there is no guard page");
180         }
181         let mut stackaddr = ptr::null_mut();
182         let mut stacksize = 0;
183         if pthread_attr_getstack(&attr, &mut stackaddr, &mut stacksize) != 0 {
184             panic!("failed to get stack information");
185         }
186         if pthread_attr_destroy(&mut attr) != 0 {
187             panic!("failed to destroy thread attributes");
188         }
189
190         stackaddr as uint + guardsize as uint
191     }
192 }
193
194 pub unsafe fn create(stack: uint, p: Thunk) -> rust_thread {
195     let mut native: libc::pthread_t = mem::zeroed();
196     let mut attr: libc::pthread_attr_t = mem::zeroed();
197     assert_eq!(pthread_attr_init(&mut attr), 0);
198     assert_eq!(pthread_attr_setdetachstate(&mut attr,
199                                            PTHREAD_CREATE_JOINABLE), 0);
200
201     // Reserve room for the red zone, the runtime's stack of last resort.
202     let stack_size = cmp::max(stack, RED_ZONE + min_stack_size(&attr) as uint);
203     match pthread_attr_setstacksize(&mut attr, stack_size as libc::size_t) {
204         0 => {
205         },
206         libc::EINVAL => {
207             // EINVAL means |stack_size| is either too small or not a
208             // multiple of the system page size.  Because it's definitely
209             // >= PTHREAD_STACK_MIN, it must be an alignment issue.
210             // Round up to the nearest page and try again.
211             let page_size = libc::sysconf(libc::_SC_PAGESIZE) as uint;
212             let stack_size = (stack_size + page_size - 1) &
213                              (-(page_size as int - 1) as uint - 1);
214             assert_eq!(pthread_attr_setstacksize(&mut attr, stack_size as libc::size_t), 0);
215         },
216         errno => {
217             // This cannot really happen.
218             panic!("pthread_attr_setstacksize() error: {}", errno);
219         },
220     };
221
222     let arg: *mut libc::c_void = mem::transmute(box p); // must box since sizeof(p)=2*uint
223     let ret = pthread_create(&mut native, &attr, thread_start, arg);
224     assert_eq!(pthread_attr_destroy(&mut attr), 0);
225
226     if ret != 0 {
227         // be sure to not leak the closure
228         let _p: Box<Box<FnOnce()+Send>> = mem::transmute(arg);
229         panic!("failed to spawn native thread: {}", ret);
230     }
231     native
232 }
233
234 #[cfg(any(target_os = "linux", target_os = "android"))]
235 pub unsafe fn set_name(name: &str) {
236     // pthread_setname_np() since glibc 2.12
237     // availability autodetected via weak linkage
238     let cname = CString::from_slice(name.as_bytes());
239     type F = unsafe extern "C" fn(libc::pthread_t, *const libc::c_char) -> libc::c_int;
240     extern {
241         #[linkage = "extern_weak"]
242         static pthread_setname_np: *const ();
243     }
244     if !pthread_setname_np.is_null() {
245         unsafe {
246             mem::transmute::<*const (), F>(pthread_setname_np)(pthread_self(), cname.as_ptr());
247         }
248     }
249 }
250
251 #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
252 pub unsafe fn set_name(name: &str) {
253     // pthread_set_name_np() since almost forever on all BSDs
254     let cname = CString::from_slice(name.as_bytes());
255     pthread_set_name_np(pthread_self(), cname.as_ptr());
256 }
257
258 #[cfg(any(target_os = "macos", target_os = "ios"))]
259 pub unsafe fn set_name(name: &str) {
260     // pthread_setname_np() since OS X 10.6 and iOS 3.2
261     let cname = CString::from_slice(name.as_bytes());
262     pthread_setname_np(cname.as_ptr());
263 }
264
265 pub unsafe fn join(native: rust_thread) {
266     assert_eq!(pthread_join(native, ptr::null_mut()), 0);
267 }
268
269 pub unsafe fn detach(native: rust_thread) {
270     assert_eq!(pthread_detach(native), 0);
271 }
272
273 pub unsafe fn yield_now() { assert_eq!(sched_yield(), 0); }
274 // glibc >= 2.15 has a __pthread_get_minstack() function that returns
275 // PTHREAD_STACK_MIN plus however many bytes are needed for thread-local
276 // storage.  We need that information to avoid blowing up when a small stack
277 // is created in an application with big thread-local storage requirements.
278 // See #6233 for rationale and details.
279 //
280 // Link weakly to the symbol for compatibility with older versions of glibc.
281 // Assumes that we've been dynamically linked to libpthread but that is
282 // currently always the case.  Note that you need to check that the symbol
283 // is non-null before calling it!
284 #[cfg(target_os = "linux")]
285 fn min_stack_size(attr: *const libc::pthread_attr_t) -> libc::size_t {
286     type F = unsafe extern "C" fn(*const libc::pthread_attr_t) -> libc::size_t;
287     extern {
288         #[linkage = "extern_weak"]
289         static __pthread_get_minstack: *const ();
290     }
291     if __pthread_get_minstack.is_null() {
292         PTHREAD_STACK_MIN
293     } else {
294         unsafe { mem::transmute::<*const (), F>(__pthread_get_minstack)(attr) }
295     }
296 }
297
298 // __pthread_get_minstack() is marked as weak but extern_weak linkage is
299 // not supported on OS X, hence this kludge...
300 #[cfg(not(target_os = "linux"))]
301 fn min_stack_size(_: *const libc::pthread_attr_t) -> libc::size_t {
302     PTHREAD_STACK_MIN
303 }
304
305 #[cfg(any(target_os = "linux", target_os = "android"))]
306 extern {
307     pub fn pthread_self() -> libc::pthread_t;
308     pub fn pthread_getattr_np(native: libc::pthread_t,
309                               attr: *mut libc::pthread_attr_t) -> libc::c_int;
310     pub fn pthread_attr_getguardsize(attr: *const libc::pthread_attr_t,
311                                      guardsize: *mut libc::size_t) -> libc::c_int;
312     pub fn pthread_attr_getstack(attr: *const libc::pthread_attr_t,
313                                  stackaddr: *mut *mut libc::c_void,
314                                  stacksize: *mut libc::size_t) -> libc::c_int;
315 }
316
317 #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
318 extern {
319     pub fn pthread_self() -> libc::pthread_t;
320     fn pthread_set_name_np(tid: libc::pthread_t, name: *const libc::c_char);
321 }
322
323 #[cfg(any(target_os = "macos", target_os = "ios"))]
324 extern {
325     pub fn pthread_self() -> libc::pthread_t;
326     pub fn pthread_get_stackaddr_np(thread: libc::pthread_t) -> *mut libc::c_void;
327     pub fn pthread_get_stacksize_np(thread: libc::pthread_t) -> libc::size_t;
328     fn pthread_setname_np(name: *const libc::c_char) -> libc::c_int;
329 }
330
331 #[cfg(target_os = "openbsd")]
332 extern {
333         pub fn pthread_self() -> libc::pthread_t;
334         pub fn pthread_stackseg_np(thread: libc::pthread_t,
335                                    sinfo: *mut stack_t) -> libc::c_uint;
336         pub fn pthread_main_np() -> libc::c_uint;
337 }
338
339 #[cfg(target_os = "openbsd")]
340 #[repr(C)]
341 pub struct stack_t {
342     pub ss_sp: *mut libc::c_void,
343     pub ss_size: libc::size_t,
344     pub ss_flags: libc::c_int,
345 }
346
347 extern {
348     fn pthread_create(native: *mut libc::pthread_t,
349                       attr: *const libc::pthread_attr_t,
350                       f: StartFn,
351                       value: *mut libc::c_void) -> libc::c_int;
352     fn pthread_join(native: libc::pthread_t,
353                     value: *mut *mut libc::c_void) -> libc::c_int;
354     fn pthread_attr_init(attr: *mut libc::pthread_attr_t) -> libc::c_int;
355     pub fn pthread_attr_destroy(attr: *mut libc::pthread_attr_t) -> libc::c_int;
356     fn pthread_attr_setstacksize(attr: *mut libc::pthread_attr_t,
357                                  stack_size: libc::size_t) -> libc::c_int;
358     fn pthread_attr_setdetachstate(attr: *mut libc::pthread_attr_t,
359                                    state: libc::c_int) -> libc::c_int;
360     fn pthread_detach(thread: libc::pthread_t) -> libc::c_int;
361     fn sched_yield() -> libc::c_int;
362 }