From 7e183329eeab96c8172df92bb96b86968ec618ab Mon Sep 17 00:00:00 2001 From: Oliver Kowalke Date: Fri, 5 Feb 2016 18:14:38 +0100 Subject: [PATCH] use captured_context aonly --- include/boost/fiber/context.hpp | 52 ++++++++++++++++--------- include/boost/fiber/detail/wrap.hpp | 20 ++++------ include/boost/fiber/scheduler.hpp | 5 ++- performance/fiber/Jamfile.v2 | 24 +++++------- performance/fiber/overhead_create.cpp | 22 ++++------- performance/fiber/overhead_detach.cpp | 43 ++++++++------------ performance/fiber/overhead_join.cpp | 43 ++++++++------------ performance/fiber/overhead_switch.cpp | 56 --------------------------- performance/fiber/overhead_yield.cpp | 56 ++++++++++++--------------- src/context.cpp | 49 ++++++++++++++--------- src/scheduler.cpp | 19 +++++++-- 11 files changed, 164 insertions(+), 225 deletions(-) delete mode 100644 performance/fiber/overhead_switch.cpp diff --git a/include/boost/fiber/context.hpp b/include/boost/fiber/context.hpp index bb3ebfa0..da9798f4 100644 --- a/include/boost/fiber/context.hpp +++ b/include/boost/fiber/context.hpp @@ -18,7 +18,7 @@ #include #include #include -#include +#include #include #include #include @@ -160,15 +160,22 @@ private: struct data_t { detail::spinlock_lock * lk{ nullptr }; context * ctx{ nullptr }; + context * from; - constexpr data_t() noexcept = default; - - explicit data_t( detail::spinlock_lock * lk_) noexcept : - lk{ lk_ } { + explicit data_t( context * from_) noexcept : + from{ from_ } { } - explicit data_t( context * ctx_) noexcept : - ctx{ ctx_ } { + explicit data_t( detail::spinlock_lock * lk_, + context * from_) noexcept : + lk{ lk_ }, + from{ from_ } { + } + + explicit data_t( context * ctx_, + context * from_) noexcept : + ctx{ ctx_ }, + from{ from_ } { } }; @@ -184,16 +191,19 @@ private: int flags_; #endif scheduler * scheduler_{ nullptr }; - boost::context::execution_context ctx_; + boost::context::captured_context ctx_; void resume_( data_t &) noexcept; void set_ready_( context *) noexcept; template< typename Fn, typename Tpl > - void run_( Fn && fn_, Tpl && tpl_, data_t * dp) noexcept { + boost::context::captured_context + run_( boost::context::captured_context ctx, Fn && fn_, Tpl && tpl_, data_t * dp) noexcept { try { typename std::decay< Fn >::type fn = std::forward< Fn >( fn_); typename std::decay< Tpl >::type tpl = std::forward< Tpl >( tpl_); + // update captured_context of calling fiber + dp->from->ctx_ = std::move( ctx); if ( nullptr != dp->lk) { dp->lk->unlock(); } else if ( nullptr != dp->ctx) { @@ -203,8 +213,7 @@ private: } catch ( fiber_interrupted const&) { } // terminate context - terminate(); - BOOST_ASSERT_MSG( false, "fiber already terminated"); + return set_terminated(); } public: @@ -307,17 +316,17 @@ public: ctx_{ std::allocator_arg, palloc, salloc, detail::wrap( [this]( typename std::decay< Fn >::type & fn, typename std::decay< Tpl >::type & tpl, - boost::context::execution_context & ctx, void * vp) mutable noexcept { - run_( std::move( fn), std::move( tpl), static_cast< data_t * >( vp) ); + boost::context::captured_context ctx, void * vp) mutable noexcept { + return run_( std::move( ctx), std::move( fn), std::move( tpl), static_cast< data_t * >( vp) ); }, std::forward< Fn >( fn), - std::forward< Tpl >( tpl), - boost::context::execution_context::current())} + std::forward< Tpl >( tpl) )} + #else ctx_{ std::allocator_arg, palloc, salloc, - [this,fn=detail::decay_copy( std::forward< Fn >( fn) ),tpl=std::forward< Tpl >( tpl), - ctx=boost::context::execution_context::current()] (void * vp) mutable noexcept { - run_( std::move( fn), std::move( tpl), static_cast< data_t * >( vp) ); + [this,fn=detail::decay_copy( std::forward< Fn >( fn) ),tpl=std::forward< Tpl >( tpl)] + (boost::context::captured_context ctx, void * vp) mutable noexcept { + return run_( std::move( ctx), std::move( fn), std::move( tpl), static_cast< data_t * >( vp) ); }} #endif {} @@ -337,12 +346,13 @@ public: void suspend() noexcept; void suspend( detail::spinlock_lock &) noexcept; + boost::context::captured_context suspend_with_cc() noexcept; void join(); void yield() noexcept; - void terminate() noexcept; + boost::context::captured_context set_terminated() noexcept; bool wait_until( std::chrono::steady_clock::time_point const&) noexcept; bool wait_until( std::chrono::steady_clock::time_point const&, @@ -462,7 +472,11 @@ public: friend void intrusive_ptr_release( context * ctx) noexcept { BOOST_ASSERT( nullptr != ctx); if ( 0 == --ctx->use_count_) { + boost::context::captured_context cc( std::move( ctx->ctx_) ); + // destruct context ctx->~context(); + // deallocated stack + cc(); } } }; diff --git a/include/boost/fiber/detail/wrap.hpp b/include/boost/fiber/detail/wrap.hpp index 22b0ac62..5c9aa18d 100644 --- a/include/boost/fiber/detail/wrap.hpp +++ b/include/boost/fiber/detail/wrap.hpp @@ -29,15 +29,12 @@ private: typename std::decay< Fn1 >::type fn1_; typename std::decay< Fn2 >::type fn2_; typename std::decay< Tpl >::type tpl_; - boost::context::execution_context ctx_; public: - wrapper( Fn1 && fn1, Fn2 && fn2, Tpl && tpl, - boost::context::execution_context const& ctx) : + wrapper( Fn1 && fn1, Fn2 && fn2, Tpl && tpl) : fn1_( std::move( fn1) ), fn2_( std::move( fn2) ), - tpl_( std::move( tpl) ), - ctx_{ ctx } { + tpl_( std::move( tpl) ) { } wrapper( wrapper const&) = delete; @@ -46,22 +43,21 @@ public: wrapper( wrapper && other) = default; wrapper & operator=( wrapper && other) = default; - void operator()( void * vp) { - boost::context::detail::invoke( + boost::context::captured_context + operator()( boost::context::captured_context ctx, void * vp) { + return boost::context::detail::invoke( std::move( fn1_), - fn2_, tpl_, ctx_, vp); + fn2_, tpl_, std::move( ctx), vp); } }; template< typename Fn1, typename Fn2, typename Tpl > wrapper< Fn1, Fn2, Tpl > -wrap( Fn1 && fn1, Fn2 && fn2, Tpl && tpl, - boost::context::execution_context const& ctx) { +wrap( Fn1 && fn1, Fn2 && fn2, Tpl && tpl) { return wrapper< Fn1, Fn2, Tpl >( std::forward< Fn1 >( fn1), std::forward< Fn2 >( fn2), - std::forward< Tpl >( tpl), - ctx); + std::forward< Tpl >( tpl) ); } }}} diff --git a/include/boost/fiber/scheduler.hpp b/include/boost/fiber/scheduler.hpp index 5fb78376..892a8131 100644 --- a/include/boost/fiber/scheduler.hpp +++ b/include/boost/fiber/scheduler.hpp @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -103,13 +104,13 @@ public: virtual ~scheduler(); - void dispatch() noexcept; + boost::context::captured_context dispatch() noexcept; void set_ready( context *) noexcept; void set_remote_ready( context *) noexcept; - void set_terminated( context *) noexcept; + boost::context::captured_context set_terminated( context *) noexcept; void yield( context *) noexcept; diff --git a/performance/fiber/Jamfile.v2 b/performance/fiber/Jamfile.v2 index 09de79cf..3cbe7116 100644 --- a/performance/fiber/Jamfile.v2 +++ b/performance/fiber/Jamfile.v2 @@ -15,9 +15,7 @@ import toolset ; project boost/fiber/performance/fiber : requirements - /boost/chrono//boost_chrono /boost/fiber//boost_fiber - /boost/program_options//boost_program_options gcc,on:-fsplit-stack gcc,on:-DBOOST_USE_SEGMENTED_STACKS clang,on:-fsplit-stack @@ -29,29 +27,25 @@ project boost/fiber/performance/fiber release ; -exe overhead_switch - : overhead_switch.cpp - ; - exe overhead_create : overhead_create.cpp ; exe overhead_join - : overhead_join.cpp - ; - -exe overhead_detach - : overhead_detach.cpp + : overhead_join.cpp ; +exe overhead_detach + : overhead_detach.cpp + ; + exe overhead_yield - : overhead_yield.cpp - ; + : overhead_yield.cpp + ; exe overhead_future - : overhead_future.cpp - ; + : overhead_future.cpp + ; #exe scale_join # : scale_join.cpp diff --git a/performance/fiber/overhead_create.cpp b/performance/fiber/overhead_create.cpp index 2d7eebe4..505e9c94 100644 --- a/performance/fiber/overhead_create.cpp +++ b/performance/fiber/overhead_create.cpp @@ -30,32 +30,24 @@ void worker() {} -duration_type measure( duration_type overhead) -{ +duration_type measure( duration_type overhead) { boost::fibers::fiber( worker).join(); - duration_type result = duration_type::zero(); - BOOST_PP_REPEAT_FROM_TO(1, JOBS, JOIN, _) - result /= JOBS; // loops - return result; } -int main( int argc, char * argv[]) -{ - try - { +int main( int argc, char * argv[]) { + try { duration_type overhead = overhead_clock(); boost::uint64_t res = measure( overhead).count(); std::cout << "average of " << res << " nano seconds" << std::endl; - return EXIT_SUCCESS; + } catch ( std::exception const& e) { + std::cerr << "exception: " << e.what() << std::endl; + } catch (...) { + std::cerr << "unhandled exception" << std::endl; } - catch ( std::exception const& e) - { std::cerr << "exception: " << e.what() << std::endl; } - catch (...) - { std::cerr << "unhandled exception" << std::endl; } return EXIT_FAILURE; } diff --git a/performance/fiber/overhead_detach.cpp b/performance/fiber/overhead_detach.cpp index 8543f56f..caf4f880 100644 --- a/performance/fiber/overhead_detach.cpp +++ b/performance/fiber/overhead_detach.cpp @@ -18,44 +18,35 @@ #define JOBS BOOST_PP_LIMIT_REPEAT #endif +#define CREATE(z, n, _) \ + boost::fibers::fiber BOOST_PP_CAT(f,n) (worker); + #define DETACH(z, n, _) \ -{ \ - boost::fibers::fiber f( worker); \ - time_point_type start( clock_type::now() ); \ - f.detach(); \ - duration_type total = clock_type::now() - start; \ - total -= overhead; \ - result += total; \ -} + BOOST_PP_CAT(f,n) .detach(); void worker() {} -duration_type measure( duration_type overhead) -{ +duration_type measure( duration_type overhead) { boost::fibers::fiber( worker).join(); - - duration_type result = duration_type::zero(); - - BOOST_PP_REPEAT_FROM_TO(1, JOBS, DETACH, _) - - result /= JOBS; // loops - + BOOST_PP_REPEAT_FROM_TO(1, JOBS, CREATE, _) + time_point_type start( clock_type::now() ); + BOOST_PP_REPEAT_FROM_TO(1, JOBS, DETACH, _); + duration_type result = clock_type::now() - start; + result -= overhead; + result /= JOBS; // joined fibers return result; } -int main( int argc, char * argv[]) -{ - try - { +int main( int argc, char * argv[]) { + try { duration_type overhead = overhead_clock(); boost::uint64_t res = measure( overhead).count(); std::cout << "average of " << res << " nano seconds" << std::endl; - return EXIT_SUCCESS; + } catch ( std::exception const& e) { + std::cerr << "exception: " << e.what() << std::endl; + } catch (...) { + std::cerr << "unhandled exception" << std::endl; } - catch ( std::exception const& e) - { std::cerr << "exception: " << e.what() << std::endl; } - catch (...) - { std::cerr << "unhandled exception" << std::endl; } return EXIT_FAILURE; } diff --git a/performance/fiber/overhead_join.cpp b/performance/fiber/overhead_join.cpp index 0be0ff80..ed014aeb 100644 --- a/performance/fiber/overhead_join.cpp +++ b/performance/fiber/overhead_join.cpp @@ -18,44 +18,35 @@ #define JOBS BOOST_PP_LIMIT_REPEAT #endif +#define CREATE(z, n, _) \ + boost::fibers::fiber BOOST_PP_CAT(f,n) (worker); + #define JOIN(z, n, _) \ -{ \ - boost::fibers::fiber f( worker); \ - time_point_type start( clock_type::now() ); \ - f.join(); \ - duration_type total = clock_type::now() - start; \ - total -= overhead; \ - result += total; \ -} + BOOST_PP_CAT(f,n) .join(); void worker() {} -duration_type measure( duration_type overhead) -{ +duration_type measure( duration_type overhead) { boost::fibers::fiber( worker).join(); - - duration_type result = duration_type::zero(); - - BOOST_PP_REPEAT_FROM_TO(1, JOBS, JOIN, _) - - result /= JOBS; // loops - + BOOST_PP_REPEAT_FROM_TO(1, JOBS, CREATE, _) + time_point_type start( clock_type::now() ); + BOOST_PP_REPEAT_FROM_TO(1, JOBS, JOIN, _); + duration_type result = clock_type::now() - start; + result -= overhead; + result /= JOBS; // joined fibers return result; } -int main( int argc, char * argv[]) -{ - try - { +int main( int argc, char * argv[]) { + try { duration_type overhead = overhead_clock(); boost::uint64_t res = measure( overhead).count(); std::cout << "average of " << res << " nano seconds" << std::endl; - return EXIT_SUCCESS; + } catch ( std::exception const& e) { + std::cerr << "exception: " << e.what() << std::endl; + } catch (...) { + std::cerr << "unhandled exception" << std::endl; } - catch ( std::exception const& e) - { std::cerr << "exception: " << e.what() << std::endl; } - catch (...) - { std::cerr << "unhandled exception" << std::endl; } return EXIT_FAILURE; } diff --git a/performance/fiber/overhead_switch.cpp b/performance/fiber/overhead_switch.cpp deleted file mode 100644 index 7f8ac604..00000000 --- a/performance/fiber/overhead_switch.cpp +++ /dev/null @@ -1,56 +0,0 @@ - -// Copyright Oliver Kowalke 2009. -// 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) - -#include -#include -#include - -#include -#include -#include - -#include "../clock.hpp" - -#ifndef JOBS -#define JOBS BOOST_PP_LIMIT_REPEAT -#endif - -#define CREATE(z, n, _) \ - boost::fibers::fiber BOOST_PP_CAT(f,n) ( \ - [&total,&overhead](){ \ - time_point_type start( clock_type::now() ); \ - boost::this_fiber::yield(); \ - duration_type diff = clock_type::now() - start; \ - diff -= overhead; \ - total /= JOBS + 2; /* main-fiber and dispatcher-fiber */\ - total += diff; \ - }); - -#define JOIN(z, n, _) \ - BOOST_PP_CAT(f,n) .join(); - -duration_type measure( duration_type overhead) { - duration_type total = duration_type::zero(); - BOOST_PP_REPEAT_FROM_TO(1, JOBS, CREATE, _) - BOOST_PP_REPEAT_FROM_TO(1, JOBS, JOIN, _); - total /= JOBS; // loops - return total; -} - -int main( int argc, char * argv[]) { - try { - duration_type overhead = overhead_clock(); - boost::uint64_t res = measure( overhead).count(); - std::cout << "average of " << res << " nano seconds" << std::endl; - - return EXIT_SUCCESS; - } catch ( std::exception const& e) { - std::cerr << "exception: " << e.what() << std::endl; - } catch (...) { - std::cerr << "unhandled exception" << std::endl; - } - return EXIT_FAILURE; -} diff --git a/performance/fiber/overhead_yield.cpp b/performance/fiber/overhead_yield.cpp index e748a808..8eefcb15 100644 --- a/performance/fiber/overhead_yield.cpp +++ b/performance/fiber/overhead_yield.cpp @@ -18,46 +18,38 @@ #define JOBS BOOST_PP_LIMIT_REPEAT #endif +#define CREATE(z, n, _) \ + boost::fibers::fiber BOOST_PP_CAT(f,n) ( \ + [&total,&overhead](){ \ + time_point_type start( clock_type::now() ); \ + boost::this_fiber::yield(); \ + duration_type diff = clock_type::now() - start; \ + diff -= overhead; \ + total += diff; \ + }); + #define JOIN(z, n, _) \ - boost::fibers::fiber( worker, overhead, & result).join(); + BOOST_PP_CAT(f,n) .join(); -void worker( duration_type overhead, duration_type * result) -{ - time_point_type start( clock_type::now() ); - boost::this_fiber::yield(); - duration_type total = clock_type::now() - start; - total -= overhead; - * result += total; +duration_type measure( duration_type overhead) { + duration_type total = duration_type::zero(); + BOOST_PP_REPEAT_FROM_TO(1, JOBS, CREATE, _) + BOOST_PP_REPEAT_FROM_TO(1, JOBS, JOIN, _); + total /= JOBS; // JOBS switches for return from this_fiber::yield() + total /= JOBS; // measurement of JOBS fibers + return total; } -duration_type measure( duration_type overhead) -{ - duration_type result = duration_type::zero(); - - boost::fibers::fiber( worker, overhead, & result).join(); - - result = duration_type::zero(); - - BOOST_PP_REPEAT_FROM_TO(1, JOBS, JOIN, _) - - result /= JOBS; // loops - - return result; -} - -int main( int argc, char * argv[]) -{ - try - { +int main( int argc, char * argv[]) { + try { duration_type overhead = overhead_clock(); boost::uint64_t res = measure( overhead).count(); std::cout << "average of " << res << " nano seconds" << std::endl; - return EXIT_SUCCESS; + } catch ( std::exception const& e) { + std::cerr << "exception: " << e.what() << std::endl; + } catch (...) { + std::cerr << "unhandled exception" << std::endl; } - catch ( std::exception const& e) - { std::cerr << "exception: " << e.what() << std::endl; } - catch (...) - { std::cerr << "unhandled exception" << std::endl; } return EXIT_FAILURE; } diff --git a/src/context.cpp b/src/context.cpp index 63b1db1b..eb5dcfce 100644 --- a/src/context.cpp +++ b/src/context.cpp @@ -134,8 +134,6 @@ context_initializer::~context_initializer() { context * context::active() noexcept { - // initialized the first time control passes; per thread - thread_local static boost::context::detail::activation_record_initializer rec_initializer; // initialized the first time control passes; per thread thread_local static context_initializer ctx_initializer; return active_; @@ -148,11 +146,15 @@ context::reset_active() noexcept { void context::resume_( data_t & d) noexcept { - data_t * dp = static_cast< data_t * >( ctx_( & d) ); - if ( nullptr != dp->lk) { - dp->lk->unlock(); - } else if ( nullptr != dp->ctx) { - active_->set_ready_( dp->ctx); + auto result = ctx_( & d); + data_t * dp( static_cast< data_t * >( std::get< 1 >( result) ) ); + if ( nullptr != dp) { + dp->from->ctx_ = std::move( std::get< 0 >( result) ); + if ( nullptr != dp->lk) { + dp->lk->unlock(); + } else if ( nullptr != dp->ctx) { + active_->set_ready_( dp->ctx); + } } } @@ -165,7 +167,7 @@ context::set_ready_( context * ctx) noexcept { context::context( main_context_t) noexcept : use_count_{ 1 }, // allocated on main- or thread-stack flags_{ flag_main_context }, - ctx_{ boost::context::execution_context::current() } { + ctx_{} { } // dispatcher fiber context @@ -173,17 +175,17 @@ context::context( dispatcher_context_t, boost::context::preallocated const& pall default_stack const& salloc, scheduler * sched) : flags_{ flag_dispatcher_context }, ctx_{ std::allocator_arg, palloc, salloc, - [this,sched] (void * vp) noexcept { + [this,sched] ( boost::context::captured_context ctx, void * vp) noexcept { data_t * dp = static_cast< data_t * >( vp); + // update captured_context of calling fiber + dp->from->ctx_ = std::move( ctx); if ( nullptr != dp->lk) { dp->lk->unlock(); } else if ( nullptr != dp->ctx) { active_->set_ready_( dp->ctx); } // execute scheduler::dispatch() - sched->dispatch(); - // dispatcher context should never return from scheduler::dispatch() - BOOST_ASSERT_MSG( false, "disatcher fiber already terminated"); + return sched->dispatch(); }} { } @@ -205,13 +207,24 @@ context::get_id() const noexcept { return id( const_cast< context * >( this) ); } +boost::context::captured_context +context::suspend_with_cc() noexcept { + context * prev = this; + // active_ will point to `this` + // prev will point to previous active context + std::swap( active_, prev); + data_t d{ prev }; + // context switch + return std::move( std::get< 0 >( ctx_( & d) ) ); +} + void context::resume() noexcept { context * prev = this; // active_ will point to `this` // prev will point to previous active context std::swap( active_, prev); - data_t d{}; + data_t d{ prev }; resume_( d); } @@ -221,7 +234,7 @@ context::resume( detail::spinlock_lock & lk) noexcept { // active_ will point to `this` // prev will point to previous active context std::swap( active_, prev); - data_t d{ & lk }; + data_t d{ & lk, prev }; resume_( d); } @@ -231,7 +244,7 @@ context::resume( context * ready_ctx) noexcept { // active_ will point to `this` // prev will point to previous active context std::swap( active_, prev); - data_t d{ ready_ctx }; + data_t d{ ready_ctx, prev }; resume_( d); } @@ -277,8 +290,8 @@ context::yield() noexcept { scheduler_->yield( context::active() ); } -void -context::terminate() noexcept { +boost::context::captured_context +context::set_terminated() noexcept { // protect for concurrent access std::unique_lock< detail::spinlock > lk( splk_); // mark as terminated @@ -298,7 +311,7 @@ context::terminate() noexcept { } fss_data_.clear(); // switch to another context - scheduler_->set_terminated( this); + return scheduler_->set_terminated( this); } bool diff --git a/src/scheduler.cpp b/src/scheduler.cpp index 055e8586..84cc054f 100644 --- a/src/scheduler.cpp +++ b/src/scheduler.cpp @@ -22,6 +22,17 @@ namespace boost { namespace fibers { +boost::context::captured_context +suspend_with_cc( context * active_ctx, context * ctx) { + BOOST_ASSERT( nullptr != active_ctx); + BOOST_ASSERT( nullptr != ctx); + //BOOST_ASSERT( main_ctx_ == active_ctx || dispatcher_ctx_.get() == active_ctx || active_ctx->worker_is_linked() ); + BOOST_ASSERT( active_ctx->get_scheduler() == ctx->get_scheduler() ); + BOOST_ASSERT( active_ctx != ctx); + // resume active-fiber == ctx + return ctx->suspend_with_cc(); +} + void scheduler::resume_( context * active_ctx, context * ctx) noexcept { BOOST_ASSERT( nullptr != active_ctx); @@ -168,7 +179,7 @@ scheduler::~scheduler() { main_ctx_ = nullptr; } -void +boost::context::captured_context scheduler::dispatch() noexcept { BOOST_ASSERT( context::active() == dispatcher_ctx_); while ( ! shutdown_) { @@ -229,7 +240,7 @@ scheduler::dispatch() noexcept { // release termianted context' release_terminated_(); // return to main-context - resume_( dispatcher_ctx_.get(), main_ctx_); + return suspend_with_cc( dispatcher_ctx_.get(), main_ctx_); } void @@ -276,7 +287,7 @@ scheduler::set_remote_ready( context * ctx) noexcept { sched_algo_->notify(); } -void +boost::context::captured_context scheduler::set_terminated( context * active_ctx) noexcept { BOOST_ASSERT( nullptr != active_ctx); BOOST_ASSERT( context::active() == active_ctx); @@ -292,7 +303,7 @@ scheduler::set_terminated( context * active_ctx) noexcept { // intrusive_ptr_release( ctx); active_ctx->terminated_link( terminated_queue_); // resume another fiber - resume_( active_ctx, get_next_() ); + return suspend_with_cc( active_ctx, get_next_() ); } void