Skip to content

Commit

Permalink
Changing the coroutine implementations to do a lazy init
Browse files Browse the repository at this point in the history
This changes the the context implementations to perform a lazy intitialization
of their respective contexts. This potentially leads to less resource usage.
  • Loading branch information
Thomas Heller committed Feb 6, 2018
1 parent 4caaabc commit d562990
Show file tree
Hide file tree
Showing 9 changed files with 82 additions and 142 deletions.
11 changes: 6 additions & 5 deletions hpx/runtime/threads/coroutines/detail/context_base.hpp
Expand Up @@ -88,15 +88,15 @@ namespace hpx { namespace threads { namespace coroutines { namespace detail
/////////////////////////////////////////////////////////////////////////////
std::ptrdiff_t const default_stack_size = -1;

class context_base : public default_context_impl
template <typename CoroutineImpl>
class context_base : public default_context_impl<CoroutineImpl>
{
public:
typedef void deleter_type(context_base const*);
typedef hpx::threads::thread_id_type thread_id_type;

template <typename Derived>
context_base(Derived& derived, std::ptrdiff_t stack_size, thread_id_type id)
: default_context_impl(derived, stack_size),
context_base(std::ptrdiff_t stack_size, thread_id_type id)
: default_context_impl<CoroutineImpl>(stack_size),
m_caller(),
m_state(ctx_ready),
m_exit_state(ctx_exit_not_requested),
Expand Down Expand Up @@ -169,6 +169,7 @@ namespace hpx { namespace threads { namespace coroutines { namespace detail
// on return.
void invoke()
{
this->init();
HPX_ASSERT(is_ready());
do_invoke();
// TODO: could use a binary or here to eliminate
Expand Down Expand Up @@ -374,7 +375,7 @@ namespace hpx { namespace threads { namespace coroutines { namespace detail
swap_context(m_caller, *this, detail::invoke_hint());
}

typedef default_context_impl::context_impl_base ctx_type;
typedef typename default_context_impl<CoroutineImpl>::context_impl_base ctx_type;
ctx_type m_caller;

static HPX_EXPORT allocation_counters m_allocation_counters;
Expand Down
30 changes: 8 additions & 22 deletions hpx/runtime/threads/coroutines/detail/context_generic_context.hpp
Expand Up @@ -189,6 +189,7 @@ namespace hpx { namespace threads { namespace coroutines
}
#endif

template <typename CoroutineImpl>
class fcontext_context_impl
{
public:
Expand All @@ -197,29 +198,13 @@ namespace hpx { namespace threads { namespace coroutines
public:
typedef fcontext_context_impl context_impl_base;

fcontext_context_impl()
#if BOOST_VERSION < 106100
: cb_(0)
#else
: cb_(std::make_pair(nullptr, nullptr))
#endif
, funp_(0)
#if BOOST_VERSION > 105500
, ctx_(0)
#endif
, alloc_()
, stack_size_(0)
, stack_pointer_(0)
{}

// Create a context that on restore invokes Functor on
// a new stack. The stack size can be optionally specified.
template <typename Functor>
fcontext_context_impl(Functor& cb, std::ptrdiff_t stack_size)
explicit fcontext_context_impl(std::ptrdiff_t stack_size)
#if BOOST_VERSION < 106100
: cb_(reinterpret_cast<intptr_t>(&cb))
: cb_(reinterpret_cast<intptr_t>(this))
#else
: cb_(std::make_pair(reinterpret_cast<void*>(&cb), nullptr))
: cb_(std::make_pair(reinterpret_cast<void*>(this), nullptr))
#endif
, funp_(&trampoline<Functor>)
#if BOOST_VERSION > 105500
Expand All @@ -230,8 +215,11 @@ namespace hpx { namespace threads { namespace coroutines
(stack_size == -1) ?
alloc_.minimum_stacksize() : std::size_t(stack_size)
)
, stack_pointer_(alloc_.allocate(stack_size_))
{}

void init()
{
stack_pointer_ = alloc_.allocate(stack_size_);
#if BOOST_VERSION < 105600
boost::context::fcontext_t* ctx =
boost::context::make_fcontext(stack_pointer_, stack_size_, funp_);
Expand Down Expand Up @@ -339,8 +327,6 @@ namespace hpx { namespace threads { namespace coroutines
std::size_t stack_size_;
void * stack_pointer_;
};

typedef fcontext_context_impl context_impl;
}}
}}}

Expand Down
27 changes: 8 additions & 19 deletions hpx/runtime/threads/coroutines/detail/context_impl.hpp
Expand Up @@ -110,7 +110,8 @@
#include <hpx/runtime/threads/coroutines/detail/context_generic_context.hpp>
namespace hpx { namespace threads { namespace coroutines { namespace detail
{
typedef generic_context::context_impl default_context_impl;
template <typename CoroutineImpl>
using default_context_impl = generic_context::fcontext_context_impl<CoroutineImpl>;
}}}}

#elif (defined(__linux) || defined(linux) || defined(__linux__)) \
Expand All @@ -119,23 +120,26 @@ namespace hpx { namespace threads { namespace coroutines { namespace detail
#include <hpx/runtime/threads/coroutines/detail/context_linux_x86.hpp>
namespace hpx { namespace threads { namespace coroutines { namespace detail
{
typedef lx::context_impl default_context_impl;
template <typename CoroutineImpl>
using default_context_impl = lx::x86_linux_context_impl<CoroutineImpl>;
}}}}

#elif defined(_POSIX_VERSION) || defined(__bgq__) || defined(__powerpc__)

#include <hpx/runtime/threads/coroutines/detail/context_posix.hpp>
namespace hpx { namespace threads { namespace coroutines { namespace detail
{
typedef posix::context_impl default_context_impl;
template <typename CoroutineImpl>
using default_context_impl = posix::ucontext_context_impl<CoroutineImpl>;
}}}}

#elif defined(HPX_HAVE_FIBER_BASED_COROUTINES)

#include <hpx/runtime/threads/coroutines/detail/context_windows_fibers.hpp>
namespace hpx { namespace threads { namespace coroutines { namespace detail
{
typedef windows::context_impl default_context_impl;
template <typename CoroutineImpl>
using default_context_impl = windows::fibers_context_impl<CoroutineImpl>;
}}}}

#else
Expand All @@ -144,19 +148,4 @@ namespace hpx { namespace threads { namespace coroutines { namespace detail

#endif // HPX_HAVE_GENERIC_CONTEXT_COROUTINES

namespace hpx { namespace threads { namespace coroutines
{
// functions to be called for each thread after it started running
// and before it exits
inline void thread_startup(char const* thread_type)
{
detail::default_context_impl::thread_startup(thread_type);
}

inline void thread_shutdown()
{
detail::default_context_impl::thread_shutdown();
}
}}}

#endif /*HPX_RUNTIME_THREADS_COROUTINES_DETAIL_CONTEXT_IMPL_HPP*/
109 changes: 36 additions & 73 deletions hpx/runtime/threads/coroutines/detail/context_linux_x86.hpp
Expand Up @@ -93,16 +93,17 @@ namespace hpx { namespace threads { namespace coroutines
return u.t;
}

template<typename T>
HPX_FORCEINLINE void trampoline(T* fun);
// template<typename T>
// HPX_FORCEINLINE void trampoline(void* fun);

template<typename T>
void trampoline(T* fun)
void trampoline(void* fun)
{
(*fun)();
(*static_cast<T*>(fun))();
std::abort();
}

template <typename CoroutineImpl>
class x86_linux_context_impl;

class x86_linux_context_impl_base : detail::context_impl_base
Expand Down Expand Up @@ -147,48 +148,30 @@ namespace hpx { namespace threads { namespace coroutines
void ** m_sp;
};

template <typename CoroutineImpl>
class x86_linux_context_impl : public x86_linux_context_impl_base
{
public:
enum { default_stack_size = 4 * EXEC_PAGESIZE };

typedef x86_linux_context_impl_base context_impl_base;

x86_linux_context_impl()
: m_stack(nullptr)
{
#if defined(HPX_HAVE_STACKOVERFLOW_DETECTION)
// concept inspired by the following links:
//
// https://rethinkdb.com/blog/handling-stack-overflow-on-custom-stacks/
// http://www.evanjones.ca/software/threading.html
//
segv_stack.ss_sp = valloc(SEGV_STACK_SIZE);
segv_stack.ss_flags = 0;
segv_stack.ss_size = SEGV_STACK_SIZE;

std::memset(&action, '\0', sizeof(action));
action.sa_flags = SA_SIGINFO|SA_ONSTACK;
action.sa_sigaction = &x86_linux_context_impl::sigsegv_handler;

sigaltstack(&segv_stack, nullptr);
sigemptyset(&action.sa_mask);
sigaddset(&action.sa_mask, SIGSEGV);
sigaction(SIGSEGV, &action, nullptr);
#endif
}

/**
* Create a context that on restore invokes Functor on
* a new stack. The stack size can be optionally specified.
*/
template<typename Functor>
x86_linux_context_impl(Functor& cb, std::ptrdiff_t stack_size = -1)
explicit x86_linux_context_impl(std::ptrdiff_t stack_size = -1)
: m_stack_size(stack_size == -1
? static_cast<std::ptrdiff_t>(default_stack_size)
: stack_size),
m_stack(nullptr)
{
}

void init()
{
if (m_stack != nullptr) return;

if (0 != (m_stack_size % EXEC_PAGESIZE))
{
throw std::runtime_error(
Expand All @@ -208,15 +191,15 @@ namespace hpx { namespace threads { namespace coroutines
HPX_ASSERT(m_stack);
posix::watermark_stack(m_stack, static_cast<std::size_t>(m_stack_size));

typedef void fun(Functor*);
fun * funp = trampoline;
typedef void fun(void*);
fun * funp = trampoline<CoroutineImpl>;

m_sp = (static_cast<void**>(m_stack)
+ static_cast<std::size_t>(m_stack_size) / sizeof(void*))
- context_size;

m_sp[backup_cb_idx] = m_sp[cb_idx] = &cb;
m_sp[backup_funp_idx] = m_sp[funp_idx] = nasty_cast<void*>(funp);
m_sp[cb_idx] = this;
m_sp[funp_idx] = nasty_cast<void*>(funp);

#if defined(HPX_HAVE_VALGRIND) && !defined(NVALGRIND)
{
Expand Down Expand Up @@ -312,28 +295,26 @@ namespace hpx { namespace threads { namespace coroutines

void reset_stack()
{
if (m_stack)
{
if (posix::reset_stack(
m_stack, static_cast<std::size_t>(m_stack_size)))
increment_stack_unbind_count();
}
HPX_ASSERT(m_stack);
if (posix::reset_stack(
m_stack, static_cast<std::size_t>(m_stack_size)))
increment_stack_unbind_count();
}

void rebind_stack()
{
if (m_stack)
{
increment_stack_recycle_count();
HPX_ASSERT(m_stack);
increment_stack_recycle_count();

// On rebind, we initialize our stack to ensure a virgin stack
m_sp = (static_cast<void**>(m_stack)
+ static_cast<std::size_t>(m_stack_size) / sizeof(void*))
- context_size;
// On rebind, we initialize our stack to ensure a virgin stack
m_sp = (static_cast<void**>(m_stack)
+ static_cast<std::size_t>(m_stack_size) / sizeof(void*))
- context_size;

m_sp[cb_idx] = m_sp[backup_cb_idx];
m_sp[funp_idx] = m_sp[backup_funp_idx];
}
typedef void fun(void*);
fun * funp = trampoline<CoroutineImpl>;
m_sp[cb_idx] = this;
m_sp[funp_idx] = nasty_cast<void*>(funp);
}

std::ptrdiff_t get_available_stack_space()
Expand Down Expand Up @@ -382,19 +363,9 @@ namespace hpx { namespace threads { namespace coroutines
friend void swap_context(x86_linux_context_impl_base& from,
x86_linux_context_impl_base const& to, yield_hint);

// 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*/)
{}

static void thread_shutdown()
{}

private:
#if defined(__x86_64__)
/** structure of context_data:
* 13: backup address of function to execute
* 12: backup address of trampoline
* 11: additional alignment (or valgrind_id if enabled)
* 10: parm 0 of trampoline
* 9: dummy return address for trampoline
Expand All @@ -412,16 +383,12 @@ namespace hpx { namespace threads { namespace coroutines
static const std::size_t valgrind_id_idx = 11;
#endif

static const std::size_t context_size = 14;
static const std::size_t backup_cb_idx = 13;
static const std::size_t backup_funp_idx = 12;
static const std::size_t context_size = 12;
static const std::size_t cb_idx = 10;
static const std::size_t funp_idx = 8;
#else
/** structure of context_data:
* 9: valgrind_id (if enabled)
* 8: backup address of function to execute
* 7: backup address of trampoline
* 7: valgrind_id (if enabled)
* 6: parm 0 of trampoline
* 5: dummy return address for trampoline
* 4: return addr (here: start addr)
Expand All @@ -431,14 +398,12 @@ namespace hpx { namespace threads { namespace coroutines
* 0: edi
**/
#if defined(HPX_HAVE_VALGRIND) && !defined(NVALGRIND)
static const std::size_t context_size = 10;
static const std::size_t valgrind_id_idx = 9;
static const std::size_t context_size = 8;
static const std::size_t valgrind_id_idx = 7;
#else
static const std::size_t context_size = 9;
static const std::size_t context_size = 7;
#endif

static const std::size_t backup_cb_idx = 8;
static const std::size_t backup_funp_idx = 7;
static const std::size_t cb_idx = 6;
static const std::size_t funp_idx = 4;
#endif
Expand All @@ -452,8 +417,6 @@ namespace hpx { namespace threads { namespace coroutines
#endif
};

typedef x86_linux_context_impl context_impl;

/**
* Free function. Saves the current context in @p from
* and restores the context in @p to.
Expand Down
4 changes: 2 additions & 2 deletions hpx/runtime/threads/coroutines/detail/context_posix.hpp
Expand Up @@ -217,6 +217,7 @@ namespace hpx { namespace threads { namespace coroutines
HPX_COROUTINE_DECLARE_CONTEXT(m_ctx);
};

template <typename CoroutineImpl>
class ucontext_context_impl
: public ucontext_context_impl_base
{
Expand All @@ -232,8 +233,7 @@ namespace hpx { namespace threads { namespace coroutines
* Create a context that on restore invokes Functor on
* a new stack. The stack size can be optionally specified.
*/
template<typename Functor>
explicit ucontext_context_impl(Functor & cb, std::ptrdiff_t stack_size)
explicit ucontext_context_impl(std::ptrdiff_t stack_size)
: m_stack_size(stack_size == -1 ? (std::ptrdiff_t)default_stack_size
: stack_size),
m_stack(alloc_stack(m_stack_size)),
Expand Down

0 comments on commit d562990

Please sign in to comment.