}
fn local_io<'a>(&'a mut self) -> Option<rtio::LocalIo<'a>> { None }
fn stack_bounds(&self) -> (uint, uint) { fail!() }
+ fn stack_guard(&self) -> Option<uint> { fail!() }
fn can_block(&self) -> bool { true }
fn wrap(self: Box<SimpleTask>) -> Box<Any+'static> { fail!() }
}
}
}
+ /// Point to the last writable byte of the stack
+ pub fn guard(&self) -> *const uint {
+ (self.start() as uint + page_size()) as *const uint
+ }
+
/// Point to the low end of the allocated stack
pub fn start(&self) -> *const uint {
self.buf.as_ref().map(|m| m.data() as *const uint)
c.current_stack_segment.end() as uint)
}
+ fn stack_guard(&self) -> Option<uint> {
+ let c = self.coroutine.as_ref()
+ .expect("GreenTask.stack_guard called without a coroutine");
+
+ Some(c.current_stack_segment.guard() as uint)
+ }
+
fn can_block(&self) -> bool { false }
fn wrap(self: Box<GreenTask>) -> Box<Any+'static> {
rt::init(argc, argv);
let mut exit_code = None;
let mut main = Some(main);
- let mut task = task::new((my_stack_bottom, my_stack_top));
+ let mut task = task::new((my_stack_bottom, my_stack_top),
+ rt::thread::main_guard_page());
task.name = Some(str::Slice("<main>"));
drop(task.run(|| {
unsafe {
use std::task::{TaskBuilder, Spawner};
/// Creates a new Task which is ready to execute as a 1:1 task.
-pub fn new(stack_bounds: (uint, uint)) -> Box<Task> {
+pub fn new(stack_bounds: (uint, uint), stack_guard: uint) -> Box<Task> {
let mut task = box Task::new();
let mut ops = ops();
ops.stack_bounds = stack_bounds;
+ ops.stack_guard = stack_guard;
task.put_runtime(ops);
return task;
}
io: io::IoFactory::new(),
// these *should* get overwritten
stack_bounds: (0, 0),
+ stack_guard: 0
}
}
my_stack);
}
let mut ops = ops;
+ ops.stack_guard = rt::thread::current_guard_page();
ops.stack_bounds = (my_stack - stack + 1024, my_stack);
let mut f = Some(f);
// native tasks necessarily know their precise bounds, hence this is
// optional.
stack_bounds: (uint, uint),
+
+ stack_guard: uint
}
impl rt::Runtime for Ops {
fn stack_bounds(&self) -> (uint, uint) { self.stack_bounds }
+ fn stack_guard(&self) -> Option<uint> {
+ if self.stack_guard != 0 {
+ Some(self.stack_guard)
+ } else {
+ None
+ }
+ }
+
fn can_block(&self) -> bool { true }
// This function gets a little interesting. There are a few safety and
mod thread_local_storage;
mod util;
mod libunwind;
+mod stack_overflow;
pub mod args;
pub mod bookkeeping;
fn local_io<'a>(&'a mut self) -> Option<rtio::LocalIo<'a>>;
/// The (low, high) edges of the current stack.
fn stack_bounds(&self) -> (uint, uint); // (lo, hi)
+ /// The last writable byte of the stack next to the guard page
+ fn stack_guard(&self) -> Option<uint>;
fn can_block(&self) -> bool;
// FIXME: This is a serious code smell and this should not exist at all.
args::init(argc, argv);
local_ptr::init();
at_exit_imp::init();
+ thread::init();
}
// FIXME(#14344) this shouldn't be necessary
bookkeeping::wait_for_other_tasks();
at_exit_imp::run();
args::cleanup();
+ thread::cleanup();
local_ptr::cleanup();
}
#[cfg(not(test))] // in testing, use the original libstd's version
#[lang = "stack_exhausted"]
extern fn stack_exhausted() {
- use core::prelude::*;
- use alloc::boxed::Box;
- use local::Local;
- use task::Task;
use core::intrinsics;
unsafe {
// #9854 - unwinding on windows through __morestack has never worked
// #2361 - possible implementation of not using landing pads
- let task: Option<Box<Task>> = Local::try_take();
- let name = match task {
- Some(ref task) => {
- task.name.as_ref().map(|n| n.as_slice())
- }
- None => None
- };
- let name = name.unwrap_or("<unknown>");
-
- // See the message below for why this is not emitted to the
- // task's logger. This has the additional conundrum of the
- // logger may not be initialized just yet, meaning that an FFI
- // call would happen to initialized it (calling out to libuv),
- // and the FFI call needs 2MB of stack when we just ran out.
- rterrln!("task '{}' has overflowed its stack", name);
+ ::stack_overflow::report();
intrinsics::abort();
}
--- /dev/null
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![allow(non_camel_case_types)]
+
+use core::prelude::*;
+use libc;
+use local::Local;
+use task::Task;
+
+pub unsafe fn init() {
+ imp::init();
+}
+
+pub unsafe fn cleanup() {
+ imp::cleanup();
+}
+
+pub struct Handler {
+ _data: *mut libc::c_void
+}
+
+impl Handler {
+ pub unsafe fn new() -> Handler {
+ imp::make_handler()
+ }
+}
+
+impl Drop for Handler {
+ fn drop(&mut self) {
+ unsafe {
+ imp::drop_handler(self);
+ }
+ }
+}
+
+pub unsafe fn report() {
+ // See the message below for why this is not emitted to the
+ // ^ Where did the message below go?
+ // task's logger. This has the additional conundrum of the
+ // logger may not be initialized just yet, meaning that an FFI
+ // call would happen to initialized it (calling out to libuv),
+ // and the FFI call needs 2MB of stack when we just ran out.
+
+ let task: Option<*mut Task> = Local::try_unsafe_borrow();
+
+ let name = task.and_then(|task| {
+ (*task).name.as_ref().map(|n| n.as_slice())
+ });
+
+ rterrln!("\ntask '{}' has overflowed its stack", name.unwrap_or("<unknown>"));
+}
+
+// get_task_info is called from an exception / signal handler.
+// It returns the guard page of the current task or 0 if that
+// guard page doesn't exist. None is returned if there's currently
+// no local task.
+#[cfg(any(windows, target_os = "linux", target_os = "macos"))]
+unsafe fn get_task_guard_page() -> Option<uint> {
+ let task: Option<*mut Task> = Local::try_unsafe_borrow();
+
+ task.map(|task| {
+ let runtime = (*task).take_runtime();
+ let guard = runtime.stack_guard();
+ (*task).put_runtime(runtime);
+
+ guard.unwrap_or(0)
+ })
+}
+
+#[cfg(windows)]
+#[allow(non_snake_case)]
+mod imp {
+ use core::ptr;
+ use core::mem;
+ use libc;
+ use libc::types::os::arch::extra::{LPVOID, DWORD, LONG, BOOL};
+ use stack;
+ use super::{Handler, get_task_guard_page, report};
+
+ // This is initialized in init() and only read from after
+ static mut PAGE_SIZE: uint = 0;
+
+ #[no_stack_check]
+ extern "system" fn vectored_handler(ExceptionInfo: *mut EXCEPTION_POINTERS) -> LONG {
+ unsafe {
+ let rec = &(*(*ExceptionInfo).ExceptionRecord);
+ let code = rec.ExceptionCode;
+
+ if code != EXCEPTION_STACK_OVERFLOW {
+ return EXCEPTION_CONTINUE_SEARCH;
+ }
+
+ // We're calling into functions with stack checks,
+ // however stack checks by limit should be disabled on Windows
+ stack::record_sp_limit(0);
+
+ if get_task_guard_page().is_some() {
+ report();
+ }
+
+ EXCEPTION_CONTINUE_SEARCH
+ }
+ }
+
+ pub unsafe fn init() {
+ let mut info = mem::zeroed();
+ libc::GetSystemInfo(&mut info);
+ PAGE_SIZE = info.dwPageSize as uint;
+
+ if AddVectoredExceptionHandler(0, vectored_handler) == ptr::null_mut() {
+ fail!("failed to install exception handler");
+ }
+
+ mem::forget(make_handler());
+ }
+
+ pub unsafe fn cleanup() {
+ }
+
+ pub unsafe fn make_handler() -> Handler {
+ if SetThreadStackGuarantee(&mut 0x5000) == 0 {
+ fail!("failed to reserve stack space for exception handling");
+ }
+
+ super::Handler { _data: 0i as *mut libc::c_void }
+ }
+
+ pub unsafe fn drop_handler(_handler: &mut Handler) {
+ }
+
+ pub struct EXCEPTION_RECORD {
+ pub ExceptionCode: DWORD,
+ pub ExceptionFlags: DWORD,
+ pub ExceptionRecord: *mut EXCEPTION_RECORD,
+ pub ExceptionAddress: LPVOID,
+ pub NumberParameters: DWORD,
+ pub ExceptionInformation: [LPVOID, ..EXCEPTION_MAXIMUM_PARAMETERS]
+ }
+
+ pub struct EXCEPTION_POINTERS {
+ pub ExceptionRecord: *mut EXCEPTION_RECORD,
+ pub ContextRecord: LPVOID
+ }
+
+ pub type PVECTORED_EXCEPTION_HANDLER = extern "system"
+ fn(ExceptionInfo: *mut EXCEPTION_POINTERS) -> LONG;
+
+ pub type ULONG = libc::c_ulong;
+
+ const EXCEPTION_CONTINUE_SEARCH: LONG = 0;
+ const EXCEPTION_MAXIMUM_PARAMETERS: uint = 15;
+ const EXCEPTION_STACK_OVERFLOW: DWORD = 0xc00000fd;
+
+ extern "system" {
+ fn AddVectoredExceptionHandler(FirstHandler: ULONG,
+ VectoredHandler: PVECTORED_EXCEPTION_HANDLER)
+ -> LPVOID;
+ fn SetThreadStackGuarantee(StackSizeInBytes: *mut ULONG) -> BOOL;
+ }
+}
+
+#[cfg(any(target_os = "linux", target_os = "macos"))]
+mod imp {
+ use core::prelude::*;
+ use stack;
+
+ use super::{Handler, get_task_guard_page, report};
+ use core::mem;
+ use core::ptr;
+ use core::intrinsics;
+ use self::signal::{siginfo, sigaction, SIGBUS, SIG_DFL,
+ SA_SIGINFO, SA_ONSTACK, sigaltstack,
+ SIGSTKSZ};
+ use libc;
+ use libc::funcs::posix88::mman::{mmap, munmap};
+ use libc::consts::os::posix88::{SIGSEGV,
+ PROT_READ,
+ PROT_WRITE,
+ MAP_PRIVATE,
+ MAP_ANON,
+ MAP_FAILED};
+
+
+ // This is initialized in init() and only read from after
+ static mut PAGE_SIZE: uint = 0;
+
+ #[no_stack_check]
+ unsafe extern fn signal_handler(signum: libc::c_int,
+ info: *mut siginfo,
+ _data: *mut libc::c_void) {
+
+ // We can not return from a SIGSEGV or SIGBUS signal.
+ // See: https://www.gnu.org/software/libc/manual/html_node/Handler-Returns.html
+
+ unsafe fn term(signum: libc::c_int) -> ! {
+ use core::mem::transmute;
+
+ signal(signum, transmute(SIG_DFL));
+ raise(signum);
+ intrinsics::abort();
+ }
+
+ // We're calling into functions with stack checks
+ stack::record_sp_limit(0);
+
+ match get_task_guard_page() {
+ Some(guard) => {
+ let addr = (*info).si_addr as uint;
+
+ if guard == 0 || addr < guard - PAGE_SIZE || addr >= guard {
+ term(signum);
+ }
+
+ report();
+
+ intrinsics::abort()
+ }
+ None => term(signum)
+ }
+ }
+
+ static mut MAIN_ALTSTACK: *mut libc::c_void = 0 as *mut libc::c_void;
+
+ pub unsafe fn init() {
+ let psize = libc::sysconf(libc::consts::os::sysconf::_SC_PAGESIZE);
+ if psize == -1 {
+ fail!("failed to get page size");
+ }
+
+ PAGE_SIZE = psize as uint;
+
+ let mut action: sigaction = mem::zeroed();
+ action.sa_flags = SA_SIGINFO | SA_ONSTACK;
+ action.sa_sigaction = signal_handler as sighandler_t;
+ sigaction(SIGSEGV, &action, ptr::null_mut());
+ sigaction(SIGBUS, &action, ptr::null_mut());
+
+ let handler = make_handler();
+ MAIN_ALTSTACK = handler._data;
+ mem::forget(handler);
+ }
+
+ pub unsafe fn cleanup() {
+ Handler { _data: MAIN_ALTSTACK };
+ }
+
+ pub unsafe fn make_handler() -> Handler {
+ let alt_stack = mmap(ptr::null_mut(),
+ signal::SIGSTKSZ,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANON,
+ -1,
+ 0);
+ if alt_stack == MAP_FAILED {
+ fail!("failed to allocate an alternative stack");
+ }
+
+ let mut stack: sigaltstack = mem::zeroed();
+
+ stack.ss_sp = alt_stack;
+ stack.ss_flags = 0;
+ stack.ss_size = SIGSTKSZ;
+
+ sigaltstack(&stack, ptr::null_mut());
+
+ Handler { _data: alt_stack }
+ }
+
+ pub unsafe fn drop_handler(handler: &mut Handler) {
+ munmap(handler._data, SIGSTKSZ);
+ }
+
+ type sighandler_t = *mut libc::c_void;
+
+ #[cfg(any(all(target_os = "linux", target_arch = "x86"), // may not match
+ all(target_os = "linux", target_arch = "x86_64"),
+ all(target_os = "linux", target_arch = "arm"), // may not match
+ target_os = "android"))] // may not match
+ mod signal {
+ use libc;
+ use super::sighandler_t;
+
+ pub static SA_ONSTACK: libc::c_int = 0x08000000;
+ pub static SA_SIGINFO: libc::c_int = 0x00000004;
+ pub static SIGBUS: libc::c_int = 7;
+
+ pub static SIGSTKSZ: libc::size_t = 8192;
+
+ pub static SIG_DFL: sighandler_t = 0i as sighandler_t;
+
+ // This definition is not as accurate as it could be, {si_addr} is
+ // actually a giant union. Currently we're only interested in that field,
+ // however.
+ #[repr(C)]
+ pub struct siginfo {
+ si_signo: libc::c_int,
+ si_errno: libc::c_int,
+ si_code: libc::c_int,
+ pub si_addr: *mut libc::c_void
+ }
+
+ #[repr(C)]
+ pub struct sigaction {
+ pub sa_sigaction: sighandler_t,
+ pub sa_mask: sigset_t,
+ pub sa_flags: libc::c_int,
+ sa_restorer: *mut libc::c_void,
+ }
+
+ #[cfg(target_word_size = "32")]
+ #[repr(C)]
+ pub struct sigset_t {
+ __val: [libc::c_ulong, ..32],
+ }
+ #[cfg(target_word_size = "64")]
+ #[repr(C)]
+ pub struct sigset_t {
+ __val: [libc::c_ulong, ..16],
+ }
+
+ #[repr(C)]
+ pub struct sigaltstack {
+ pub ss_sp: *mut libc::c_void,
+ pub ss_flags: libc::c_int,
+ pub ss_size: libc::size_t
+ }
+
+ }
+
+ #[cfg(target_os = "macos")]
+ mod signal {
+ use libc;
+ use super::sighandler_t;
+
+ pub const SA_ONSTACK: libc::c_int = 0x0001;
+ pub const SA_SIGINFO: libc::c_int = 0x0040;
+ pub const SIGBUS: libc::c_int = 10;
+
+ pub const SIGSTKSZ: libc::size_t = 131072;
+
+ pub const SIG_DFL: sighandler_t = 0i as sighandler_t;
+
+ pub type sigset_t = u32;
+
+ // This structure has more fields, but we're not all that interested in
+ // them.
+ #[repr(C)]
+ pub struct siginfo {
+ pub si_signo: libc::c_int,
+ pub si_errno: libc::c_int,
+ pub si_code: libc::c_int,
+ pub pid: libc::pid_t,
+ pub uid: libc::uid_t,
+ pub status: libc::c_int,
+ pub si_addr: *mut libc::c_void
+ }
+
+ #[repr(C)]
+ pub struct sigaltstack {
+ pub ss_sp: *mut libc::c_void,
+ pub ss_size: libc::size_t,
+ pub ss_flags: libc::c_int
+ }
+
+ #[repr(C)]
+ pub struct sigaction {
+ pub sa_sigaction: sighandler_t,
+ pub sa_mask: sigset_t,
+ pub sa_flags: libc::c_int,
+ }
+ }
+
+ extern {
+ pub fn signal(signum: libc::c_int, handler: sighandler_t) -> sighandler_t;
+ pub fn raise(signum: libc::c_int) -> libc::c_int;
+
+ pub fn sigaction(signum: libc::c_int,
+ act: *const sigaction,
+ oldact: *mut sigaction) -> libc::c_int;
+
+ pub fn sigaltstack(ss: *const sigaltstack,
+ oss: *mut sigaltstack) -> libc::c_int;
+ }
+}
+
+#[cfg(not(any(target_os = "linux",
+ target_os = "macos",
+ windows)))]
+mod imp {
+ use libc;
+
+ pub unsafe fn init() {
+ }
+
+ pub unsafe fn cleanup() {
+ }
+
+ pub unsafe fn make_handler() -> super::Handler {
+ super::Handler { _data: 0i as *mut libc::c_void }
+ }
+
+ pub unsafe fn drop_handler(_handler: &mut super::Handler) {
+ }
+}
/// # fn main() {
///
/// // Create a task using a native runtime
-/// let task = native::task::new((0, uint::MAX));
+/// let task = native::task::new((0, uint::MAX), 0);
///
/// // Run some code, catching any possible failures
/// let task = task.run(|| {
/// # fn main() {
///
/// // Create a new native task
- /// let task = native::task::new((0, uint::MAX));
+ /// let task = native::task::new((0, uint::MAX), 0);
///
/// // Run some code once and then destroy this task
/// task.run(|| {
use libc;
use stack;
+use stack_overflow;
+pub unsafe fn init() {
+ imp::guard::init();
+ stack_overflow::init();
+}
+
+pub unsafe fn cleanup() {
+ stack_overflow::cleanup();
+}
+
+#[cfg(target_os = "windows")]
+type StartFn = extern "system" fn(*mut libc::c_void) -> imp::rust_thread_return;
+
+#[cfg(not(target_os = "windows"))]
type StartFn = extern "C" fn(*mut libc::c_void) -> imp::rust_thread_return;
/// This struct represents a native thread's state. This is used to join on an
// no_stack_check annotation), and then we extract the main function
// and invoke it.
#[no_stack_check]
-extern fn thread_start(main: *mut libc::c_void) -> imp::rust_thread_return {
+fn start_thread(main: *mut libc::c_void) -> imp::rust_thread_return {
unsafe {
stack::record_os_managed_stack_bounds(0, uint::MAX);
+ let handler = stack_overflow::Handler::new();
let f: Box<proc()> = mem::transmute(main);
(*f)();
+ drop(handler);
mem::transmute(0 as imp::rust_thread_return)
}
}
+#[no_stack_check]
+#[cfg(target_os = "windows")]
+extern "system" fn thread_start(main: *mut libc::c_void) -> imp::rust_thread_return {
+ return start_thread(main);
+}
+
+#[no_stack_check]
+#[cfg(not(target_os = "windows"))]
+extern fn thread_start(main: *mut libc::c_void) -> imp::rust_thread_return {
+ return start_thread(main);
+}
+
+/// Returns the last writable byte of the main thread's stack next to the guard
+/// page. Must be called from the main thread.
+pub fn main_guard_page() -> uint {
+ unsafe {
+ imp::guard::main()
+ }
+}
+
+/// Returns the last writable byte of the current thread's stack next to the
+/// guard page. Must not be called from the main thread.
+pub fn current_guard_page() -> uint {
+ unsafe {
+ imp::guard::current()
+ }
+}
+
// There are two impl blocks b/c if T were specified at the top then it's just a
// pain to specify a type parameter on Thread::spawn (which doesn't need the
// type parameter).
}
#[cfg(windows)]
+#[allow(non_snake_case)]
mod imp {
use core::prelude::*;
pub type rust_thread = HANDLE;
pub type rust_thread_return = DWORD;
+ pub mod guard {
+ pub unsafe fn main() -> uint {
+ 0
+ }
+
+ pub unsafe fn current() -> uint {
+ 0
+ }
+
+ pub unsafe fn init() {
+ }
+ }
+
pub unsafe fn create(stack: uint, p: Box<proc():Send>) -> rust_thread {
let arg: *mut libc::c_void = mem::transmute(p);
// FIXME On UNIX, we guard against stack sizes that are too small but
pub type rust_thread = libc::pthread_t;
pub type rust_thread_return = *mut u8;
+ #[cfg(all(not(target_os = "linux"), not(target_os = "macos")))]
+ pub mod guard {
+ pub unsafe fn current() -> uint {
+ 0
+ }
+
+ pub unsafe fn main() -> uint {
+ 0
+ }
+
+ pub unsafe fn init() {
+ }
+ }
+
+ #[cfg(any(target_os = "linux", target_os = "macos"))]
+ pub mod guard {
+ use super::*;
+ #[cfg(any(target_os = "linux", target_os = "android"))]
+ use core::mem;
+ #[cfg(any(target_os = "linux", target_os = "android"))]
+ use core::ptr;
+ use libc;
+ use libc::funcs::posix88::mman::{mmap};
+ use libc::consts::os::posix88::{PROT_NONE,
+ MAP_PRIVATE,
+ MAP_ANON,
+ MAP_FAILED,
+ MAP_FIXED};
+
+ // These are initialized in init() and only read from after
+ static mut PAGE_SIZE: uint = 0;
+ static mut GUARD_PAGE: uint = 0;
+
+ #[cfg(target_os = "macos")]
+ unsafe fn get_stack_start() -> *mut libc::c_void {
+ current() as *mut libc::c_void
+ }
+
+ #[cfg(any(target_os = "linux", target_os = "android"))]
+ unsafe fn get_stack_start() -> *mut libc::c_void {
+ let mut attr: libc::pthread_attr_t = mem::zeroed();
+ if pthread_getattr_np(pthread_self(), &mut attr) != 0 {
+ fail!("failed to get thread attributes");
+ }
+ let mut stackaddr = ptr::null_mut();
+ let mut stacksize = 0;
+ if pthread_attr_getstack(&attr, &mut stackaddr, &mut stacksize) != 0 {
+ fail!("failed to get stack information");
+ }
+ if pthread_attr_destroy(&mut attr) != 0 {
+ fail!("failed to destroy thread attributes");
+ }
+ stackaddr
+ }
+
+ pub unsafe fn init() {
+ let psize = libc::sysconf(libc::consts::os::sysconf::_SC_PAGESIZE);
+ if psize == -1 {
+ fail!("failed to get page size");
+ }
+
+ PAGE_SIZE = psize as uint;
+
+ let stackaddr = get_stack_start();
+
+ // Rellocate the last page of the stack.
+ // This ensures SIGBUS will be raised on
+ // stack overflow.
+ let result = mmap(stackaddr,
+ PAGE_SIZE as libc::size_t,
+ PROT_NONE,
+ MAP_PRIVATE | MAP_ANON | MAP_FIXED,
+ -1,
+ 0);
+
+ if result != stackaddr || result == MAP_FAILED {
+ fail!("failed to allocate a guard page");
+ }
+
+ let offset = if cfg!(target_os = "linux") {
+ 2
+ } else {
+ 1
+ };
+
+ GUARD_PAGE = stackaddr as uint + offset * PAGE_SIZE;
+ }
+
+ pub unsafe fn main() -> uint {
+ GUARD_PAGE
+ }
+
+ #[cfg(target_os = "macos")]
+ pub unsafe fn current() -> uint {
+ (pthread_get_stackaddr_np(pthread_self()) as libc::size_t -
+ pthread_get_stacksize_np(pthread_self())) as uint
+ }
+
+ #[cfg(any(target_os = "linux", target_os = "android"))]
+ pub unsafe fn current() -> uint {
+ let mut attr: libc::pthread_attr_t = mem::zeroed();
+ if pthread_getattr_np(pthread_self(), &mut attr) != 0 {
+ fail!("failed to get thread attributes");
+ }
+ let mut guardsize = 0;
+ if pthread_attr_getguardsize(&attr, &mut guardsize) != 0 {
+ fail!("failed to get stack guard page");
+ }
+ if guardsize == 0 {
+ fail!("there is no guard page");
+ }
+ let mut stackaddr = ptr::null_mut();
+ let mut stacksize = 0;
+ if pthread_attr_getstack(&attr, &mut stackaddr, &mut stacksize) != 0 {
+ fail!("failed to get stack information");
+ }
+ if pthread_attr_destroy(&mut attr) != 0 {
+ fail!("failed to destroy thread attributes");
+ }
+
+ stackaddr as uint + guardsize as uint
+ }
+ }
+
pub unsafe fn create(stack: uint, p: Box<proc():Send>) -> rust_thread {
let mut native: libc::pthread_t = mem::zeroed();
let mut attr: libc::pthread_attr_t = mem::zeroed();
PTHREAD_STACK_MIN
}
+ #[cfg(any(target_os = "linux"))]
+ extern {
+ pub fn pthread_self() -> libc::pthread_t;
+ pub fn pthread_getattr_np(native: libc::pthread_t,
+ attr: *mut libc::pthread_attr_t) -> libc::c_int;
+ pub fn pthread_attr_getguardsize(attr: *const libc::pthread_attr_t,
+ guardsize: *mut libc::size_t) -> libc::c_int;
+ pub fn pthread_attr_getstack(attr: *const libc::pthread_attr_t,
+ stackaddr: *mut *mut libc::c_void,
+ stacksize: *mut libc::size_t) -> libc::c_int;
+ }
+
+ #[cfg(target_os = "macos")]
+ extern {
+ pub fn pthread_self() -> libc::pthread_t;
+ pub fn pthread_get_stackaddr_np(thread: libc::pthread_t) -> *mut libc::c_void;
+ pub fn pthread_get_stacksize_np(thread: libc::pthread_t) -> libc::size_t;
+ }
+
extern {
fn pthread_create(native: *mut libc::pthread_t,
attr: *const libc::pthread_attr_t,
fn pthread_join(native: libc::pthread_t,
value: *mut *mut libc::c_void) -> libc::c_int;
fn pthread_attr_init(attr: *mut libc::pthread_attr_t) -> libc::c_int;
- fn pthread_attr_destroy(attr: *mut libc::pthread_attr_t) -> libc::c_int;
+ pub fn pthread_attr_destroy(attr: *mut libc::pthread_attr_t) -> libc::c_int;
fn pthread_attr_setstacksize(attr: *mut libc::pthread_attr_t,
stack_size: libc::size_t) -> libc::c_int;
fn pthread_attr_setdetachstate(attr: *mut libc::pthread_attr_t,
--- /dev/null
+// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//ignore-android
+//ignore-freebsd
+//ignore-ios
+//ignore-dragonfly
+
+#![feature(asm)]
+
+use std::io::process::Command;
+use std::os;
+
+// lifted from the test module
+// Inlining to avoid llvm turning the recursive functions into tail calls,
+// which doesn't consume stack.
+#[inline(always)]
+#[no_stack_check]
+pub fn black_box<T>(dummy: T) { unsafe { asm!("" : : "r"(&dummy)) } }
+
+#[no_stack_check]
+fn recurse() {
+ let buf = [0i, ..10];
+ black_box(buf);
+ recurse();
+}
+
+fn main() {
+ let args = os::args();
+ let args = args.as_slice();
+ if args.len() > 1 && args[1].as_slice() == "recurse" {
+ spawn(proc() {
+ recurse();
+ });
+ } else {
+ let recurse = Command::new(args[0].as_slice()).arg("recurse").output().unwrap();
+ assert!(!recurse.status.success());
+ let error = String::from_utf8_lossy(recurse.error.as_slice());
+ assert!(error.as_slice().contains("has overflowed its stack"));
+ }
+}
--- /dev/null
+// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//ignore-android
+//ignore-linux
+//ignore-freebsd
+//ignore-ios
+//ignore-dragonfly
+
+#![feature(asm)]
+
+use std::io::process::Command;
+use std::os;
+
+// lifted from the test module
+// Inlining to avoid llvm turning the recursive functions into tail calls,
+// which doesn't consume stack.
+#[inline(always)]
+#[no_stack_check]
+pub fn black_box<T>(dummy: T) { unsafe { asm!("" : : "r"(&dummy)) } }
+
+#[no_stack_check]
+fn recurse() {
+ let buf = [0i, ..10];
+ black_box(buf);
+ recurse();
+}
+
+fn main() {
+ let args = os::args();
+ let args = args.as_slice();
+ if args.len() > 1 && args[1].as_slice() == "recurse" {
+ recurse();
+ } else {
+ let recurse = Command::new(args[0].as_slice()).arg("recurse").output().unwrap();
+ assert!(!recurse.status.success());
+ let error = String::from_utf8_lossy(recurse.error.as_slice());
+ assert!(error.as_slice().contains("has overflowed its stack"));
+ }
+}
let silent = Command::new(args[0].as_slice()).arg("silent").output().unwrap();
assert!(!silent.status.success());
let error = String::from_utf8_lossy(silent.error.as_slice());
- // FIXME #17562: Windows is using stack probes and isn't wired up to print an error
- if !cfg!(windows) {
- assert!(error.as_slice().contains("has overflowed its stack"));
- }
+ assert!(error.as_slice().contains("has overflowed its stack"));
let loud = Command::new(args[0].as_slice()).arg("loud").output().unwrap();
assert!(!loud.status.success());
let error = String::from_utf8_lossy(silent.error.as_slice());
- // FIXME #17562: Windows is using stack probes and isn't wired up to print an error
- if !cfg!(windows) {
- assert!(error.as_slice().contains("has overflowed its stack"));
- }
+ assert!(error.as_slice().contains("has overflowed its stack"));
}
}
--- /dev/null
+// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::io::process::Command;
+use std::os;
+
+fn main() {
+ let args = os::args();
+ let args = args.as_slice();
+ if args.len() > 1 && args[1].as_slice() == "segfault" {
+ unsafe { *(0 as *mut int) = 1 }; // trigger a segfault
+ } else {
+ let segfault = Command::new(args[0].as_slice()).arg("segfault").output().unwrap();
+ assert!(!segfault.status.success());
+ let error = String::from_utf8_lossy(segfault.error.as_slice());
+ assert!(!error.as_slice().contains("has overflowed its stack"));
+ }
+}