mirror of
https://github.com/boostorg/fiber.git
synced 2026-07-21 13:13:32 +00:00
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:
@@ -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;
|
||||
|
||||
@@ -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>());
|
||||
}
|
||||
|
||||
}}
|
||||
|
||||
@@ -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
@@ -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
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user