|
8 | 8 | // option. This file may not be copied, modified, or distributed
|
9 | 9 | // except according to those terms.
|
10 | 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 | +
|
11 | 17 | #[allow(non_camel_case_types)];
|
12 | 18 |
|
13 | 19 | use cast;
|
| 20 | +use kinds::Send; |
14 | 21 | use libc;
|
15 | 22 | use ops::Drop;
|
16 |
| -use uint; |
| 23 | +use option::{Option, Some, None}; |
17 | 24 | use ptr;
|
| 25 | +use uint; |
18 | 26 |
|
19 | 27 | #[cfg(windows)]
|
20 | 28 | use libc::types::os::arch::extra::{LPSECURITY_ATTRIBUTES, SIZE_T,
|
21 | 29 | LPVOID, DWORD, LPDWORD, HANDLE};
|
22 | 30 |
|
23 | 31 | #[cfg(windows)] type rust_thread = HANDLE;
|
24 | 32 | #[cfg(unix)] type rust_thread = libc::pthread_t;
|
| 33 | +#[cfg(windows)] type rust_thread_return = DWORD; |
| 34 | +#[cfg(unix)] type rust_thread_return = *libc::c_void; |
25 | 35 |
|
26 |
| -pub struct Thread { |
| 36 | +type StartFn = extern "C" fn(*libc::c_void) -> rust_thread_return; |
| 37 | + |
| 38 | +/// This struct represents a native thread's state. This is used to join on an |
| 39 | +/// existing thread created in the join-able state. |
| 40 | +pub struct Thread<T> { |
27 | 41 | priv native: rust_thread,
|
28 |
| - priv joined: bool |
| 42 | + priv joined: bool, |
| 43 | + priv packet: ~Option<T>, |
29 | 44 | }
|
30 | 45 |
|
31 | 46 | static DEFAULT_STACK_SIZE: libc::size_t = 1024*1024;
|
32 | 47 |
|
33 |
| -#[cfg(windows)] type rust_thread_return = DWORD; |
34 |
| -#[cfg(unix)] type rust_thread_return = *libc::c_void; |
| 48 | +// This is the starting point of rust os threads. The first thing we do |
| 49 | +// is make sure that we don't trigger __morestack (also why this has a |
| 50 | +// no_split_stack annotation), and then we extract the main function |
| 51 | +// and invoke it. |
| 52 | +#[no_split_stack] |
| 53 | +extern fn thread_start(main: *libc::c_void) -> rust_thread_return { |
| 54 | + use rt::context; |
| 55 | + unsafe { |
| 56 | + context::record_stack_bounds(0, uint::max_value); |
| 57 | + let f: ~proc() = cast::transmute(main); |
| 58 | + (*f)(); |
| 59 | + cast::transmute(0 as rust_thread_return) |
| 60 | + } |
| 61 | +} |
35 | 62 |
|
36 |
| -impl Thread { |
37 |
| - |
38 |
| - pub fn start(main: proc()) -> Thread { |
39 |
| - // This is the starting point of rust os threads. The first thing we do |
40 |
| - // is make sure that we don't trigger __morestack (also why this has a |
41 |
| - // no_split_stack annotation), and then we extract the main function |
42 |
| - // and invoke it. |
43 |
| - #[no_split_stack] |
44 |
| - extern "C" fn thread_start(trampoline: *libc::c_void) -> rust_thread_return { |
45 |
| - use rt::context; |
46 |
| - unsafe { |
47 |
| - context::record_stack_bounds(0, uint::max_value); |
48 |
| - let f: ~proc() = cast::transmute(trampoline); |
49 |
| - (*f)(); |
50 |
| - } |
51 |
| - unsafe { cast::transmute(0 as rust_thread_return) } |
52 |
| - } |
| 63 | +// There are two impl blocks b/c if T were specified at the top then it's just a |
| 64 | +// pain to specify a type parameter on Thread::spawn (which doesn't need the |
| 65 | +// type parameter). |
| 66 | +impl Thread<()> { |
| 67 | + |
| 68 | + /// Starts execution of a new OS thread. |
| 69 | + /// |
| 70 | + /// This function will not wait for the thread to join, but a handle to the |
| 71 | + /// thread will be returned. |
| 72 | + /// |
| 73 | + /// Note that the handle returned is used to acquire the return value of the |
| 74 | + /// procedure `main`. The `join` function will wait for the thread to finish |
| 75 | + /// and return the value that `main` generated. |
| 76 | + /// |
| 77 | + /// Also note that the `Thread` returned will *always* wait for the thread |
| 78 | + /// to finish executing. This means that even if `join` is not explicitly |
| 79 | + /// called, when the `Thread` falls out of scope its destructor will block |
| 80 | + /// waiting for the OS thread. |
| 81 | + pub fn start<T: Send>(main: proc() -> T) -> Thread<T> { |
| 82 | + |
| 83 | + // We need the address of the packet to fill in to be stable so when |
| 84 | + // `main` fills it in it's still valid, so allocate an extra ~ box to do |
| 85 | + // so. |
| 86 | + let packet = ~None; |
| 87 | + let packet2: *mut Option<T> = unsafe { |
| 88 | + *cast::transmute::<&~Option<T>, **mut Option<T>>(&packet) |
| 89 | + }; |
| 90 | + let main: proc() = proc() unsafe { *packet2 = Some(main()); }; |
| 91 | + let native = unsafe { native_thread_create(~main) }; |
53 | 92 |
|
54 |
| - let native = native_thread_create(thread_start, ~main); |
55 | 93 | Thread {
|
56 | 94 | native: native,
|
57 | 95 | joined: false,
|
| 96 | + packet: packet, |
58 | 97 | }
|
59 | 98 | }
|
60 | 99 |
|
61 |
| - pub fn join(mut self) { |
| 100 | + /// This will spawn a new thread, but it will not wait for the thread to |
| 101 | + /// finish, nor is it possible to wait for the thread to finish. |
| 102 | + /// |
| 103 | + /// This corresponds to creating threads in the 'detached' state on unix |
| 104 | + /// systems. Note that platforms may not keep the main program alive even if |
| 105 | + /// there are detached thread still running around. |
| 106 | + pub fn spawn(main: proc()) { |
| 107 | + unsafe { |
| 108 | + let handle = native_thread_create(~main); |
| 109 | + native_thread_detach(handle); |
| 110 | + } |
| 111 | + } |
| 112 | +} |
| 113 | + |
| 114 | +impl<T: Send> Thread<T> { |
| 115 | + /// Wait for this thread to finish, returning the result of the thread's |
| 116 | + /// calculation. |
| 117 | + pub fn join(mut self) -> T { |
62 | 118 | assert!(!self.joined);
|
63 |
| - native_thread_join(self.native); |
| 119 | + unsafe { native_thread_join(self.native) }; |
64 | 120 | self.joined = true;
|
| 121 | + assert!(self.packet.is_some()); |
| 122 | + self.packet.take_unwrap() |
65 | 123 | }
|
66 | 124 | }
|
67 | 125 |
|
68 |
| -#[cfg(windows)] |
69 |
| -fn native_thread_create(thread_start: extern "C" fn(*libc::c_void) -> rust_thread_return, |
70 |
| - tramp: ~proc()) -> rust_thread { |
71 |
| - unsafe { |
72 |
| - let ptr: *mut libc::c_void = cast::transmute(tramp); |
73 |
| - CreateThread(ptr::mut_null(), DEFAULT_STACK_SIZE, thread_start, ptr, 0, ptr::mut_null()) |
| 126 | +#[unsafe_destructor] |
| 127 | +impl<T: Send> Drop for Thread<T> { |
| 128 | + fn drop(&mut self) { |
| 129 | + // This is required for correctness. If this is not done then the thread |
| 130 | + // would fill in a return box which no longer exists. |
| 131 | + if !self.joined { |
| 132 | + unsafe { native_thread_join(self.native) }; |
| 133 | + } |
74 | 134 | }
|
75 | 135 | }
|
76 | 136 |
|
77 | 137 | #[cfg(windows)]
|
78 |
| -fn native_thread_join(native: rust_thread) { |
| 138 | +unsafe fn native_thread_create(p: ~proc()) -> rust_thread { |
| 139 | + let arg: *mut libc::c_void = cast::transmute(p); |
| 140 | + CreateThread(ptr::mut_null(), DEFAULT_STACK_SIZE, thread_start, |
| 141 | + arg, 0, ptr::mut_null()) |
| 142 | +} |
| 143 | + |
| 144 | +#[cfg(windows)] |
| 145 | +unsafe fn native_thread_join(native: rust_thread) { |
79 | 146 | use libc::consts::os::extra::INFINITE;
|
80 |
| - unsafe { WaitForSingleObject(native, INFINITE); } |
| 147 | + WaitForSingleObject(native, INFINITE); |
| 148 | +} |
| 149 | + |
| 150 | +#[cfg(windows)] |
| 151 | +unsafe fn native_thread_detach(native: rust_thread) { |
| 152 | + assert!(libc::CloseHandle(native) != 0); |
81 | 153 | }
|
82 | 154 |
|
83 | 155 | #[cfg(unix)]
|
84 |
| -fn native_thread_create(thread_start: extern "C" fn(*libc::c_void) -> rust_thread_return, |
85 |
| - tramp: ~proc()) -> rust_thread { |
| 156 | +unsafe fn native_thread_create(p: ~proc()) -> rust_thread { |
86 | 157 | use unstable::intrinsics;
|
87 |
| - let mut native: libc::pthread_t = unsafe { intrinsics::uninit() }; |
88 |
| - |
89 |
| - unsafe { |
90 |
| - use libc::consts::os::posix01::PTHREAD_CREATE_JOINABLE; |
| 158 | + use libc::consts::os::posix01::PTHREAD_CREATE_JOINABLE; |
91 | 159 |
|
92 |
| - let mut attr: libc::pthread_attr_t = intrinsics::uninit(); |
93 |
| - assert!(pthread_attr_init(&mut attr) == 0); |
94 |
| - assert!(pthread_attr_setstacksize(&mut attr, DEFAULT_STACK_SIZE) == 0); |
95 |
| - assert!(pthread_attr_setdetachstate(&mut attr, PTHREAD_CREATE_JOINABLE) == 0); |
| 160 | + let mut native: libc::pthread_t = intrinsics::uninit(); |
| 161 | + let mut attr: libc::pthread_attr_t = intrinsics::uninit(); |
| 162 | + assert_eq!(pthread_attr_init(&mut attr), 0); |
| 163 | + assert_eq!(pthread_attr_setstacksize(&mut attr, DEFAULT_STACK_SIZE), 0); |
| 164 | + assert_eq!(pthread_attr_setdetachstate(&mut attr, PTHREAD_CREATE_JOINABLE), 0); |
96 | 165 |
|
97 |
| - let ptr: *libc::c_void = cast::transmute(tramp); |
98 |
| - assert!(pthread_create(&mut native, &attr, thread_start, ptr) == 0); |
99 |
| - } |
| 166 | + let arg: *libc::c_void = cast::transmute(p); |
| 167 | + assert_eq!(pthread_create(&mut native, &attr, thread_start, arg), 0); |
100 | 168 | native
|
101 | 169 | }
|
102 | 170 |
|
103 | 171 | #[cfg(unix)]
|
104 |
| -fn native_thread_join(native: rust_thread) { |
105 |
| - unsafe { assert!(pthread_join(native, ptr::null()) == 0) } |
| 172 | +unsafe fn native_thread_join(native: rust_thread) { |
| 173 | + assert_eq!(pthread_join(native, ptr::null()), 0); |
106 | 174 | }
|
107 | 175 |
|
108 |
| -impl Drop for Thread { |
109 |
| - fn drop(&mut self) { |
110 |
| - assert!(self.joined); |
111 |
| - } |
| 176 | +#[cfg(unix)] |
| 177 | +fn native_thread_detach(native: rust_thread) { |
| 178 | + unsafe { assert_eq!(pthread_detach(native), 0) } |
112 | 179 | }
|
113 | 180 |
|
114 | 181 | #[cfg(windows)]
|
115 | 182 | extern "system" {
|
116 |
| - fn CreateThread(lpThreadAttributes: LPSECURITY_ATTRIBUTES, dwStackSize: SIZE_T, |
117 |
| - lpStartAddress: extern "C" fn(*libc::c_void) -> rust_thread_return, |
118 |
| - lpParameter: LPVOID, dwCreationFlags: DWORD, lpThreadId: LPDWORD) -> HANDLE; |
| 183 | + fn CreateThread(lpThreadAttributes: LPSECURITY_ATTRIBUTES, |
| 184 | + dwStackSize: SIZE_T, |
| 185 | + lpStartAddress: StartFn, |
| 186 | + lpParameter: LPVOID, |
| 187 | + dwCreationFlags: DWORD, |
| 188 | + lpThreadId: LPDWORD) -> HANDLE; |
119 | 189 | fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) -> DWORD;
|
120 | 190 | }
|
121 | 191 |
|
122 | 192 | #[cfg(unix)]
|
123 | 193 | extern {
|
124 |
| - fn pthread_create(native: *mut libc::pthread_t, attr: *libc::pthread_attr_t, |
125 |
| - f: extern "C" fn(*libc::c_void) -> rust_thread_return, |
| 194 | + fn pthread_create(native: *mut libc::pthread_t, |
| 195 | + attr: *libc::pthread_attr_t, |
| 196 | + f: StartFn, |
126 | 197 | value: *libc::c_void) -> libc::c_int;
|
127 |
| - fn pthread_join(native: libc::pthread_t, value: **libc::c_void) -> libc::c_int; |
| 198 | + fn pthread_join(native: libc::pthread_t, |
| 199 | + value: **libc::c_void) -> libc::c_int; |
128 | 200 | fn pthread_attr_init(attr: *mut libc::pthread_attr_t) -> libc::c_int;
|
129 | 201 | fn pthread_attr_setstacksize(attr: *mut libc::pthread_attr_t,
|
130 | 202 | stack_size: libc::size_t) -> libc::c_int;
|
131 | 203 | fn pthread_attr_setdetachstate(attr: *mut libc::pthread_attr_t,
|
132 | 204 | state: libc::c_int) -> libc::c_int;
|
| 205 | + fn pthread_detach(thread: libc::pthread_t) -> libc::c_int; |
| 206 | +} |
| 207 | + |
| 208 | +#[cfg(test)] |
| 209 | +mod tests { |
| 210 | + use super::Thread; |
| 211 | + |
| 212 | + #[test] |
| 213 | + fn smoke() { do Thread::start {}.join(); } |
| 214 | + |
| 215 | + #[test] |
| 216 | + fn data() { assert_eq!(do Thread::start { 1 }.join(), 1); } |
| 217 | + |
| 218 | + #[test] |
| 219 | + fn detached() { do Thread::spawn {} } |
133 | 220 | }
|
0 commit comments