]> git.lizzy.rs Git - rust.git/blob - src/libstd/rt/thread.rs
auto merge of #11386 : rcatolino/rust/ice-10955, r=pcwalton
[rust.git] / src / libstd / rt / thread.rs
1 // Copyright 2013 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 cast;
20 use kinds::Send;
21 use libc;
22 use ops::Drop;
23 use option::{Option, Some, None};
24 use uint;
25
26 type StartFn = extern "C" fn(*libc::c_void) -> imp::rust_thread_return;
27
28 /// This struct represents a native thread's state. This is used to join on an
29 /// existing thread created in the join-able state.
30 pub struct Thread<T> {
31     priv native: imp::rust_thread,
32     priv joined: bool,
33     priv packet: ~Option<T>,
34 }
35
36 static DEFAULT_STACK_SIZE: uint = 1024 * 1024;
37
38 // This is the starting point of rust os threads. The first thing we do
39 // is make sure that we don't trigger __morestack (also why this has a
40 // no_split_stack annotation), and then we extract the main function
41 // and invoke it.
42 #[no_split_stack]
43 extern fn thread_start(main: *libc::c_void) -> imp::rust_thread_return {
44     use unstable::stack;
45     unsafe {
46         stack::record_stack_bounds(0, uint::max_value);
47         let f: ~proc() = cast::transmute(main);
48         (*f)();
49         cast::transmute(0 as imp::rust_thread_return)
50     }
51 }
52
53 // There are two impl blocks b/c if T were specified at the top then it's just a
54 // pain to specify a type parameter on Thread::spawn (which doesn't need the
55 // type parameter).
56 impl Thread<()> {
57
58     /// Starts execution of a new OS thread.
59     ///
60     /// This function will not wait for the thread to join, but a handle to the
61     /// thread will be returned.
62     ///
63     /// Note that the handle returned is used to acquire the return value of the
64     /// procedure `main`. The `join` function will wait for the thread to finish
65     /// and return the value that `main` generated.
66     ///
67     /// Also note that the `Thread` returned will *always* wait for the thread
68     /// to finish executing. This means that even if `join` is not explicitly
69     /// called, when the `Thread` falls out of scope its destructor will block
70     /// waiting for the OS thread.
71     pub fn start<T: Send>(main: proc() -> T) -> Thread<T> {
72         Thread::start_stack(DEFAULT_STACK_SIZE, main)
73     }
74
75     /// Performs the same functionality as `start`, but specifies an explicit
76     /// stack size for the new thread.
77     pub fn start_stack<T: Send>(stack: uint, main: proc() -> T) -> Thread<T> {
78
79         // We need the address of the packet to fill in to be stable so when
80         // `main` fills it in it's still valid, so allocate an extra ~ box to do
81         // so.
82         let packet = ~None;
83         let packet2: *mut Option<T> = unsafe {
84             *cast::transmute::<&~Option<T>, **mut Option<T>>(&packet)
85         };
86         let main: proc() = proc() unsafe { *packet2 = Some(main()); };
87         let native = unsafe { imp::create(stack, ~main) };
88
89         Thread {
90             native: native,
91             joined: false,
92             packet: packet,
93         }
94     }
95
96     /// This will spawn a new thread, but it will not wait for the thread to
97     /// finish, nor is it possible to wait for the thread to finish.
98     ///
99     /// This corresponds to creating threads in the 'detached' state on unix
100     /// systems. Note that platforms may not keep the main program alive even if
101     /// there are detached thread still running around.
102     pub fn spawn(main: proc()) {
103         Thread::spawn_stack(DEFAULT_STACK_SIZE, main)
104     }
105
106     /// Performs the same functionality as `spawn`, but explicitly specifies a
107     /// stack size for the new thread.
108     pub fn spawn_stack(stack: uint, main: proc()) {
109         unsafe {
110             let handle = imp::create(stack, ~main);
111             imp::detach(handle);
112         }
113     }
114
115     /// Relinquishes the CPU slot that this OS-thread is currently using,
116     /// allowing another thread to run for awhile.
117     pub fn yield_now() {
118         unsafe { imp::yield_now(); }
119     }
120 }
121
122 impl<T: Send> Thread<T> {
123     /// Wait for this thread to finish, returning the result of the thread's
124     /// calculation.
125     pub fn join(mut self) -> T {
126         assert!(!self.joined);
127         unsafe { imp::join(self.native) };
128         self.joined = true;
129         assert!(self.packet.is_some());
130         self.packet.take_unwrap()
131     }
132 }
133
134 #[unsafe_destructor]
135 impl<T: Send> Drop for Thread<T> {
136     fn drop(&mut self) {
137         // This is required for correctness. If this is not done then the thread
138         // would fill in a return box which no longer exists.
139         if !self.joined {
140             unsafe { imp::join(self.native) };
141         }
142     }
143 }
144
145 #[cfg(windows)]
146 mod imp {
147     use cast;
148     use libc;
149     use libc::types::os::arch::extra::{LPSECURITY_ATTRIBUTES, SIZE_T, BOOL,
150                                        LPVOID, DWORD, LPDWORD, HANDLE};
151     use ptr;
152
153     pub type rust_thread = HANDLE;
154     pub type rust_thread_return = DWORD;
155
156     pub unsafe fn create(stack: uint, p: ~proc()) -> rust_thread {
157         let arg: *mut libc::c_void = cast::transmute(p);
158         CreateThread(ptr::mut_null(), stack as libc::size_t, super::thread_start,
159                      arg, 0, ptr::mut_null())
160     }
161
162     pub unsafe fn join(native: rust_thread) {
163         use libc::consts::os::extra::INFINITE;
164         WaitForSingleObject(native, INFINITE);
165     }
166
167     pub unsafe fn detach(native: rust_thread) {
168         assert!(libc::CloseHandle(native) != 0);
169     }
170
171     pub unsafe fn yield_now() {
172         // This function will return 0 if there are no other threads to execute,
173         // but this also means that the yield was useless so this isn't really a
174         // case that needs to be worried about.
175         SwitchToThread();
176     }
177
178     extern "system" {
179         fn CreateThread(lpThreadAttributes: LPSECURITY_ATTRIBUTES,
180                         dwStackSize: SIZE_T,
181                         lpStartAddress: super::StartFn,
182                         lpParameter: LPVOID,
183                         dwCreationFlags: DWORD,
184                         lpThreadId: LPDWORD) -> HANDLE;
185         fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) -> DWORD;
186         fn SwitchToThread() -> BOOL;
187     }
188 }
189
190 #[cfg(unix)]
191 mod imp {
192     use cast;
193     use libc::consts::os::posix01::PTHREAD_CREATE_JOINABLE;
194     use libc;
195     use ptr;
196     use unstable::intrinsics;
197
198     pub type rust_thread = libc::pthread_t;
199     pub type rust_thread_return = *libc::c_void;
200
201     pub unsafe fn create(stack: uint, p: ~proc()) -> rust_thread {
202         let mut native: libc::pthread_t = intrinsics::uninit();
203         let mut attr: libc::pthread_attr_t = intrinsics::uninit();
204         assert_eq!(pthread_attr_init(&mut attr), 0);
205         assert_eq!(pthread_attr_setstacksize(&mut attr,
206                                              stack as libc::size_t), 0);
207         assert_eq!(pthread_attr_setdetachstate(&mut attr,
208                                                PTHREAD_CREATE_JOINABLE), 0);
209
210         let arg: *libc::c_void = cast::transmute(p);
211         assert_eq!(pthread_create(&mut native, &attr,
212                                   super::thread_start, arg), 0);
213         native
214     }
215
216     pub unsafe fn join(native: rust_thread) {
217         assert_eq!(pthread_join(native, ptr::null()), 0);
218     }
219
220     pub unsafe fn detach(native: rust_thread) {
221         assert_eq!(pthread_detach(native), 0);
222     }
223
224     #[cfg(target_os = "macos")]
225     #[cfg(target_os = "android")]
226     pub unsafe fn yield_now() { assert_eq!(sched_yield(), 0); }
227
228     #[cfg(not(target_os = "macos"), not(target_os = "android"))]
229     pub unsafe fn yield_now() { assert_eq!(pthread_yield(), 0); }
230
231     extern {
232         fn pthread_create(native: *mut libc::pthread_t,
233                           attr: *libc::pthread_attr_t,
234                           f: super::StartFn,
235                           value: *libc::c_void) -> libc::c_int;
236         fn pthread_join(native: libc::pthread_t,
237                         value: **libc::c_void) -> libc::c_int;
238         fn pthread_attr_init(attr: *mut libc::pthread_attr_t) -> libc::c_int;
239         fn pthread_attr_setstacksize(attr: *mut libc::pthread_attr_t,
240                                      stack_size: libc::size_t) -> libc::c_int;
241         fn pthread_attr_setdetachstate(attr: *mut libc::pthread_attr_t,
242                                        state: libc::c_int) -> libc::c_int;
243         fn pthread_detach(thread: libc::pthread_t) -> libc::c_int;
244
245         #[cfg(target_os = "macos")]
246         #[cfg(target_os = "android")]
247         fn sched_yield() -> libc::c_int;
248         #[cfg(not(target_os = "macos"), not(target_os = "android"))]
249         fn pthread_yield() -> libc::c_int;
250     }
251 }
252
253 #[cfg(test)]
254 mod tests {
255     use super::Thread;
256
257     #[test]
258     fn smoke() { do Thread::start {}.join(); }
259
260     #[test]
261     fn data() { assert_eq!(do Thread::start { 1 }.join(), 1); }
262
263     #[test]
264     fn detached() { do Thread::spawn {} }
265 }