Merge pull request #277 from Romain-Geissler-1A/develop

[RFC] Introduce boost::fibers::initialize_thread to be able to customize th…
This commit is contained in:
Oliver Kowalke
2023-10-01 17:08:04 +02:00
committed by GitHub
6 changed files with 171 additions and 26 deletions
+5
View File
@@ -45,6 +45,8 @@
#include <boost/fiber/segmented_stack.hpp>
#include <boost/fiber/type.hpp>
#include <boost/fiber/waker.hpp>
#include <boost/fiber/stack_allocator_wrapper.hpp>
#include <boost/fiber/algo/algorithm.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
@@ -223,6 +225,9 @@ public:
}
};
// Returns true if the thread could be initialize, false otherwise (it was already initialized previously).
static bool initialize_thread(algo::algorithm::ptr_t algo, stack_allocator_wrapper&& salloc) noexcept;
static context * active() noexcept;
static void reset_active() noexcept;
+7 -2
View File
@@ -16,6 +16,7 @@
#include <boost/fiber/detail/convert.hpp>
#include <boost/fiber/fiber.hpp>
#include <boost/fiber/scheduler.hpp>
#include <boost/fiber/stack_allocator_wrapper.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
@@ -75,10 +76,14 @@ bool has_ready_fibers() noexcept {
return boost::fibers::context::active()->get_scheduler()->has_ready_fibers();
}
// Returns true if the thread could be initialize, false otherwise (it was already initialized previously).
inline bool initialize_thread(algo::algorithm::ptr_t algo, stack_allocator_wrapper&& salloc) noexcept {
return boost::fibers::context::initialize_thread(algo, std::move(salloc));
}
template< typename SchedAlgo, typename ... Args >
void use_scheduling_algorithm( Args && ... args) noexcept {
boost::fibers::context::active()->get_scheduler()
->set_algo( new SchedAlgo( std::forward< Args >( args) ... ) );
initialize_thread(new SchedAlgo(std::forward< Args >( args) ... ), make_stack_allocator_wrapper<boost::fibers::default_stack>());
}
}}
+1 -1
View File
@@ -109,7 +109,7 @@ private:
void sleep2ready_() noexcept;
public:
scheduler() noexcept;
scheduler(algo::algorithm::ptr_t algo) noexcept;
scheduler( scheduler const&) = delete;
scheduler & operator=( scheduler const&) = delete;
@@ -0,0 +1,94 @@
#ifndef BOOST_FIBERS_STACK_ALLOCATOR_WRAPPER_H
#define BOOST_FIBERS_STACK_ALLOCATOR_WRAPPER_H
#include <memory>
#include <boost/fiber/detail/config.hpp>
#include <boost/context/stack_context.hpp>
#include <boost/fiber/fixedsize_stack.hpp>
#include <boost/fiber/segmented_stack.hpp>
namespace boost {
namespace fibers {
namespace detail {
class BOOST_FIBERS_DECL polymorphic_stack_allocator_base {
public:
polymorphic_stack_allocator_base() = default;
virtual ~polymorphic_stack_allocator_base() = default;
polymorphic_stack_allocator_base(const polymorphic_stack_allocator_base&) = delete;
polymorphic_stack_allocator_base& operator=(const polymorphic_stack_allocator_base&) = delete;
polymorphic_stack_allocator_base(polymorphic_stack_allocator_base&&) = delete;
polymorphic_stack_allocator_base& operator=(polymorphic_stack_allocator_base&&) = delete;
virtual boost::context::stack_context allocate() = 0;
virtual void deallocate(boost::context::stack_context& sctx) = 0;
};
template< typename StackAllocator >
class BOOST_FIBERS_DECL polymorphic_stack_allocator_impl final : public polymorphic_stack_allocator_base {
public:
template<typename ... Args >
polymorphic_stack_allocator_impl( Args && ... args )
:_allocator(std::forward< Args >( args) ... )
{}
~polymorphic_stack_allocator_impl() = default;
boost::context::stack_context allocate() override
{
return _allocator.allocate();
}
void deallocate(boost::context::stack_context& sctx) override
{
_allocator.deallocate(sctx);
}
private:
StackAllocator _allocator;
};
}
class BOOST_FIBERS_DECL stack_allocator_wrapper final {
public:
stack_allocator_wrapper(std::unique_ptr<detail::polymorphic_stack_allocator_base> allocator)
:_allocator(std::move(allocator))
{}
~stack_allocator_wrapper() = default;
stack_allocator_wrapper(const stack_allocator_wrapper&) = delete;
stack_allocator_wrapper& operator=(const stack_allocator_wrapper&) = delete;
stack_allocator_wrapper(stack_allocator_wrapper&&) = default;
stack_allocator_wrapper& operator=(stack_allocator_wrapper&&) = default;
boost::context::stack_context allocate()
{
return _allocator->allocate();
}
void deallocate(boost::context::stack_context& sctx)
{
_allocator->deallocate(sctx);
}
private:
std::unique_ptr<detail::polymorphic_stack_allocator_base> _allocator;
};
template <typename StackAllocator, typename ... Args>
BOOST_FIBERS_DECL stack_allocator_wrapper make_stack_allocator_wrapper(Args && ... args)
{
return stack_allocator_wrapper(
std::unique_ptr<detail::polymorphic_stack_allocator_base>(
new detail::polymorphic_stack_allocator_impl<StackAllocator>(std::forward< Args >( args) ... )));
}
}
}
#endif
+62 -20
View File
@@ -12,6 +12,7 @@
#include "boost/fiber/exceptions.hpp"
#include "boost/fiber/scheduler.hpp"
#include "boost/fiber/algo/round_robin.hpp"
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
@@ -41,9 +42,9 @@ private:
}
public:
dispatcher_context( boost::context::preallocated const& palloc, default_stack && salloc) :
dispatcher_context( boost::context::preallocated const& palloc, stack_allocator_wrapper&& salloc) :
context{ 0, type::dispatcher_context, launch::post } {
c_ = boost::context::fiber{ std::allocator_arg, palloc, salloc,
c_ = boost::context::fiber{ std::allocator_arg, palloc, std::move(salloc),
std::bind( & dispatcher_context::run_, this, std::placeholders::_1) };
#if (defined(BOOST_USE_UCONTEXT)||defined(BOOST_USE_WINFIB))
c_ = std::move( c_).resume();
@@ -51,8 +52,7 @@ public:
}
};
static intrusive_ptr< context > make_dispatcher_context() {
default_stack salloc; // use default satck-size
static intrusive_ptr< context > make_dispatcher_context(stack_allocator_wrapper&& salloc) {
auto sctx = salloc.allocate();
// reserve space for control structure
void * storage = reinterpret_cast< void * >(
@@ -72,36 +72,78 @@ struct context_initializer {
static thread_local context * active_;
static thread_local std::size_t counter_;
context_initializer() {
using default_scheduler = algo::round_robin;
template< typename ... Args >
context_initializer(Args && ... args) {
if ( 0 == counter_++) {
// main fiber context of this thread
context * main_ctx = new main_context{};
// scheduler of this thread
auto sched = new scheduler{};
// attach main context to scheduler
sched->attach_main_context( main_ctx);
// create and attach dispatcher context to scheduler
sched->attach_dispatcher_context( make_dispatcher_context() );
// make main context to active context
active_ = main_ctx;
initialize(std::forward< Args >( args) ... );
}
}
~context_initializer() {
if ( 0 == --counter_) {
context * main_ctx = active_;
BOOST_ASSERT( main_ctx->is_context( type::main_context) );
scheduler * sched = main_ctx->get_scheduler();
delete sched;
delete main_ctx;
deinitialize();
}
}
void initialize()
{
initialize(new default_scheduler(), make_stack_allocator_wrapper<default_stack>());
}
void initialize(algo::algorithm::ptr_t algo, stack_allocator_wrapper&& salloc)
{
// main fiber context of this thread
context * main_ctx = new main_context{};
// scheduler of this thread
auto sched = new scheduler(algo);
// attach main context to scheduler
sched->attach_main_context( main_ctx);
// create and attach dispatcher context to scheduler
sched->attach_dispatcher_context( make_dispatcher_context(std::move(salloc)) );
// make main context to active context
active_ = main_ctx;
}
void deinitialize()
{
context * main_ctx = active_;
BOOST_ASSERT( main_ctx->is_context( type::main_context) );
scheduler * sched = main_ctx->get_scheduler();
delete sched;
delete main_ctx;
}
};
// zero-initialization
thread_local context * context_initializer::active_{ nullptr };
thread_local std::size_t context_initializer::counter_{ 0 };
bool context::initialize_thread(algo::algorithm::ptr_t algo, stack_allocator_wrapper&& salloc) noexcept
{
if (context_initializer::counter_ == 0)
{
// Initilization is not done yet, so do it now with a local variable
// context_initializer which will decrease the counter when leaving this function.
context_initializer ctx_initializer(algo, std::move(salloc));
// Now call active() to register a thread local context_initializer which will
// ensure resources are free'ed when the thread exits.
active();
return true;
}
else
{
// It's too late already to initialize the dispatcher stack allocator, still we can update
// the algo.
active()->get_scheduler()->set_algo(algo);
return false;
}
}
context *
context::active() noexcept {
// initialized the first time control passes; per thread
+2 -3
View File
@@ -11,7 +11,6 @@
#include <boost/assert.hpp>
#include "boost/fiber/algo/round_robin.hpp"
#include "boost/fiber/context.hpp"
#include "boost/fiber/exceptions.hpp"
@@ -94,8 +93,8 @@ scheduler::sleep2ready_() noexcept {
}
}
scheduler::scheduler() noexcept :
algo_{ new algo::round_robin() } {
scheduler::scheduler(algo::algorithm::ptr_t algo) noexcept :
algo_{algo} {
}
scheduler::~scheduler() {