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