]> git.lizzy.rs Git - rust.git/blob - src/libstd/sys/unix/thread.rs
Rollup merge of #35616 - clementmiao:E0067_new_error_format, r=jonathandturner
[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 prelude::v1::*;
12
13 use alloc::boxed::FnBox;
14 use cmp;
15 use ffi::CStr;
16 use io;
17 use libc;
18 use mem;
19 use ptr;
20 use sys::os;
21 use time::Duration;
22
23 use sys_common::thread::*;
24
25 pub struct Thread {
26     id: libc::pthread_t,
27 }
28
29 // Some platforms may have pthread_t as a pointer in which case we still want
30 // a thread to be Send/Sync
31 unsafe impl Send for Thread {}
32 unsafe impl Sync for Thread {}
33
34 impl Thread {
35     pub unsafe fn new<'a>(stack: usize, p: Box<FnBox() + 'a>)
36                           -> io::Result<Thread> {
37         let p = box p;
38         let mut native: libc::pthread_t = mem::zeroed();
39         let mut attr: libc::pthread_attr_t = mem::zeroed();
40         assert_eq!(libc::pthread_attr_init(&mut attr), 0);
41
42         let stack_size = cmp::max(stack, min_stack_size(&attr));
43         match libc::pthread_attr_setstacksize(&mut attr,
44                                               stack_size as libc::size_t) {
45             0 => {}
46             n => {
47                 assert_eq!(n, libc::EINVAL);
48                 // EINVAL means |stack_size| is either too small or not a
49                 // multiple of the system page size.  Because it's definitely
50                 // >= PTHREAD_STACK_MIN, it must be an alignment issue.
51                 // Round up to the nearest page and try again.
52                 let page_size = os::page_size();
53                 let stack_size = (stack_size + page_size - 1) &
54                                  (-(page_size as isize - 1) as usize - 1);
55                 let stack_size = stack_size as libc::size_t;
56                 assert_eq!(libc::pthread_attr_setstacksize(&mut attr,
57                                                            stack_size), 0);
58             }
59         };
60
61         let ret = libc::pthread_create(&mut native, &attr, thread_start,
62                                        &*p as *const _ as *mut _);
63         assert_eq!(libc::pthread_attr_destroy(&mut attr), 0);
64
65         return if ret != 0 {
66             Err(io::Error::from_raw_os_error(ret))
67         } else {
68             mem::forget(p); // ownership passed to pthread_create
69             Ok(Thread { id: native })
70         };
71
72         extern fn thread_start(main: *mut libc::c_void) -> *mut libc::c_void {
73             unsafe { start_thread(main); }
74             ptr::null_mut()
75         }
76     }
77
78     pub fn yield_now() {
79         let ret = unsafe { libc::sched_yield() };
80         debug_assert_eq!(ret, 0);
81     }
82
83     #[cfg(any(target_os = "linux",
84               target_os = "android"))]
85     pub fn set_name(name: &CStr) {
86         const PR_SET_NAME: libc::c_int = 15;
87         // pthread wrapper only appeared in glibc 2.12, so we use syscall
88         // directly.
89         unsafe {
90             libc::prctl(PR_SET_NAME, name.as_ptr() as libc::c_ulong, 0, 0, 0);
91         }
92     }
93
94     #[cfg(any(target_os = "freebsd",
95               target_os = "dragonfly",
96               target_os = "bitrig",
97               target_os = "openbsd"))]
98     pub fn set_name(name: &CStr) {
99         unsafe {
100             libc::pthread_set_name_np(libc::pthread_self(), name.as_ptr());
101         }
102     }
103
104     #[cfg(any(target_os = "macos", target_os = "ios"))]
105     pub fn set_name(name: &CStr) {
106         unsafe {
107             libc::pthread_setname_np(name.as_ptr());
108         }
109     }
110
111     #[cfg(target_os = "netbsd")]
112     pub fn set_name(name: &CStr) {
113         use ffi::CString;
114         let cname = CString::new(&b"%s"[..]).unwrap();
115         unsafe {
116             libc::pthread_setname_np(libc::pthread_self(), cname.as_ptr(),
117                                      name.as_ptr() as *mut libc::c_void);
118         }
119     }
120     #[cfg(any(target_env = "newlib", target_os = "solaris", target_os = "emscripten"))]
121     pub fn set_name(_name: &CStr) {
122         // Newlib, Illumos and Emscripten have no way to set a thread name.
123     }
124
125     pub fn sleep(dur: Duration) {
126         let mut secs = dur.as_secs();
127         let mut nsecs = dur.subsec_nanos() as libc::c_long;
128
129         // If we're awoken with a signal then the return value will be -1 and
130         // nanosleep will fill in `ts` with the remaining time.
131         unsafe {
132             while secs > 0 || nsecs > 0 {
133                 let mut ts = libc::timespec {
134                     tv_sec: cmp::min(libc::time_t::max_value() as u64, secs) as libc::time_t,
135                     tv_nsec: nsecs,
136                 };
137                 secs -= ts.tv_sec as u64;
138                 if libc::nanosleep(&ts, &mut ts) == -1 {
139                     assert_eq!(os::errno(), libc::EINTR);
140                     secs += ts.tv_sec as u64;
141                     nsecs = ts.tv_nsec;
142                 } else {
143                     nsecs = 0;
144                 }
145             }
146         }
147     }
148
149     pub fn join(self) {
150         unsafe {
151             let ret = libc::pthread_join(self.id, ptr::null_mut());
152             mem::forget(self);
153             debug_assert_eq!(ret, 0);
154         }
155     }
156
157     pub fn id(&self) -> libc::pthread_t { self.id }
158
159     pub fn into_id(self) -> libc::pthread_t {
160         let id = self.id;
161         mem::forget(self);
162         id
163     }
164 }
165
166 impl Drop for Thread {
167     fn drop(&mut self) {
168         let ret = unsafe { libc::pthread_detach(self.id) };
169         debug_assert_eq!(ret, 0);
170     }
171 }
172
173 #[cfg(all(not(all(target_os = "linux", not(target_env = "musl"))),
174           not(target_os = "freebsd"),
175           not(target_os = "macos"),
176           not(target_os = "bitrig"),
177           not(all(target_os = "netbsd", not(target_vendor = "rumprun"))),
178           not(target_os = "openbsd"),
179           not(target_os = "solaris")))]
180 #[cfg_attr(test, allow(dead_code))]
181 pub mod guard {
182     pub unsafe fn current() -> Option<usize> { None }
183     pub unsafe fn init() -> Option<usize> { None }
184 }
185
186
187 #[cfg(any(all(target_os = "linux", not(target_env = "musl")),
188           target_os = "freebsd",
189           target_os = "macos",
190           target_os = "bitrig",
191           all(target_os = "netbsd", not(target_vendor = "rumprun")),
192           target_os = "openbsd",
193           target_os = "solaris"))]
194 #[cfg_attr(test, allow(dead_code))]
195 pub mod guard {
196     use prelude::v1::*;
197
198     use libc;
199     use libc::mmap;
200     use libc::{PROT_NONE, MAP_PRIVATE, MAP_ANON, MAP_FAILED, MAP_FIXED};
201     use sys::os;
202
203     #[cfg(any(target_os = "macos",
204               target_os = "bitrig",
205               target_os = "openbsd",
206               target_os = "solaris"))]
207     unsafe fn get_stack_start() -> Option<*mut libc::c_void> {
208         current().map(|s| s as *mut libc::c_void)
209     }
210
211     #[cfg(any(target_os = "android", target_os = "freebsd",
212               target_os = "linux", target_os = "netbsd"))]
213     unsafe fn get_stack_start() -> Option<*mut libc::c_void> {
214         let mut ret = None;
215         let mut attr: libc::pthread_attr_t = ::mem::zeroed();
216         assert_eq!(libc::pthread_attr_init(&mut attr), 0);
217         #[cfg(target_os = "freebsd")]
218             let e = libc::pthread_attr_get_np(libc::pthread_self(), &mut attr);
219         #[cfg(not(target_os = "freebsd"))]
220             let e = libc::pthread_getattr_np(libc::pthread_self(), &mut attr);
221         if e == 0 {
222             let mut stackaddr = ::ptr::null_mut();
223             let mut stacksize = 0;
224             assert_eq!(libc::pthread_attr_getstack(&attr, &mut stackaddr,
225                                                    &mut stacksize), 0);
226             ret = Some(stackaddr);
227         }
228         assert_eq!(libc::pthread_attr_destroy(&mut attr), 0);
229         ret
230     }
231
232     pub unsafe fn init() -> Option<usize> {
233         let psize = os::page_size();
234         let mut stackaddr = match get_stack_start() {
235             Some(addr) => addr,
236             None => return None,
237         };
238
239         // Ensure stackaddr is page aligned! A parent process might
240         // have reset RLIMIT_STACK to be non-page aligned. The
241         // pthread_attr_getstack() reports the usable stack area
242         // stackaddr < stackaddr + stacksize, so if stackaddr is not
243         // page-aligned, calculate the fix such that stackaddr <
244         // new_page_aligned_stackaddr < stackaddr + stacksize
245         let remainder = (stackaddr as usize) % psize;
246         if remainder != 0 {
247             stackaddr = ((stackaddr as usize) + psize - remainder)
248                 as *mut libc::c_void;
249         }
250
251         // Rellocate the last page of the stack.
252         // This ensures SIGBUS will be raised on
253         // stack overflow.
254         let result = mmap(stackaddr,
255                           psize as libc::size_t,
256                           PROT_NONE,
257                           MAP_PRIVATE | MAP_ANON | MAP_FIXED,
258                           -1,
259                           0);
260
261         if result != stackaddr || result == MAP_FAILED {
262             panic!("failed to allocate a guard page");
263         }
264
265         let offset = if cfg!(any(target_os = "linux", target_os = "freebsd")) {
266             2
267         } else {
268             1
269         };
270
271         Some(stackaddr as usize + offset * psize)
272     }
273
274     #[cfg(target_os = "solaris")]
275     pub unsafe fn current() -> Option<usize> {
276         let mut current_stack: libc::stack_t = ::mem::zeroed();
277         assert_eq!(libc::stack_getbounds(&mut current_stack), 0);
278         Some(current_stack.ss_sp as usize)
279     }
280
281     #[cfg(target_os = "macos")]
282     pub unsafe fn current() -> Option<usize> {
283         Some((libc::pthread_get_stackaddr_np(libc::pthread_self()) as libc::size_t -
284               libc::pthread_get_stacksize_np(libc::pthread_self())) as usize)
285     }
286
287     #[cfg(any(target_os = "openbsd", target_os = "bitrig"))]
288     pub unsafe fn current() -> Option<usize> {
289         let mut current_stack: libc::stack_t = ::mem::zeroed();
290         assert_eq!(libc::pthread_stackseg_np(libc::pthread_self(),
291                                              &mut current_stack), 0);
292
293         let extra = if cfg!(target_os = "bitrig") {3} else {1} * os::page_size();
294         Some(if libc::pthread_main_np() == 1 {
295             // main thread
296             current_stack.ss_sp as usize - current_stack.ss_size as usize + extra
297         } else {
298             // new thread
299             current_stack.ss_sp as usize - current_stack.ss_size as usize
300         })
301     }
302
303     #[cfg(any(target_os = "android", target_os = "freebsd",
304               target_os = "linux", target_os = "netbsd"))]
305     pub unsafe fn current() -> Option<usize> {
306         let mut ret = None;
307         let mut attr: libc::pthread_attr_t = ::mem::zeroed();
308         assert_eq!(libc::pthread_attr_init(&mut attr), 0);
309         #[cfg(target_os = "freebsd")]
310             let e = libc::pthread_attr_get_np(libc::pthread_self(), &mut attr);
311         #[cfg(not(target_os = "freebsd"))]
312             let e = libc::pthread_getattr_np(libc::pthread_self(), &mut attr);
313         if e == 0 {
314             let mut guardsize = 0;
315             assert_eq!(libc::pthread_attr_getguardsize(&attr, &mut guardsize), 0);
316             if guardsize == 0 {
317                 panic!("there is no guard page");
318             }
319             let mut stackaddr = ::ptr::null_mut();
320             let mut size = 0;
321             assert_eq!(libc::pthread_attr_getstack(&attr, &mut stackaddr,
322                                                    &mut size), 0);
323
324             ret = if cfg!(target_os = "freebsd") {
325                 Some(stackaddr as usize - guardsize as usize)
326             } else if cfg!(target_os = "netbsd") {
327                 Some(stackaddr as usize)
328             } else {
329                 Some(stackaddr as usize + guardsize as usize)
330             };
331         }
332         assert_eq!(libc::pthread_attr_destroy(&mut attr), 0);
333         ret
334     }
335 }
336
337 // glibc >= 2.15 has a __pthread_get_minstack() function that returns
338 // PTHREAD_STACK_MIN plus however many bytes are needed for thread-local
339 // storage.  We need that information to avoid blowing up when a small stack
340 // is created in an application with big thread-local storage requirements.
341 // See #6233 for rationale and details.
342 #[cfg(target_os = "linux")]
343 #[allow(deprecated)]
344 fn min_stack_size(attr: *const libc::pthread_attr_t) -> usize {
345     weak!(fn __pthread_get_minstack(*const libc::pthread_attr_t) -> libc::size_t);
346
347     match __pthread_get_minstack.get() {
348         None => libc::PTHREAD_STACK_MIN as usize,
349         Some(f) => unsafe { f(attr) as usize },
350     }
351 }
352
353 // No point in looking up __pthread_get_minstack() on non-glibc
354 // platforms.
355 #[cfg(all(not(target_os = "linux"),
356           not(target_os = "netbsd")))]
357 fn min_stack_size(_: *const libc::pthread_attr_t) -> usize {
358     libc::PTHREAD_STACK_MIN as usize
359 }
360
361 #[cfg(target_os = "netbsd")]
362 fn min_stack_size(_: *const libc::pthread_attr_t) -> usize {
363     2048 // just a guess
364 }