(index<- ) ./libstd/rt/thread.rs
git branch: * master 5200215 auto merge of #14035 : alexcrichton/rust/experimental, r=huonw
modified: Fri May 9 13:02:28 2014
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 cast;
21 use kinds::Send;
22 use libc;
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()> = cast::transmute(main);
50 (*f)();
51 cast::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 *cast::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 cast;
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 = cast::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> = cast::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 cast;
217 use cmp;
218 use kinds::Send;
219 use libc::consts::os::posix01::{PTHREAD_CREATE_JOINABLE, PTHREAD_STACK_MIN};
220 use libc;
221 use mem;
222 use os;
223 use owned::Box;
224 use ptr;
225 use rt::stack::RED_ZONE;
226
227 pub type rust_thread = libc::pthread_t;
228 pub type rust_thread_return = *u8;
229
230 pub unsafe fn create(stack: uint, p: Box<proc():Send>) -> rust_thread {
231 let mut native: libc::pthread_t = mem::uninit();
232 let mut attr: libc::pthread_attr_t = mem::uninit();
233 assert_eq!(pthread_attr_init(&mut attr), 0);
234 assert_eq!(pthread_attr_setdetachstate(&mut attr,
235 PTHREAD_CREATE_JOINABLE), 0);
236
237 // Reserve room for the red zone, the runtime's stack of last resort.
238 let stack_size = cmp::max(stack, RED_ZONE + min_stack_size(&attr) as uint);
239 match pthread_attr_setstacksize(&mut attr, stack_size as libc::size_t) {
240 0 => {
241 },
242 libc::EINVAL => {
243 // EINVAL means |stack_size| is either too small or not a
244 // multiple of the system page size. Because it's definitely
245 // >= PTHREAD_STACK_MIN, it must be an alignment issue.
246 // Round up to the neareast page and try again.
247 let page_size = os::page_size();
248 let stack_size = (stack_size + page_size - 1) & (-(page_size - 1) - 1);
249 assert_eq!(pthread_attr_setstacksize(&mut attr, stack_size as libc::size_t), 0);
250 },
251 errno => {
252 // This cannot really happen.
253 fail!("pthread_attr_setstacksize() error: {} ({})", os::last_os_error(), errno);
254 },
255 };
256
257 let arg: *libc::c_void = cast::transmute(p);
258 let ret = pthread_create(&mut native, &attr, super::thread_start, arg);
259 assert_eq!(pthread_attr_destroy(&mut attr), 0);
260
261 if ret != 0 {
262 // be sure to not leak the closure
263 let _p: Box<proc():Send> = cast::transmute(arg);
264 fail!("failed to spawn native thread: {}", os::last_os_error());
265 }
266 native
267 }
268
269 pub unsafe fn join(native: rust_thread) {
270 assert_eq!(pthread_join(native, ptr::null()), 0);
271 }
272
273 pub unsafe fn detach(native: rust_thread) {
274 assert_eq!(pthread_detach(native), 0);
275 }
276
277 #[cfg(target_os = "macos")]
278 #[cfg(target_os = "android")]
279 pub unsafe fn yield_now() { assert_eq!(sched_yield(), 0); }
280
281 #[cfg(not(target_os = "macos"), not(target_os = "android"))]
282 pub unsafe fn yield_now() { assert_eq!(pthread_yield(), 0); }
283
284 // glibc >= 2.15 has a __pthread_get_minstack() function that returns
285 // PTHREAD_STACK_MIN plus however many bytes are needed for thread-local
286 // storage. We need that information to avoid blowing up when a small stack
287 // is created in an application with big thread-local storage requirements.
288 // See #6233 for rationale and details.
289 //
290 // Link weakly to the symbol for compatibility with older versions of glibc.
291 // Assumes that we've been dynamically linked to libpthread but that is
292 // currently always the case. Note that you need to check that the symbol
293 // is non-null before calling it!
294 #[cfg(target_os = "linux")]
295 fn min_stack_size(attr: *libc::pthread_attr_t) -> libc::size_t {
296 use ptr::RawPtr;
297 type F = extern "C" unsafe fn(*libc::pthread_attr_t) -> libc::size_t;
298 extern {
299 #[linkage = "extern_weak"]
300 static __pthread_get_minstack: *();
301 }
302 if __pthread_get_minstack.is_null() {
303 PTHREAD_STACK_MIN
304 } else {
305 unsafe { cast::transmute::<*(), F>(__pthread_get_minstack)(attr) }
306 }
307 }
308
309 // __pthread_get_minstack() is marked as weak but extern_weak linkage is
310 // not supported on OS X, hence this kludge...
311 #[cfg(not(target_os = "linux"))]
312 fn min_stack_size(_: *libc::pthread_attr_t) -> libc::size_t {
313 PTHREAD_STACK_MIN
314 }
315
316 extern {
317 fn pthread_create(native: *mut libc::pthread_t,
318 attr: *libc::pthread_attr_t,
319 f: super::StartFn,
320 value: *libc::c_void) -> libc::c_int;
321 fn pthread_join(native: libc::pthread_t,
322 value: **libc::c_void) -> libc::c_int;
323 fn pthread_attr_init(attr: *mut libc::pthread_attr_t) -> libc::c_int;
324 fn pthread_attr_destroy(attr: *mut libc::pthread_attr_t) -> libc::c_int;
325 fn pthread_attr_setstacksize(attr: *mut libc::pthread_attr_t,
326 stack_size: libc::size_t) -> libc::c_int;
327 fn pthread_attr_setdetachstate(attr: *mut libc::pthread_attr_t,
328 state: libc::c_int) -> libc::c_int;
329 fn pthread_detach(thread: libc::pthread_t) -> libc::c_int;
330
331 #[cfg(target_os = "macos")]
332 #[cfg(target_os = "android")]
333 fn sched_yield() -> libc::c_int;
334 #[cfg(not(target_os = "macos"), not(target_os = "android"))]
335 fn pthread_yield() -> libc::c_int;
336 }
337 }
338
339 #[cfg(test)]
340 mod tests {
341 use super::Thread;
342
343 #[test]
344 fn smoke() { Thread::start(proc (){}).join(); }
345
346 #[test]
347 fn data() { assert_eq!(Thread::start(proc () { 1 }).join(), 1); }
348
349 #[test]
350 fn detached() { Thread::spawn(proc () {}) }
351
352 #[test]
353 fn small_stacks() {
354 assert_eq!(42, Thread::start_stack(0, proc () 42).join());
355 assert_eq!(42, Thread::start_stack(1, proc () 42).join());
356 }
357 }
358
libstd/rt/thread.rs:31:52-31:52 -struct- definition:
/// existing thread created in the join-able state.
pub struct Thread<T> {
native: imp::rust_thread,
references:- 691: Thread {
92: native: native,
--
124: impl<T: Send> Thread<T> {
125: /// Wait for this thread to finish, returning the result of the thread's
--
137: impl<T: Send> Drop for Thread<T> {
138: fn drop(&mut self) {
libstd/rt/thread.rs:269:4-269:4 -fn- definition:
pub unsafe fn join(native: rust_thread) {
assert_eq!(pthread_join(native, ptr::null()), 0);
}
references:- 2128: assert!(!self.joined);
129: unsafe { imp::join(self.native) };
130: self.joined = true;
--
141: if !self.joined {
142: unsafe { imp::join(self.native) };
143: }
libstd/rt/thread.rs:227:4-227:4 -NK_AS_STR_TODO- definition:
pub type rust_thread = libc::pthread_t;
pub type rust_thread_return = *u8;
pub unsafe fn create(stack: uint, p: Box<proc():Send>) -> rust_thread {
references:- 4230: pub unsafe fn create(stack: uint, p: Box<proc():Send>) -> rust_thread {
231: let mut native: libc::pthread_t = mem::uninit();
--
273: pub unsafe fn detach(native: rust_thread) {
274: assert_eq!(pthread_detach(native), 0);
libstd/rt/thread.rs:228:4-228:4 -NK_AS_STR_TODO- definition:
pub type rust_thread_return = *u8;
pub unsafe fn create(stack: uint, p: Box<proc():Send>) -> rust_thread {
let mut native: libc::pthread_t = mem::uninit();
references:- 328: type StartFn = extern "C" fn(*libc::c_void) -> imp::rust_thread_return;
--
50: (*f)();
51: cast::transmute(0 as imp::rust_thread_return)
52: }
libstd/rt/thread.rs:230:4-230:4 -fn- definition:
pub unsafe fn create(stack: uint, p: Box<proc():Send>) -> rust_thread {
let mut native: libc::pthread_t = mem::uninit();
let mut attr: libc::pthread_attr_t = mem::uninit();
references:- 288: let main = proc() unsafe { *packet2 = Some(main()); };
89: let native = unsafe { imp::create(stack, box main) };
--
111: unsafe {
112: let handle = imp::create(stack, box main);
113: imp::detach(handle);