Skip to content

Commit b02b5cd

Browse files
committed
Retry on EINVAL from pthread_attr_setstacksize()
Enforce that the stack size is > RED_ZONE + PTHREAD_STACK_MIN. If the call to pthread_attr_setstacksize() subsequently fails with EINVAL, it means that the platform requires the stack size to be a multiple of the page size. In that case, round up to the nearest page and retry. Fixes #11694.
1 parent 464b2e2 commit b02b5cd

File tree

2 files changed

+45
-6
lines changed

2 files changed

+45
-6
lines changed

src/libstd/rt/thread.rs

+44-5
Original file line numberDiff line numberDiff line change
@@ -145,18 +145,30 @@ impl<T: Send> Drop for Thread<T> {
145145
#[cfg(windows)]
146146
mod imp {
147147
use cast;
148+
use cmp;
148149
use libc;
149150
use libc::types::os::arch::extra::{LPSECURITY_ATTRIBUTES, SIZE_T, BOOL,
150151
LPVOID, DWORD, LPDWORD, HANDLE};
151152
use ptr;
153+
use unstable::stack::RED_ZONE;
152154

153155
pub type rust_thread = HANDLE;
154156
pub type rust_thread_return = DWORD;
155157

156158
pub unsafe fn create(stack: uint, p: ~proc()) -> rust_thread {
157159
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())
160172
}
161173

162174
pub unsafe fn join(native: rust_thread) {
@@ -190,10 +202,13 @@ mod imp {
190202
#[cfg(unix)]
191203
mod imp {
192204
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};
194207
use libc;
208+
use os;
195209
use ptr;
196210
use unstable::intrinsics;
211+
use unstable::stack::RED_ZONE;
197212

198213
pub type rust_thread = libc::pthread_t;
199214
pub type rust_thread_return = *u8;
@@ -202,11 +217,29 @@ mod imp {
202217
let mut native: libc::pthread_t = intrinsics::uninit();
203218
let mut attr: libc::pthread_attr_t = intrinsics::uninit();
204219
assert_eq!(pthread_attr_init(&mut attr), 0);
205-
assert_eq!(pthread_attr_setstacksize(&mut attr,
206-
stack as libc::size_t), 0);
207220
assert_eq!(pthread_attr_setdetachstate(&mut attr,
208221
PTHREAD_CREATE_JOINABLE), 0);
209222

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+
210243
let arg: *libc::c_void = cast::transmute(p);
211244
assert_eq!(pthread_create(&mut native, &attr,
212245
super::thread_start, arg), 0);
@@ -262,4 +295,10 @@ mod tests {
262295

263296
#[test]
264297
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+
}
265304
}

src/libstd/unstable/stack.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
//! detection is not guaranteed to continue in the future. Usage of this module
2525
//! is discouraged unless absolutely necessary.
2626
27-
static RED_ZONE: uint = 20 * 1024;
27+
pub static RED_ZONE: uint = 20 * 1024;
2828

2929
/// This function is invoked from rust's current __morestack function. Segmented
3030
/// stacks are currently not enabled as segmented stacks, but rather one giant

0 commit comments

Comments
 (0)