@@ -145,18 +145,30 @@ impl<T: Send> Drop for Thread<T> {
145
145
#[ cfg( windows) ]
146
146
mod imp {
147
147
use cast;
148
+ use cmp;
148
149
use libc;
149
150
use libc:: types:: os:: arch:: extra:: { LPSECURITY_ATTRIBUTES , SIZE_T , BOOL ,
150
151
LPVOID , DWORD , LPDWORD , HANDLE } ;
151
152
use ptr;
153
+ use unstable:: stack:: RED_ZONE ;
152
154
153
155
pub type rust_thread = HANDLE ;
154
156
pub type rust_thread_return = DWORD ;
155
157
156
158
pub unsafe fn create ( stack : uint , p : ~proc ( ) ) -> rust_thread {
157
159
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
+ // FIXME On UNIX, we guard against stack sizes that are too small but
161
+ // that's because pthreads enforces that stacks are at least
162
+ // PTHREAD_STACK_MIN bytes big. Windows has no such lower limit, it's
163
+ // just that below a certain threshold you can't do anything useful.
164
+ // That threshold is application and architecture-specific, however.
165
+ // For now, the only requirement is that it's big enough to hold the
166
+ // red zone. Round up to the next 64 kB because that's what the NT
167
+ // kernel does, might as well make it explicit. With the current
168
+ // 20 kB red zone, that makes for a 64 kB minimum stack.
169
+ let stack_size = ( cmp:: max ( stack, RED_ZONE ) + 0xfffe ) & ( -0xfffe - 1 ) ;
170
+ CreateThread ( ptr:: mut_null ( ) , stack_size as libc:: size_t ,
171
+ super :: thread_start, arg, 0 , ptr:: mut_null ( ) )
160
172
}
161
173
162
174
pub unsafe fn join ( native : rust_thread ) {
@@ -190,10 +202,13 @@ mod imp {
190
202
#[ cfg( unix) ]
191
203
mod imp {
192
204
use cast;
193
- use libc:: consts:: os:: posix01:: PTHREAD_CREATE_JOINABLE ;
205
+ use cmp;
206
+ use libc:: consts:: os:: posix01:: { PTHREAD_CREATE_JOINABLE , PTHREAD_STACK_MIN } ;
194
207
use libc;
208
+ use os;
195
209
use ptr;
196
210
use unstable:: intrinsics;
211
+ use unstable:: stack:: RED_ZONE ;
197
212
198
213
pub type rust_thread = libc:: pthread_t ;
199
214
pub type rust_thread_return = * u8 ;
@@ -202,11 +217,29 @@ mod imp {
202
217
let mut native: libc:: pthread_t = intrinsics:: uninit ( ) ;
203
218
let mut attr: libc:: pthread_attr_t = intrinsics:: uninit ( ) ;
204
219
assert_eq ! ( pthread_attr_init( & mut attr) , 0 ) ;
205
- assert_eq ! ( pthread_attr_setstacksize( & mut attr,
206
- stack as libc:: size_t) , 0 ) ;
207
220
assert_eq ! ( pthread_attr_setdetachstate( & mut attr,
208
221
PTHREAD_CREATE_JOINABLE ) , 0 ) ;
209
222
223
+ // Reserve room for the red zone, the runtime's stack of last resort.
224
+ let stack_size = cmp:: max ( stack, RED_ZONE + PTHREAD_STACK_MIN as uint ) ;
225
+ match pthread_attr_setstacksize ( & mut attr, stack_size as libc:: size_t ) {
226
+ 0 => {
227
+ } ,
228
+ libc:: EINVAL => {
229
+ // EINVAL means |stack_size| is either too small or not a
230
+ // multiple of the system page size. Because it's definitely
231
+ // >= PTHREAD_STACK_MIN, it must be an alignment issue.
232
+ // Round up to the neareast page and try again.
233
+ let page_size = os:: page_size ( ) ;
234
+ let stack_size = ( stack_size + page_size - 1 ) & ( -( page_size - 1 ) - 1 ) ;
235
+ assert_eq ! ( pthread_attr_setstacksize( & mut attr, stack_size as libc:: size_t) , 0 ) ;
236
+ } ,
237
+ errno => {
238
+ // This cannot really happen.
239
+ fail ! ( "pthread_attr_setstacksize() error: {} ({})" , os:: last_os_error( ) , errno) ;
240
+ } ,
241
+ } ;
242
+
210
243
let arg: * libc:: c_void = cast:: transmute ( p) ;
211
244
assert_eq ! ( pthread_create( & mut native, & attr,
212
245
super :: thread_start, arg) , 0 ) ;
@@ -262,4 +295,10 @@ mod tests {
262
295
263
296
#[ test]
264
297
fn detached ( ) { Thread :: spawn ( proc ( ) { } ) }
298
+
299
+ #[ test]
300
+ fn small_stacks ( ) {
301
+ assert_eq ! ( 42 , Thread :: start_stack( 0 , proc ( ) 42 ) . join( ) ) ;
302
+ assert_eq ! ( 42 , Thread :: start_stack( 1 , proc ( ) 42 ) . join( ) ) ;
303
+ }
265
304
}
0 commit comments