]> git.lizzy.rs Git - rust.git/blob - src/librustrt/thread.rs
return &mut T from the arenas, not &T
[rust.git] / src / librustrt / thread.rs
1 // Copyright 2013-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 //! Native os-thread management
12 //!
13 //! This modules contains bindings necessary for managing OS-level threads.
14 //! These functions operate outside of the rust runtime, creating threads
15 //! which are not used for scheduling in any way.
16
17 #![allow(non_camel_case_types)]
18
19 use core::prelude::*;
20
21 use alloc::boxed::Box;
22 use core::mem;
23 use core::uint;
24 use libc;
25
26 use stack;
27 use stack_overflow;
28
29 pub unsafe fn init() {
30     imp::guard::init();
31     stack_overflow::init();
32 }
33
34 pub unsafe fn cleanup() {
35     stack_overflow::cleanup();
36 }
37
38 #[cfg(target_os = "windows")]
39 type StartFn = extern "system" fn(*mut libc::c_void) -> imp::rust_thread_return;
40
41 #[cfg(not(target_os = "windows"))]
42 type StartFn = extern "C" fn(*mut libc::c_void) -> imp::rust_thread_return;
43
44 /// This struct represents a native thread's state. This is used to join on an
45 /// existing thread created in the join-able state.
46 pub struct Thread<T> {
47     native: imp::rust_thread,
48     joined: bool,
49     packet: Box<Option<T>>,
50 }
51
52 static DEFAULT_STACK_SIZE: uint = 1024 * 1024;
53
54 // This is the starting point of rust os threads. The first thing we do
55 // is make sure that we don't trigger __morestack (also why this has a
56 // no_stack_check annotation), and then we extract the main function
57 // and invoke it.
58 #[no_stack_check]
59 fn start_thread(main: *mut libc::c_void) -> imp::rust_thread_return {
60     unsafe {
61         stack::record_os_managed_stack_bounds(0, uint::MAX);
62         let handler = stack_overflow::Handler::new();
63         let f: Box<proc()> = mem::transmute(main);
64         (*f)();
65         drop(handler);
66         mem::transmute(0 as imp::rust_thread_return)
67     }
68 }
69
70 #[no_stack_check]
71 #[cfg(target_os = "windows")]
72 extern "system" fn thread_start(main: *mut libc::c_void) -> imp::rust_thread_return {
73     return start_thread(main);
74 }
75
76 #[no_stack_check]
77 #[cfg(not(target_os = "windows"))]
78 extern fn thread_start(main: *mut libc::c_void) -> imp::rust_thread_return {
79     return start_thread(main);
80 }
81
82 /// Returns the last writable byte of the main thread's stack next to the guard
83 /// page. Must be called from the main thread.
84 pub fn main_guard_page() -> uint {
85     unsafe {
86         imp::guard::main()
87     }
88 }
89
90 /// Returns the last writable byte of the current thread's stack next to the
91 /// guard page. Must not be called from the main thread.
92 pub fn current_guard_page() -> uint {
93     unsafe {
94         imp::guard::current()
95     }
96 }
97
98 // There are two impl blocks b/c if T were specified at the top then it's just a
99 // pain to specify a type parameter on Thread::spawn (which doesn't need the
100 // type parameter).
101 impl Thread<()> {
102
103     /// Starts execution of a new OS thread.
104     ///
105     /// This function will not wait for the thread to join, but a handle to the
106     /// thread will be returned.
107     ///
108     /// Note that the handle returned is used to acquire the return value of the
109     /// procedure `main`. The `join` function will wait for the thread to finish
110     /// and return the value that `main` generated.
111     ///
112     /// Also note that the `Thread` returned will *always* wait for the thread
113     /// to finish executing. This means that even if `join` is not explicitly
114     /// called, when the `Thread` falls out of scope its destructor will block
115     /// waiting for the OS thread.
116     pub fn start<T: Send>(main: proc():Send -> T) -> Thread<T> {
117         Thread::start_stack(DEFAULT_STACK_SIZE, main)
118     }
119
120     /// Performs the same functionality as `start`, but specifies an explicit
121     /// stack size for the new thread.
122     pub fn start_stack<T: Send>(stack: uint, main: proc():Send -> T) -> Thread<T> {
123
124         // We need the address of the packet to fill in to be stable so when
125         // `main` fills it in it's still valid, so allocate an extra box to do
126         // so.
127         let packet = box None;
128         let packet2: *mut Option<T> = unsafe {
129             *mem::transmute::<&Box<Option<T>>, *const *mut Option<T>>(&packet)
130         };
131         let main = proc() unsafe { *packet2 = Some(main()); };
132         let native = unsafe { imp::create(stack, box main) };
133
134         Thread {
135             native: native,
136             joined: false,
137             packet: packet,
138         }
139     }
140
141     /// This will spawn a new thread, but it will not wait for the thread to
142     /// finish, nor is it possible to wait for the thread to finish.
143     ///
144     /// This corresponds to creating threads in the 'detached' state on unix
145     /// systems. Note that platforms may not keep the main program alive even if
146     /// there are detached thread still running around.
147     pub fn spawn(main: proc():Send) {
148         Thread::spawn_stack(DEFAULT_STACK_SIZE, main)
149     }
150
151     /// Performs the same functionality as `spawn`, but explicitly specifies a
152     /// stack size for the new thread.
153     pub fn spawn_stack(stack: uint, main: proc():Send) {
154         unsafe {
155             let handle = imp::create(stack, box main);
156             imp::detach(handle);
157         }
158     }
159
160     /// Relinquishes the CPU slot that this OS-thread is currently using,
161     /// allowing another thread to run for awhile.
162     pub fn yield_now() {
163         unsafe { imp::yield_now(); }
164     }
165 }
166
167 impl<T: Send> Thread<T> {
168     /// Wait for this thread to finish, returning the result of the thread's
169     /// calculation.
170     pub fn join(mut self) -> T {
171         assert!(!self.joined);
172         unsafe { imp::join(self.native) };
173         self.joined = true;
174         assert!(self.packet.is_some());
175         self.packet.take().unwrap()
176     }
177 }
178
179 #[unsafe_destructor]
180 impl<T: Send> Drop for Thread<T> {
181     fn drop(&mut self) {
182         // This is required for correctness. If this is not done then the thread
183         // would fill in a return box which no longer exists.
184         if !self.joined {
185             unsafe { imp::join(self.native) };
186         }
187     }
188 }
189
190 #[cfg(windows)]
191 #[allow(non_snake_case)]
192 mod imp {
193     use core::prelude::*;
194
195     use alloc::boxed::Box;
196     use core::cmp;
197     use core::mem;
198     use core::ptr;
199     use libc;
200     use libc::types::os::arch::extra::{LPSECURITY_ATTRIBUTES, SIZE_T, BOOL,
201                                        LPVOID, DWORD, LPDWORD, HANDLE};
202     use stack::RED_ZONE;
203
204     pub type rust_thread = HANDLE;
205     pub type rust_thread_return = DWORD;
206
207     pub mod guard {
208         pub unsafe fn main() -> uint {
209             0
210         }
211
212         pub unsafe fn current() -> uint {
213             0
214         }
215
216         pub unsafe fn init() {
217         }
218     }
219
220     pub unsafe fn create(stack: uint, p: Box<proc():Send>) -> rust_thread {
221         let arg: *mut libc::c_void = mem::transmute(p);
222         // FIXME On UNIX, we guard against stack sizes that are too small but
223         // that's because pthreads enforces that stacks are at least
224         // PTHREAD_STACK_MIN bytes big.  Windows has no such lower limit, it's
225         // just that below a certain threshold you can't do anything useful.
226         // That threshold is application and architecture-specific, however.
227         // For now, the only requirement is that it's big enough to hold the
228         // red zone.  Round up to the next 64 kB because that's what the NT
229         // kernel does, might as well make it explicit.  With the current
230         // 20 kB red zone, that makes for a 64 kB minimum stack.
231         let stack_size = (cmp::max(stack, RED_ZONE) + 0xfffe) & (-0xfffe - 1);
232         let ret = CreateThread(ptr::null_mut(), stack_size as libc::size_t,
233                                super::thread_start, arg, 0, ptr::null_mut());
234
235         if ret as uint == 0 {
236             // be sure to not leak the closure
237             let _p: Box<proc():Send> = mem::transmute(arg);
238             fail!("failed to spawn native thread: {}", ret);
239         }
240         return ret;
241     }
242
243     pub unsafe fn join(native: rust_thread) {
244         use libc::consts::os::extra::INFINITE;
245         WaitForSingleObject(native, INFINITE);
246     }
247
248     pub unsafe fn detach(native: rust_thread) {
249         assert!(libc::CloseHandle(native) != 0);
250     }
251
252     pub unsafe fn yield_now() {
253         // This function will return 0 if there are no other threads to execute,
254         // but this also means that the yield was useless so this isn't really a
255         // case that needs to be worried about.
256         SwitchToThread();
257     }
258
259     #[allow(non_snake_case)]
260     extern "system" {
261         fn CreateThread(lpThreadAttributes: LPSECURITY_ATTRIBUTES,
262                         dwStackSize: SIZE_T,
263                         lpStartAddress: super::StartFn,
264                         lpParameter: LPVOID,
265                         dwCreationFlags: DWORD,
266                         lpThreadId: LPDWORD) -> HANDLE;
267         fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) -> DWORD;
268         fn SwitchToThread() -> BOOL;
269     }
270 }
271
272 #[cfg(unix)]
273 mod imp {
274     use core::prelude::*;
275
276     use alloc::boxed::Box;
277     use core::cmp;
278     use core::mem;
279     use core::ptr;
280     use libc::consts::os::posix01::{PTHREAD_CREATE_JOINABLE, PTHREAD_STACK_MIN};
281     use libc;
282
283     use stack::RED_ZONE;
284
285     pub type rust_thread = libc::pthread_t;
286     pub type rust_thread_return = *mut u8;
287
288     #[cfg(all(not(target_os = "linux"), not(target_os = "macos")))]
289     pub mod guard {
290         pub unsafe fn current() -> uint {
291             0
292         }
293
294         pub unsafe fn main() -> uint {
295             0
296         }
297
298         pub unsafe fn init() {
299         }
300     }
301
302     #[cfg(any(target_os = "linux", target_os = "macos"))]
303     pub mod guard {
304         use super::*;
305         #[cfg(any(target_os = "linux", target_os = "android"))]
306         use core::mem;
307         #[cfg(any(target_os = "linux", target_os = "android"))]
308         use core::ptr;
309         use libc;
310         use libc::funcs::posix88::mman::{mmap};
311         use libc::consts::os::posix88::{PROT_NONE,
312                                         MAP_PRIVATE,
313                                         MAP_ANON,
314                                         MAP_FAILED,
315                                         MAP_FIXED};
316
317         // These are initialized in init() and only read from after
318         static mut PAGE_SIZE: uint = 0;
319         static mut GUARD_PAGE: uint = 0;
320
321         #[cfg(target_os = "macos")]
322         unsafe fn get_stack_start() -> *mut libc::c_void {
323             current() as *mut libc::c_void
324         }
325
326         #[cfg(any(target_os = "linux", target_os = "android"))]
327         unsafe fn get_stack_start() -> *mut libc::c_void {
328             let mut attr: libc::pthread_attr_t = mem::zeroed();
329             if pthread_getattr_np(pthread_self(), &mut attr) != 0 {
330                 fail!("failed to get thread attributes");
331             }
332             let mut stackaddr = ptr::null_mut();
333             let mut stacksize = 0;
334             if pthread_attr_getstack(&attr, &mut stackaddr, &mut stacksize) != 0 {
335                 fail!("failed to get stack information");
336             }
337             if pthread_attr_destroy(&mut attr) != 0 {
338                 fail!("failed to destroy thread attributes");
339             }
340             stackaddr
341         }
342
343         pub unsafe fn init() {
344             let psize = libc::sysconf(libc::consts::os::sysconf::_SC_PAGESIZE);
345             if psize == -1 {
346                 fail!("failed to get page size");
347             }
348
349             PAGE_SIZE = psize as uint;
350
351             let stackaddr = get_stack_start();
352
353             // Rellocate the last page of the stack.
354             // This ensures SIGBUS will be raised on
355             // stack overflow.
356             let result = mmap(stackaddr,
357                               PAGE_SIZE as libc::size_t,
358                               PROT_NONE,
359                               MAP_PRIVATE | MAP_ANON | MAP_FIXED,
360                               -1,
361                               0);
362
363             if result != stackaddr || result == MAP_FAILED {
364                 fail!("failed to allocate a guard page");
365             }
366
367             let offset = if cfg!(target_os = "linux") {
368                 2
369             } else {
370                 1
371             };
372
373             GUARD_PAGE = stackaddr as uint + offset * PAGE_SIZE;
374         }
375
376         pub unsafe fn main() -> uint {
377             GUARD_PAGE
378         }
379
380         #[cfg(target_os = "macos")]
381         pub unsafe fn current() -> uint {
382             (pthread_get_stackaddr_np(pthread_self()) as libc::size_t -
383              pthread_get_stacksize_np(pthread_self())) as uint
384         }
385
386         #[cfg(any(target_os = "linux", target_os = "android"))]
387         pub unsafe fn current() -> uint {
388             let mut attr: libc::pthread_attr_t = mem::zeroed();
389             if pthread_getattr_np(pthread_self(), &mut attr) != 0 {
390                 fail!("failed to get thread attributes");
391             }
392             let mut guardsize = 0;
393             if pthread_attr_getguardsize(&attr, &mut guardsize) != 0 {
394                 fail!("failed to get stack guard page");
395             }
396             if guardsize == 0 {
397                 fail!("there is no guard page");
398             }
399             let mut stackaddr = ptr::null_mut();
400             let mut stacksize = 0;
401             if pthread_attr_getstack(&attr, &mut stackaddr, &mut stacksize) != 0 {
402                 fail!("failed to get stack information");
403             }
404             if pthread_attr_destroy(&mut attr) != 0 {
405                 fail!("failed to destroy thread attributes");
406             }
407
408             stackaddr as uint + guardsize as uint
409         }
410     }
411
412     pub unsafe fn create(stack: uint, p: Box<proc():Send>) -> rust_thread {
413         let mut native: libc::pthread_t = mem::zeroed();
414         let mut attr: libc::pthread_attr_t = mem::zeroed();
415         assert_eq!(pthread_attr_init(&mut attr), 0);
416         assert_eq!(pthread_attr_setdetachstate(&mut attr,
417                                                PTHREAD_CREATE_JOINABLE), 0);
418
419         // Reserve room for the red zone, the runtime's stack of last resort.
420         let stack_size = cmp::max(stack, RED_ZONE + min_stack_size(&attr) as uint);
421         match pthread_attr_setstacksize(&mut attr, stack_size as libc::size_t) {
422             0 => {
423             },
424             libc::EINVAL => {
425                 // EINVAL means |stack_size| is either too small or not a
426                 // multiple of the system page size.  Because it's definitely
427                 // >= PTHREAD_STACK_MIN, it must be an alignment issue.
428                 // Round up to the nearest page and try again.
429                 let page_size = libc::sysconf(libc::_SC_PAGESIZE) as uint;
430                 let stack_size = (stack_size + page_size - 1) &
431                                  (-(page_size as int - 1) as uint - 1);
432                 assert_eq!(pthread_attr_setstacksize(&mut attr, stack_size as libc::size_t), 0);
433             },
434             errno => {
435                 // This cannot really happen.
436                 fail!("pthread_attr_setstacksize() error: {}", errno);
437             },
438         };
439
440         let arg: *mut libc::c_void = mem::transmute(p);
441         let ret = pthread_create(&mut native, &attr, super::thread_start, arg);
442         assert_eq!(pthread_attr_destroy(&mut attr), 0);
443
444         if ret != 0 {
445             // be sure to not leak the closure
446             let _p: Box<proc():Send> = mem::transmute(arg);
447             fail!("failed to spawn native thread: {}", ret);
448         }
449         native
450     }
451
452     pub unsafe fn join(native: rust_thread) {
453         assert_eq!(pthread_join(native, ptr::null_mut()), 0);
454     }
455
456     pub unsafe fn detach(native: rust_thread) {
457         assert_eq!(pthread_detach(native), 0);
458     }
459
460     pub unsafe fn yield_now() { assert_eq!(sched_yield(), 0); }
461     // glibc >= 2.15 has a __pthread_get_minstack() function that returns
462     // PTHREAD_STACK_MIN plus however many bytes are needed for thread-local
463     // storage.  We need that information to avoid blowing up when a small stack
464     // is created in an application with big thread-local storage requirements.
465     // See #6233 for rationale and details.
466     //
467     // Link weakly to the symbol for compatibility with older versions of glibc.
468     // Assumes that we've been dynamically linked to libpthread but that is
469     // currently always the case.  Note that you need to check that the symbol
470     // is non-null before calling it!
471     #[cfg(target_os = "linux")]
472     fn min_stack_size(attr: *const libc::pthread_attr_t) -> libc::size_t {
473         type F = unsafe extern "C" fn(*const libc::pthread_attr_t) -> libc::size_t;
474         extern {
475             #[linkage = "extern_weak"]
476             static __pthread_get_minstack: *const ();
477         }
478         if __pthread_get_minstack.is_null() {
479             PTHREAD_STACK_MIN
480         } else {
481             unsafe { mem::transmute::<*const (), F>(__pthread_get_minstack)(attr) }
482         }
483     }
484
485     // __pthread_get_minstack() is marked as weak but extern_weak linkage is
486     // not supported on OS X, hence this kludge...
487     #[cfg(not(target_os = "linux"))]
488     fn min_stack_size(_: *const libc::pthread_attr_t) -> libc::size_t {
489         PTHREAD_STACK_MIN
490     }
491
492     #[cfg(any(target_os = "linux"))]
493     extern {
494         pub fn pthread_self() -> libc::pthread_t;
495         pub fn pthread_getattr_np(native: libc::pthread_t,
496                                   attr: *mut libc::pthread_attr_t) -> libc::c_int;
497         pub fn pthread_attr_getguardsize(attr: *const libc::pthread_attr_t,
498                                          guardsize: *mut libc::size_t) -> libc::c_int;
499         pub fn pthread_attr_getstack(attr: *const libc::pthread_attr_t,
500                                      stackaddr: *mut *mut libc::c_void,
501                                      stacksize: *mut libc::size_t) -> libc::c_int;
502     }
503
504     #[cfg(target_os = "macos")]
505     extern {
506         pub fn pthread_self() -> libc::pthread_t;
507         pub fn pthread_get_stackaddr_np(thread: libc::pthread_t) -> *mut libc::c_void;
508         pub fn pthread_get_stacksize_np(thread: libc::pthread_t) -> libc::size_t;
509     }
510
511     extern {
512         fn pthread_create(native: *mut libc::pthread_t,
513                           attr: *const libc::pthread_attr_t,
514                           f: super::StartFn,
515                           value: *mut libc::c_void) -> libc::c_int;
516         fn pthread_join(native: libc::pthread_t,
517                         value: *mut *mut libc::c_void) -> libc::c_int;
518         fn pthread_attr_init(attr: *mut libc::pthread_attr_t) -> libc::c_int;
519         pub fn pthread_attr_destroy(attr: *mut libc::pthread_attr_t) -> libc::c_int;
520         fn pthread_attr_setstacksize(attr: *mut libc::pthread_attr_t,
521                                      stack_size: libc::size_t) -> libc::c_int;
522         fn pthread_attr_setdetachstate(attr: *mut libc::pthread_attr_t,
523                                        state: libc::c_int) -> libc::c_int;
524         fn pthread_detach(thread: libc::pthread_t) -> libc::c_int;
525         fn sched_yield() -> libc::c_int;
526     }
527 }
528
529 #[cfg(test)]
530 mod tests {
531     use super::Thread;
532
533     #[test]
534     fn smoke() { Thread::start(proc (){}).join(); }
535
536     #[test]
537     fn data() { assert_eq!(Thread::start(proc () { 1i }).join(), 1); }
538
539     #[test]
540     fn detached() { Thread::spawn(proc () {}) }
541
542     #[test]
543     fn small_stacks() {
544         assert_eq!(42i, Thread::start_stack(0, proc () 42i).join());
545         assert_eq!(42i, Thread::start_stack(1, proc () 42i).join());
546     }
547 }