diff --git a/hpx/config.hpp b/hpx/config.hpp index 45c12a612fce..c47d88606487 100644 --- a/hpx/config.hpp +++ b/hpx/config.hpp @@ -407,6 +407,27 @@ #endif /////////////////////////////////////////////////////////////////////////////// + +#if !defined(HPX_THREADS_STACK_OVERHEAD) +# if defined(HPX_DEBUG) +# define HPX_THREADS_STACK_OVERHEAD 0x2800 +# else +# if defined(HPX_INTEL_VERSION) +# define HPX_THREADS_STACK_OVERHEAD 0x2800 +# else +# define HPX_THREADS_STACK_OVERHEAD 0x800 +# endif +# endif +#endif + +#if !defined(HPX_SMALL_STACK_SIZE) +# if defined(__has_feature) +# if __has_feature(address_sanitizer) +# define HPX_SMALL_STACK_SIZE 0x20000 // 128kByte +# endif +# endif +#endif + #if !defined(HPX_SMALL_STACK_SIZE) # if defined(HPX_WINDOWS) && !defined(HPX_HAVE_GENERIC_CONTEXT_COROUTINES) # define HPX_SMALL_STACK_SIZE 0x4000 // 16kByte @@ -432,12 +453,14 @@ /////////////////////////////////////////////////////////////////////////////// // This limits how deep the internal recursion of future continuations will go // before a new operation is re-spawned. -#if defined(__has_feature) -#if __has_feature(address_sanitizer) +#if !defined(HPX_CONTINUATION_MAX_RECURSION_DEPTH) +# if defined(__has_feature) +# if __has_feature(address_sanitizer) // if we build under AddressSanitizer we set the max recursion depth to 1 to not // run into stack overflows. -#define HPX_CONTINUATION_MAX_RECURSION_DEPTH 1 -#endif +# define HPX_CONTINUATION_MAX_RECURSION_DEPTH 1 +# endif +# endif #endif #if !defined(HPX_CONTINUATION_MAX_RECURSION_DEPTH) diff --git a/hpx/lcos/detail/future_data.hpp b/hpx/lcos/detail/future_data.hpp index f66ac0926910..30283cf3657c 100644 --- a/hpx/lcos/detail/future_data.hpp +++ b/hpx/lcos/detail/future_data.hpp @@ -357,9 +357,24 @@ namespace detail // allowed void handle_on_completed(completed_callback_type && on_completed) { +#if defined(HPX_WINDOWS) + bool recurse_asynchronously = false; +#elif defined(HPX_HAVE_THREADS_GET_STACK_POINTER) + std::ptrdiff_t remaining_stack = this_thread::get_available_stack_space(); + + if(remaining_stack < 0) + { + HPX_THROW_EXCEPTION(out_of_memory, + "future_data::handle_on_completed", + "Stack overflow"); + } + bool recurse_asynchronously = remaining_stack < 8 * HPX_THREADS_STACK_OVERHEAD; +#else handle_continuation_recursion_count cnt; + bool recurse_asynchronously = cnt.count_ > HPX_CONTINUATION_MAX_RECURSION_DEPTH; +#endif - if (cnt.count_ <= HPX_CONTINUATION_MAX_RECURSION_DEPTH) + if (!recurse_asynchronously) { // directly execute continuation on this thread on_completed(); diff --git a/hpx/runtime/threads/thread_helpers.hpp b/hpx/runtime/threads/thread_helpers.hpp index cdf5df75be0f..12bde57a456e 100644 --- a/hpx/runtime/threads/thread_helpers.hpp +++ b/hpx/runtime/threads/thread_helpers.hpp @@ -545,6 +545,9 @@ namespace hpx { namespace this_thread /// HPX_EXPORT threads::executors::current_executor get_executor(error_code& ec = throws); + + // returns the remaining available stack space + HPX_EXPORT std::ptrdiff_t get_available_stack_space(); }} /////////////////////////////////////////////////////////////////////////////// diff --git a/hpx/util/coroutine/coroutine.hpp b/hpx/util/coroutine/coroutine.hpp index 1685121afe3c..7d85a06b7cd6 100644 --- a/hpx/util/coroutine/coroutine.hpp +++ b/hpx/util/coroutine/coroutine.hpp @@ -276,6 +276,15 @@ namespace hpx { namespace util { namespace coroutines return m_pimpl == 0; } + std::ptrdiff_t get_available_stack_space() + { +#if defined(HPX_HAVE_THREADS_GET_STACK_POINTER) + return m_pimpl->get_available_stack_space(); +#else + return (std::numeric_limits::max)(); +#endif + } + protected: // The second parameter is used to avoid calling this constructor // by mistake from other member functions (specifically operator=). diff --git a/hpx/util/coroutine/detail/context_generic_context.hpp b/hpx/util/coroutine/detail/context_generic_context.hpp index 38fba1e2cd4b..1640797d9948 100644 --- a/hpx/util/coroutine/detail/context_generic_context.hpp +++ b/hpx/util/coroutine/detail/context_generic_context.hpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -228,6 +229,16 @@ namespace hpx { namespace util { namespace coroutines return stack_size_; } + std::ptrdiff_t get_available_stack_space() + { +#if defined(HPX_HAVE_THREADS_GET_STACK_POINTER) + return + reinterpret_cast(stack_pointer_) - get_stack_ptr(); +#else + return (std::numeric_limits::max)(); +#endif + } + // global functions to be called for each OS-thread after it started // running and before it exits static void thread_startup(char const* thread_type) {} diff --git a/hpx/util/coroutine/detail/context_linux_x86.hpp b/hpx/util/coroutine/detail/context_linux_x86.hpp index 8a68ebc7cefd..2bfdb24e7d18 100644 --- a/hpx/util/coroutine/detail/context_linux_x86.hpp +++ b/hpx/util/coroutine/detail/context_linux_x86.hpp @@ -2,7 +2,7 @@ // Copyright (c) 2007 Robert Perricone // Copyright (c) 2007-2012 Hartmut Kaiser // Copyright (c) 2011 Bryce Adelstein-Lelbach -// Copyright (c) 2013 Thomas Heller +// Copyright (c) 2013-2016 Thomas Heller // // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -238,6 +239,12 @@ namespace hpx { namespace util { namespace coroutines } } + std::ptrdiff_t get_available_stack_space() + { + return + get_stack_ptr() - reinterpret_cast(m_stack) - context_size; + } + typedef boost::atomic counter_type; static counter_type& get_stack_unbind_counter() diff --git a/hpx/util/coroutine/detail/context_posix.hpp b/hpx/util/coroutine/detail/context_posix.hpp index 03e8f462b8c0..409abc95fdd7 100644 --- a/hpx/util/coroutine/detail/context_posix.hpp +++ b/hpx/util/coroutine/detail/context_posix.hpp @@ -145,6 +145,7 @@ namespace hpx { namespace util { namespace coroutines { namespace detail #include // SIGSTKSZ #include #include +#include #include #include @@ -237,6 +238,16 @@ namespace hpx { namespace util { namespace coroutines { return m_stack_size; } + std::ptrdiff_t get_available_stack_space() + { +#if defined(HPX_HAVE_THREADS_GET_STACK_POINTER) + return + get_stack_ptr() - reinterpret_cast(m_stack); +#else + return (std::numeric_limits::max)(); +#endif + } + // global functions to be called for each OS-thread after it started // running and before it exits static void thread_startup(char const* thread_type) {} diff --git a/hpx/util/coroutine/detail/get_stack_pointer.hpp b/hpx/util/coroutine/detail/get_stack_pointer.hpp new file mode 100644 index 000000000000..1283b726c00c --- /dev/null +++ b/hpx/util/coroutine/detail/get_stack_pointer.hpp @@ -0,0 +1,40 @@ +// Copyright (c) 2013-2016 Thomas Heller +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef HPX_COROUTINE_GET_STACK_POINTER_HPP +#define HPX_COROUTINE_GET_STACK_POINTER_HPP + +#if !defined(HPX_WINDOWS) +#if defined(__x86_64__) || defined(__amd64) \ + || defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__) \ + || defined(__powerpc__) \ + || defined(__arm__) +#define HPX_HAVE_THREADS_GET_STACK_POINTER +#endif + +#include + +namespace hpx { namespace util { namespace coroutines { namespace detail +{ + inline boost::uintptr_t get_stack_ptr() + { + boost::uintptr_t stack_ptr = (std::numeric_limits::max)(); +#if defined(__x86_64__) || defined(__amd64) + asm("movq %%rsp, %0" : "=r"(stack_ptr)); +#elif defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__) + asm("movl %%esp, %0" : "=r"(stack_ptr)); +#elif defined(__powerpc__) + std::size_t stack_ptr_p = &stack_ptr; + asm("stw %%r1, 0(%0)" : "=&r"(stack_ptr_p)); +#elif defined(__arm__) + asm("mov %0, sp" : "=r"(stack_ptr)); +#endif + return stack_ptr; + } +}}}} + +#endif +#endif diff --git a/hpx/util/coroutine/detail/self.hpp b/hpx/util/coroutine/detail/self.hpp index 3a42dbeb6878..7aea7615e208 100644 --- a/hpx/util/coroutine/detail/self.hpp +++ b/hpx/util/coroutine/detail/self.hpp @@ -160,6 +160,15 @@ namespace hpx { namespace util { namespace coroutines { namespace detail #endif } + std::ptrdiff_t get_available_stack_space() + { +#if defined(HPX_HAVE_THREADS_GET_STACK_POINTER) + return m_pimpl->get_available_stack_space(); +#else + return (std::numeric_limits::max)(); +#endif + } + explicit coroutine_self(impl_type * pimpl, coroutine_self* next_self = 0) : m_pimpl(pimpl), next_self_(next_self) {} diff --git a/src/runtime/threads/thread_helpers.cpp b/src/runtime/threads/thread_helpers.cpp index 5b8f53496a65..1c5250ab3faf 100644 --- a/src/runtime/threads/thread_helpers.cpp +++ b/src/runtime/threads/thread_helpers.cpp @@ -544,5 +544,16 @@ namespace hpx { namespace this_thread { return threads::get_executor(threads::get_self_id(), ec); } + + std::ptrdiff_t get_available_stack_space() + { + threads::thread_self *self = threads::get_self_ptr(); + if(self) + { + return self->get_available_stack_space(); + } + + return (std::numeric_limits::max)(); + } }} diff --git a/src/util/lightweight_test.cpp b/src/util/lightweight_test.cpp index fb962fbba140..b963fe0efea0 100644 --- a/src/util/lightweight_test.cpp +++ b/src/util/lightweight_test.cpp @@ -3,6 +3,8 @@ // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +#define HPX_NO_VERSION_CHECK + #include #include diff --git a/tests/regressions/lcos/dataflow_recursion_1613.cpp b/tests/regressions/lcos/dataflow_recursion_1613.cpp index 9153723540c6..dbbe25543159 100644 --- a/tests/regressions/lcos/dataflow_recursion_1613.cpp +++ b/tests/regressions/lcos/dataflow_recursion_1613.cpp @@ -13,7 +13,7 @@ #include -#define NUM_FUTURES std::size_t(2*HPX_CONTINUATION_MAX_RECURSION_DEPTH) +#define NUM_FUTURES std::size_t(300) // One way to force recursion is to make all futures depend on the next and // make the last of the futures ready, triggering a chain of continuations. diff --git a/tests/unit/threads/thread_stacksize.cpp b/tests/unit/threads/thread_stacksize.cpp index c9d34d393ea9..0a0696d9d567 100644 --- a/tests/unit/threads/thread_stacksize.cpp +++ b/tests/unit/threads/thread_stacksize.cpp @@ -9,18 +9,6 @@ #include #include -// Define the amount of stack space that we need to reserve for management -// purposes. -#if defined(HPX_DEBUG) - static const std::size_t management_space = 0x1600; -#else -#if defined(HPX_INTEL_VERSION) - static const std::size_t management_space = 0x2000; -#else - static const std::size_t management_space = 0x800; -#endif -#endif - /////////////////////////////////////////////////////////////////////////////// void test_small_stacksize() { @@ -30,8 +18,8 @@ void test_small_stacksize() hpx::get_runtime().get_config().get_stack_size( hpx::threads::thread_stacksize_small)); - // allocate HPX_SMALL_STACK_SIZE - management_space memory on the stack - char array[HPX_SMALL_STACK_SIZE-management_space]; + // allocate HPX_SMALL_STACK_SIZE - HPX_THREADS_STACK_OVERHEAD memory on the stack + char array[HPX_SMALL_STACK_SIZE-HPX_THREADS_STACK_OVERHEAD]; // do something to that array std::memset(array, '\0', sizeof(array)); @@ -48,8 +36,8 @@ void test_medium_stacksize() hpx::get_runtime().get_config().get_stack_size( hpx::threads::thread_stacksize_medium)); - // allocate HPX_MEDIUM_STACK_SIZE - management_space memory on the stack - char array[HPX_MEDIUM_STACK_SIZE-management_space]; + // allocate HPX_MEDIUM_STACK_SIZE - HPX_THREADS_STACK_OVERHEAD memory on the stack + char array[HPX_MEDIUM_STACK_SIZE-HPX_THREADS_STACK_OVERHEAD]; // do something to that array std::memset(array, '\0', sizeof(array)); @@ -66,8 +54,8 @@ void test_large_stacksize() hpx::get_runtime().get_config().get_stack_size( hpx::threads::thread_stacksize_large)); - // allocate HPX_LARGE_STACK_SIZE - management_space memory on the stack - char array[HPX_LARGE_STACK_SIZE-management_space]; + // allocate HPX_LARGE_STACK_SIZE - HPX_THREADS_STACK_OVERHEAD memory on the stack + char array[HPX_LARGE_STACK_SIZE-HPX_THREADS_STACK_OVERHEAD]; // do something to that array std::memset(array, '\0', sizeof(array)); @@ -84,8 +72,8 @@ void test_huge_stacksize() hpx::get_runtime().get_config().get_stack_size( hpx::threads::thread_stacksize_huge)); - // allocate HPX_LARGE_STACK_SIZE - management_space memory on the stack - char array[HPX_HUGE_STACK_SIZE-management_space]; + // allocate HPX_LARGE_STACK_SIZE - HPX_THREADS_STACK_OVERHEAD memory on the stack + char array[HPX_HUGE_STACK_SIZE-HPX_THREADS_STACK_OVERHEAD]; // do something to that array std::memset(array, '\0', sizeof(array));