Skip to content

Commit

Permalink
Fixing continuation recursions to not depend on fixed amount of recur…
Browse files Browse the repository at this point in the history
…sions

 - Disabling the continuation recursion counter on windows because it is not
   needed there (stack grows dynamically)
 - Adding function to retrieve the current value of the stack pointers on
   supported platforms (x86, x86_64, arm and powerpc)
 - When running the completion handlers, check for available stack base
   to decide wether to recurse asynchronously or not
 - Fallback to continuation recursion count for platforms where we can't retrieve
   the stack pointer
  • Loading branch information
sithhell committed Jan 27, 2016
1 parent 29695d9 commit 64f6c3f
Show file tree
Hide file tree
Showing 13 changed files with 156 additions and 27 deletions.
31 changes: 27 additions & 4 deletions hpx/config.hpp
Expand Up @@ -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
Expand All @@ -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)
Expand Down
17 changes: 16 additions & 1 deletion hpx/lcos/detail/future_data.hpp
Expand Up @@ -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();
Expand Down
3 changes: 3 additions & 0 deletions hpx/runtime/threads/thread_helpers.hpp
Expand Up @@ -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();
}}

///////////////////////////////////////////////////////////////////////////////
Expand Down
9 changes: 9 additions & 0 deletions hpx/util/coroutine/coroutine.hpp
Expand Up @@ -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<std::ptrdiff_t>::max)();
#endif
}

protected:
// The second parameter is used to avoid calling this constructor
// by mistake from other member functions (specifically operator=).
Expand Down
11 changes: 11 additions & 0 deletions hpx/util/coroutine/detail/context_generic_context.hpp
Expand Up @@ -13,6 +13,7 @@
#include <hpx/config/forceinline.hpp>
#include <hpx/util/assert.hpp>
#include <hpx/util/coroutine/detail/config.hpp>
#include <hpx/util/coroutine/detail/get_stack_pointer.hpp>
#include <hpx/util/coroutine/exception.hpp>
#include <hpx/util/coroutine/detail/swap_context.hpp>

Expand Down Expand Up @@ -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<boost::uintptr_t>(stack_pointer_) - get_stack_ptr();
#else
return (std::numeric_limits<std::ptrdiff_t>::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) {}
Expand Down
9 changes: 8 additions & 1 deletion hpx/util/coroutine/detail/context_linux_x86.hpp
Expand Up @@ -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
Expand All @@ -18,6 +18,7 @@
#include <hpx/util/coroutine/detail/config.hpp>
#include <hpx/util/coroutine/detail/posix_utility.hpp>
#include <hpx/util/coroutine/detail/swap_context.hpp>
#include <hpx/util/coroutine/detail/get_stack_pointer.hpp>
#include <hpx/util/get_and_reset_value.hpp>

#include <sys/param.h>
Expand Down Expand Up @@ -238,6 +239,12 @@ namespace hpx { namespace util { namespace coroutines
}
}

std::ptrdiff_t get_available_stack_space()
{
return
get_stack_ptr() - reinterpret_cast<boost::uintptr_t>(m_stack) - context_size;
}

typedef boost::atomic<boost::int64_t> counter_type;

static counter_type& get_stack_unbind_counter()
Expand Down
11 changes: 11 additions & 0 deletions hpx/util/coroutine/detail/context_posix.hpp
Expand Up @@ -145,6 +145,7 @@ namespace hpx { namespace util { namespace coroutines { namespace detail
#include <signal.h> // SIGSTKSZ
#include <boost/noncopyable.hpp>
#include <hpx/util/coroutine/exception.hpp>
#include <hpx/util/coroutine/detail/get_stack_pointer.hpp>
#include <hpx/util/coroutine/detail/posix_utility.hpp>
#include <hpx/util/coroutine/detail/swap_context.hpp>

Expand Down Expand Up @@ -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<boost::uintptr_t>(m_stack);
#else
return (std::numeric_limits<std::ptrdiff_t>::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) {}
Expand Down
40 changes: 40 additions & 0 deletions 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 <boost/cstdint.hpp>

namespace hpx { namespace util { namespace coroutines { namespace detail
{
inline boost::uintptr_t get_stack_ptr()
{
boost::uintptr_t stack_ptr = (std::numeric_limits<boost::uintptr_t>::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
9 changes: 9 additions & 0 deletions hpx/util/coroutine/detail/self.hpp
Expand Up @@ -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<std::ptrdiff_t>::max)();
#endif
}

explicit coroutine_self(impl_type * pimpl, coroutine_self* next_self = 0)
: m_pimpl(pimpl), next_self_(next_self)
{}
Expand Down
11 changes: 11 additions & 0 deletions src/runtime/threads/thread_helpers.cpp
Expand Up @@ -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<std::ptrdiff_t>::max)();
}
}}

2 changes: 2 additions & 0 deletions src/util/lightweight_test.cpp
Expand Up @@ -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 <hpx/util/lightweight_test.hpp>
#include <iostream>

Expand Down
2 changes: 1 addition & 1 deletion tests/regressions/lcos/dataflow_recursion_1613.cpp
Expand Up @@ -13,7 +13,7 @@

#include <boost/atomic.hpp>

#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.
Expand Down
28 changes: 8 additions & 20 deletions tests/unit/threads/thread_stacksize.cpp
Expand Up @@ -9,18 +9,6 @@
#include <hpx/runtime/threads/thread_data.hpp>
#include <hpx/util/lightweight_test.hpp>

// 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()
{
Expand All @@ -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));
Expand All @@ -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));
Expand All @@ -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));
Expand All @@ -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));
Expand Down

0 comments on commit 64f6c3f

Please sign in to comment.