@@ -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_get_minstack ( & attr) 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 ) ;
@@ -228,6 +261,51 @@ mod imp {
228
261
#[ cfg( not( target_os = "macos" ) , not( target_os = "android" ) ) ]
229
262
pub unsafe fn yield_now ( ) { assert_eq ! ( pthread_yield( ) , 0 ) ; }
230
263
264
+ #[ cfg( not( target_os = "linux" ) ) ]
265
+ unsafe fn __pthread_get_minstack ( _: * libc:: pthread_attr_t ) -> libc:: size_t {
266
+ libc:: PTHREAD_STACK_MIN
267
+ }
268
+
269
+ // glibc >= 2.15 has a __pthread_get_minstack() function that returns
270
+ // PTHREAD_STACK_MIN plus however many bytes are needed for thread-local
271
+ // storage. We need that information to avoid blowing up when a small stack
272
+ // is created in an application with big thread-local storage requirements.
273
+ // See #6233 for rationale and details.
274
+ //
275
+ // Dynamically resolve the symbol for compatibility with older versions
276
+ // of glibc. Assumes that we've been dynamically linked to libpthread
277
+ // but that is currently always the case. Note that this means we take
278
+ // a dlopen/dlsym/dlclose hit for every new thread. Mitigating that by
279
+ // caching the symbol or the function's return value has its drawbacks:
280
+ //
281
+ // * Caching the symbol breaks when libpthread.so is reloaded because
282
+ // its address changes.
283
+ //
284
+ // * Caching the return value assumes that it's a fixed quantity.
285
+ // Not very future-proof and untrue in the presence of guard pages
286
+ // The reason __pthread_get_minstack() takes a *libc::pthread_attr_t
287
+ // as its argument is because it takes pthread_attr_setguardsize() into
288
+ // account.
289
+ //
290
+ // A better solution is to define __pthread_get_minstack() as a weak symbol
291
+ // but there is currently no way to express that in Rust code.
292
+ #[ cfg( target_os = "linux" ) ]
293
+ unsafe fn __pthread_get_minstack ( attr : * libc:: pthread_attr_t ) -> libc:: size_t {
294
+ use option:: None ;
295
+ use result:: { Err , Ok } ;
296
+ use unstable:: dynamic_lib;
297
+ match dynamic_lib:: DynamicLibrary :: open ( None ) {
298
+ Err ( err) => fail ! ( "DynamicLibrary::open(): {}" , err) ,
299
+ Ok ( handle) => {
300
+ match handle. symbol :: < extern "C" fn ( * libc:: pthread_attr_t ) ->
301
+ libc:: size_t > ( "__pthread_get_minstack" ) {
302
+ Err ( _) => libc:: PTHREAD_STACK_MIN ,
303
+ Ok ( __pthread_get_minstack) => __pthread_get_minstack ( attr) ,
304
+ }
305
+ }
306
+ }
307
+ }
308
+
231
309
extern {
232
310
fn pthread_create ( native : * mut libc:: pthread_t ,
233
311
attr : * libc:: pthread_attr_t ,
@@ -262,4 +340,10 @@ mod tests {
262
340
263
341
#[ test]
264
342
fn detached ( ) { Thread :: spawn ( proc ( ) { } ) }
343
+
344
+ #[ test]
345
+ fn small_stacks ( ) {
346
+ assert_eq ! ( 42 , Thread :: start_stack( 0 , proc ( ) 42 ) . join( ) ) ;
347
+ assert_eq ! ( 42 , Thread :: start_stack( 1 , proc ( ) 42 ) . join( ) ) ;
348
+ }
265
349
}
0 commit comments