Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixing continuation recursions to not depend on fixed amount of recursions #1969

Merged
merged 3 commits into from Jan 28, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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
20 changes: 19 additions & 1 deletion hpx/lcos/detail/future_data.hpp
Expand Up @@ -357,9 +357,27 @@ 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
42 changes: 42 additions & 0 deletions hpx/util/coroutine/detail/get_stack_pointer.hpp
@@ -0,0 +1,42 @@
// 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>

#include <limits>

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