]> git.lizzy.rs Git - rust.git/blob - src/libstd/rt/thread.rs
core: Finish stabilizing the `mem` module.
[rust.git] / src / libstd / rt / 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 #![allow(unsigned_negate)]
19
20 use kinds::Send;
21 use libc;
22 use mem;
23 use ops::Drop;
24 use option::{Option, Some, None};
25 use owned::Box;
26 use uint;
27
28 type StartFn = extern "C" fn(*libc::c_void) -> imp::rust_thread_return;
29
30 /// This struct represents a native thread's state. This is used to join on an
31 /// existing thread created in the join-able state.
32 pub struct Thread<T> {
33     native: imp::rust_thread,
34     joined: bool,
35     packet: Box<Option<T>>,
36 }
37
38 static DEFAULT_STACK_SIZE: uint = 1024 * 1024;
39
40 // This is the starting point of rust os threads. The first thing we do
41 // is make sure that we don't trigger __morestack (also why this has a
42 // no_split_stack annotation), and then we extract the main function
43 // and invoke it.
44 #[no_split_stack]
45 extern fn thread_start(main: *libc::c_void) -> imp::rust_thread_return {
46     use rt::stack;
47     unsafe {
48         stack::record_stack_bounds(0, uint::MAX);
49         let f: Box<proc()> = mem::transmute(main);
50         (*f)();
51         mem::transmute(0 as imp::rust_thread_return)
52     }
53 }
54
55 // There are two impl blocks b/c if T were specified at the top then it's just a
56 // pain to specify a type parameter on Thread::spawn (which doesn't need the
57 // type parameter).
58 impl Thread<()> {
59
60     /// Starts execution of a new OS thread.
61     ///
62     /// This function will not wait for the thread to join, but a handle to the
63     /// thread will be returned.
64     ///
65     /// Note that the handle returned is used to acquire the return value of the
66     /// procedure `main`. The `join` function will wait for the thread to finish
67     /// and return the value that `main` generated.
68     ///
69     /// Also note that the `Thread` returned will *always* wait for the thread
70     /// to finish executing. This means that even if `join` is not explicitly
71     /// called, when the `Thread` falls out of scope its destructor will block
72     /// waiting for the OS thread.
73     pub fn start<T: Send>(main: proc():Send -> T) -> Thread<T> {
74         Thread::start_stack(DEFAULT_STACK_SIZE, main)
75     }
76
77     /// Performs the same functionality as `start`, but specifies an explicit
78     /// stack size for the new thread.
79     pub fn start_stack<T: Send>(stack: uint, main: proc():Send -> T) -> Thread<T> {
80
81         // We need the address of the packet to fill in to be stable so when
82         // `main` fills it in it's still valid, so allocate an extra box to do
83         // so.
84         let packet = box None;
85         let packet2: *mut Option<T> = unsafe {
86             *mem::transmute::<&Box<Option<T>>, **mut Option<T>>(&packet)
87         };
88         let main = proc() unsafe { *packet2 = Some(main()); };
89         let native = unsafe { imp::create(stack, box main) };
90
91         Thread {
92             native: native,
93             joined: false,
94             packet: packet,
95         }
96     }
97
98     /// This will spawn a new thread, but it will not wait for the thread to
99     /// finish, nor is it possible to wait for the thread to finish.
100     ///
101     /// This corresponds to creating threads in the 'detached' state on unix
102     /// systems. Note that platforms may not keep the main program alive even if
103     /// there are detached thread still running around.
104     pub fn spawn(main: proc():Send) {
105         Thread::spawn_stack(DEFAULT_STACK_SIZE, main)
106     }
107
108     /// Performs the same functionality as `spawn`, but explicitly specifies a
109     /// stack size for the new thread.
110     pub fn spawn_stack(stack: uint, main: proc():Send) {
111         unsafe {
112             let handle = imp::create(stack, box main);
113             imp::detach(handle);
114         }
115     }
116
117     /// Relinquishes the CPU slot that this OS-thread is currently using,
118     /// allowing another thread to run for awhile.
119     pub fn yield_now() {
120         unsafe { imp::yield_now(); }
121     }
122 }
123
124 impl<T: Send> Thread<T> {
125     /// Wait for this thread to finish, returning the result of the thread's
126     /// calculation.
127     pub fn join(mut self) -> T {
128         assert!(!self.joined);
129         unsafe { imp::join(self.native) };
130         self.joined = true;
131         assert!(self.packet.is_some());
132         self.packet.take_unwrap()
133     }
134 }
135
136 #[unsafe_destructor]
137 impl<T: Send> Drop for Thread<T> {
138     fn drop(&mut self) {
139         // This is required for correctness. If this is not done then the thread
140         // would fill in a return box which no longer exists.
141         if !self.joined {
142             unsafe { imp::join(self.native) };
143         }
144     }
145 }
146
147 #[cfg(windows)]
148 mod imp {
149     use mem;
150     use cmp;
151     use kinds::Send;
152     use libc;
153     use libc::types::os::arch::extra::{LPSECURITY_ATTRIBUTES, SIZE_T, BOOL,
154                                        LPVOID, DWORD, LPDWORD, HANDLE};
155     use os;
156     use owned::Box;
157     use ptr;
158     use rt::stack::RED_ZONE;
159
160     pub type rust_thread = HANDLE;
161     pub type rust_thread_return = DWORD;
162
163     pub unsafe fn create(stack: uint, p: Box<proc():Send>) -> rust_thread {
164         let arg: *mut libc::c_void = mem::transmute(p);
165         // FIXME On UNIX, we guard against stack sizes that are too small but
166         // that's because pthreads enforces that stacks are at least
167         // PTHREAD_STACK_MIN bytes big.  Windows has no such lower limit, it's
168         // just that below a certain threshold you can't do anything useful.
169         // That threshold is application and architecture-specific, however.
170         // For now, the only requirement is that it's big enough to hold the
171         // red zone.  Round up to the next 64 kB because that's what the NT
172         // kernel does, might as well make it explicit.  With the current
173         // 20 kB red zone, that makes for a 64 kB minimum stack.
174         let stack_size = (cmp::max(stack, RED_ZONE) + 0xfffe) & (-0xfffe - 1);
175         let ret = CreateThread(ptr::mut_null(), stack_size as libc::size_t,
176                                super::thread_start, arg, 0, ptr::mut_null());
177
178         if ret as uint == 0 {
179             // be sure to not leak the closure
180             let _p: Box<proc():Send> = mem::transmute(arg);
181             fail!("failed to spawn native thread: {}", os::last_os_error());
182         }
183         return ret;
184     }
185
186     pub unsafe fn join(native: rust_thread) {
187         use libc::consts::os::extra::INFINITE;
188         WaitForSingleObject(native, INFINITE);
189     }
190
191     pub unsafe fn detach(native: rust_thread) {
192         assert!(libc::CloseHandle(native) != 0);
193     }
194
195     pub unsafe fn yield_now() {
196         // This function will return 0 if there are no other threads to execute,
197         // but this also means that the yield was useless so this isn't really a
198         // case that needs to be worried about.
199         SwitchToThread();
200     }
201
202     extern "system" {
203         fn CreateThread(lpThreadAttributes: LPSECURITY_ATTRIBUTES,
204                         dwStackSize: SIZE_T,
205                         lpStartAddress: super::StartFn,
206                         lpParameter: LPVOID,
207                         dwCreationFlags: DWORD,
208                         lpThreadId: LPDWORD) -> HANDLE;
209         fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) -> DWORD;
210         fn SwitchToThread() -> BOOL;
211     }
212 }
213
214 #[cfg(unix)]
215 mod imp {
216     use cmp;
217     use kinds::Send;
218     use libc::consts::os::posix01::{PTHREAD_CREATE_JOINABLE, PTHREAD_STACK_MIN};
219     use libc;
220     use mem;
221     use os;
222     use owned::Box;
223     use ptr;
224     use rt::stack::RED_ZONE;
225
226     pub type rust_thread = libc::pthread_t;
227     pub type rust_thread_return = *u8;
228
229     pub unsafe fn create(stack: uint, p: Box<proc():Send>) -> rust_thread {
230         let mut native: libc::pthread_t = mem::zeroed();
231         let mut attr: libc::pthread_attr_t = mem::zeroed();
232         assert_eq!(pthread_attr_init(&mut attr), 0);
233         assert_eq!(pthread_attr_setdetachstate(&mut attr,
234                                                PTHREAD_CREATE_JOINABLE), 0);
235
236         // Reserve room for the red zone, the runtime's stack of last resort.
237         let stack_size = cmp::max(stack, RED_ZONE + min_stack_size(&attr) as uint);
238         match pthread_attr_setstacksize(&mut attr, stack_size as libc::size_t) {
239             0 => {
240             },
241             libc::EINVAL => {
242                 // EINVAL means |stack_size| is either too small or not a
243                 // multiple of the system page size.  Because it's definitely
244                 // >= PTHREAD_STACK_MIN, it must be an alignment issue.
245                 // Round up to the neareast page and try again.
246                 let page_size = os::page_size();
247                 let stack_size = (stack_size + page_size - 1) & (-(page_size - 1) - 1);
248                 assert_eq!(pthread_attr_setstacksize(&mut attr, stack_size as libc::size_t), 0);
249             },
250             errno => {
251                 // This cannot really happen.
252                 fail!("pthread_attr_setstacksize() error: {} ({})", os::last_os_error(), errno);
253             },
254         };
255
256         let arg: *libc::c_void = mem::transmute(p);
257         let ret = pthread_create(&mut native, &attr, super::thread_start, arg);
258         assert_eq!(pthread_attr_destroy(&mut attr), 0);
259
260         if ret != 0 {
261             // be sure to not leak the closure
262             let _p: Box<proc():Send> = mem::transmute(arg);
263             fail!("failed to spawn native thread: {}", os::last_os_error());
264         }
265         native
266     }
267
268     pub unsafe fn join(native: rust_thread) {
269         assert_eq!(pthread_join(native, ptr::null()), 0);
270     }
271
272     pub unsafe fn detach(native: rust_thread) {
273         assert_eq!(pthread_detach(native), 0);
274     }
275
276     pub unsafe fn yield_now() { assert_eq!(sched_yield(), 0); }
277
278     // glibc >= 2.15 has a __pthread_get_minstack() function that returns
279     // PTHREAD_STACK_MIN plus however many bytes are needed for thread-local
280     // storage.  We need that information to avoid blowing up when a small stack
281     // is created in an application with big thread-local storage requirements.
282     // See #6233 for rationale and details.
283     //
284     // Link weakly to the symbol for compatibility with older versions of glibc.
285     // Assumes that we've been dynamically linked to libpthread but that is
286     // currently always the case.  Note that you need to check that the symbol
287     // is non-null before calling it!
288     #[cfg(target_os = "linux")]
289     fn min_stack_size(attr: *libc::pthread_attr_t) -> libc::size_t {
290         use ptr::RawPtr;
291         type F = unsafe extern "C" fn(*libc::pthread_attr_t) -> libc::size_t;
292         extern {
293             #[linkage = "extern_weak"]
294             static __pthread_get_minstack: *();
295         }
296         if __pthread_get_minstack.is_null() {
297             PTHREAD_STACK_MIN
298         } else {
299             unsafe { mem::transmute::<*(), F>(__pthread_get_minstack)(attr) }
300         }
301     }
302
303     // __pthread_get_minstack() is marked as weak but extern_weak linkage is
304     // not supported on OS X, hence this kludge...
305     #[cfg(not(target_os = "linux"))]
306     fn min_stack_size(_: *libc::pthread_attr_t) -> libc::size_t {
307         PTHREAD_STACK_MIN
308     }
309
310     extern {
311         fn pthread_create(native: *mut libc::pthread_t,
312                           attr: *libc::pthread_attr_t,
313                           f: super::StartFn,
314                           value: *libc::c_void) -> libc::c_int;
315         fn pthread_join(native: libc::pthread_t,
316                         value: **libc::c_void) -> libc::c_int;
317         fn pthread_attr_init(attr: *mut libc::pthread_attr_t) -> libc::c_int;
318         fn pthread_attr_destroy(attr: *mut libc::pthread_attr_t) -> libc::c_int;
319         fn pthread_attr_setstacksize(attr: *mut libc::pthread_attr_t,
320                                      stack_size: libc::size_t) -> libc::c_int;
321         fn pthread_attr_setdetachstate(attr: *mut libc::pthread_attr_t,
322                                        state: libc::c_int) -> libc::c_int;
323         fn pthread_detach(thread: libc::pthread_t) -> libc::c_int;
324         fn sched_yield() -> libc::c_int;
325     }
326 }
327
328 #[cfg(test)]
329 mod tests {
330     use super::Thread;
331
332     #[test]
333     fn smoke() { Thread::start(proc (){}).join(); }
334
335     #[test]
336     fn data() { assert_eq!(Thread::start(proc () { 1 }).join(), 1); }
337
338     #[test]
339     fn detached() { Thread::spawn(proc () {}) }
340
341     #[test]
342     fn small_stacks() {
343         assert_eq!(42, Thread::start_stack(0, proc () 42).join());
344         assert_eq!(42, Thread::start_stack(1, proc () 42).join());
345     }
346 }
347