From 6459b7607561b6947d4cbc00cb0a4ae382a59b52 Mon Sep 17 00:00:00 2001 From: Oliver Kowalke Date: Mon, 7 Sep 2015 22:30:26 +0200 Subject: [PATCH] do not use context::active() inside scheduler --- include/boost/fiber/context.hpp | 4 +- include/boost/fiber/scheduler.hpp | 25 ++++--- src/context.cpp | 8 +-- src/scheduler.cpp | 110 +++++++++++++++++------------- 4 files changed, 81 insertions(+), 66 deletions(-) diff --git a/include/boost/fiber/context.hpp b/include/boost/fiber/context.hpp index e4b347cb..847177e3 100644 --- a/include/boost/fiber/context.hpp +++ b/include/boost/fiber/context.hpp @@ -371,13 +371,13 @@ public: template< typename Clock, typename Duration > bool do_wait_until( std::chrono::time_point< Clock, Duration > const& timeout_time, detail::spinlock_lock & lk) { - return scheduler_->wait_until( timeout_time, lk); + return scheduler_->wait_until( this, timeout_time, lk); } template< typename Rep, typename Period > bool do_wait_for( std::chrono::duration< Rep, Period > const& timeout_duration, detail::spinlock_lock & lk) { - return scheduler_->wait_for( timeout_duration, lk); + return scheduler_->wait_for( this, timeout_duration, lk); } void do_yield(); diff --git a/include/boost/fiber/scheduler.hpp b/include/boost/fiber/scheduler.hpp index 968ff839..b5f55a24 100644 --- a/include/boost/fiber/scheduler.hpp +++ b/include/boost/fiber/scheduler.hpp @@ -41,13 +41,14 @@ private: tqueue_t tqueue_; std::chrono::steady_clock::duration wait_interval_; - void resume_( context *); + void resume_( context *, context *); - bool wait_until_( std::chrono::steady_clock::time_point const&, + bool wait_until_( context *, + std::chrono::steady_clock::time_point const&, detail::spinlock_lock &); public: - scheduler() noexcept; + scheduler( context *) noexcept; scheduler( scheduler const&) = delete; scheduler & operator=( scheduler const&) = delete; @@ -56,28 +57,30 @@ public: void spawn( context *); - void run(); + void run( context *); - void wait( detail::spinlock_lock &); + void wait( context *, detail::spinlock_lock &); template< typename Clock, typename Duration > - bool wait_until( std::chrono::time_point< Clock, Duration > const& timeout_time_, + bool wait_until( context * af, + std::chrono::time_point< Clock, Duration > const& timeout_time_, detail::spinlock_lock & lk) { std::chrono::steady_clock::time_point timeout_time( detail::convert_tp( timeout_time_) ); - return wait_until_( timeout_time, lk); + return wait_until_( af, timeout_time, lk); } template< typename Rep, typename Period > - bool wait_for( std::chrono::duration< Rep, Period > const& timeout_duration, + bool wait_for( context * af, + std::chrono::duration< Rep, Period > const& timeout_duration, detail::spinlock_lock & lk) { return wait_until_( - std::chrono::steady_clock::now() + timeout_duration, lk); + af, std::chrono::steady_clock::now() + timeout_duration, lk); } - void yield(); + void yield( context *); - void join( context *); + void join( context *,context *); std::size_t ready_fibers() const noexcept; diff --git a/src/context.cpp b/src/context.cpp index a2639acc..2f8c703d 100644 --- a/src/context.cpp +++ b/src/context.cpp @@ -161,7 +161,7 @@ context::do_schedule() { BOOST_ASSERT( nullptr != scheduler_); BOOST_ASSERT( this == active_); - scheduler_->run(); + scheduler_->run( this); } void @@ -169,7 +169,7 @@ context::do_wait( detail::spinlock_lock & lk) { BOOST_ASSERT( nullptr != scheduler_); BOOST_ASSERT( this == active_); - scheduler_->wait( lk); + scheduler_->wait( this, lk); } void @@ -177,7 +177,7 @@ context::do_yield() { BOOST_ASSERT( nullptr != scheduler_); BOOST_ASSERT( this == active_); - scheduler_->yield(); + scheduler_->yield( this); } void @@ -186,7 +186,7 @@ context::do_join( context * f) { BOOST_ASSERT( this == active_); BOOST_ASSERT( nullptr != f); - scheduler_->join( f); + scheduler_->join( this, f); } std::size_t diff --git a/src/scheduler.cpp b/src/scheduler.cpp index e46b2179..2eace170 100644 --- a/src/scheduler.cpp +++ b/src/scheduler.cpp @@ -34,7 +34,7 @@ scheduler::scheduler( context * main_context) noexcept : } scheduler::~scheduler() noexcept { - // FIXME: test for main-fiber + BOOST_ASSERT( context::active() == main_context_); for (;;) { // NOTE: at this stage the fibers in the waiting-queue // can only be detached fibers @@ -52,45 +52,69 @@ scheduler::~scheduler() noexcept { // add active-fiber to joinig-list of f // fiber::join() should not fail because fiber f is in state_ready // so main-fiber should be in the waiting-queue of fiber f - f->join( context::active() ); + f->join( main_context_); // set main-fiber to state_waiting - context::active()->set_waiting(); + main_context_->set_waiting(); // push main-fiber to waiting-queue - wqueue_.push( context::active() ); + wqueue_.push( main_context_); // resume fiber f - resume_( f); + resume_( main_context_, f); } else if ( wqueue_.empty() ) { // ready- and waiting-queue are empty so we can quit break; } } BOOST_ASSERT( wqueue_.empty() ); - // FIXME: test for main-fiber + BOOST_ASSERT( context::active() == main_context_); } void -scheduler::resume_( context * f) { +scheduler::resume_( context * af, context * f) { + BOOST_ASSERT( nullptr != af); BOOST_ASSERT( nullptr != f); BOOST_ASSERT( f->is_ready() ); // set fiber to state running f->set_running(); // fiber next-to-run is same as current active-fiber // this might happen in context of this_fiber::yield() - if ( f == context::active() ) { + if ( f == af) { return; } // pass new fiber the scheduler of the current active fiber // this might be necessary if the new fiber was miggrated from // another thread - f->set_scheduler( context::active()->get_scheduler() ); + f->set_scheduler( af->get_scheduler() ); // assign new fiber to active-fiber - context * old( context::active( f) ); + context::active( f); // push terminated fibers to terminated-queue - if ( old->is_terminated() ) { - tqueue_.push( old); + if ( af->is_terminated() ) { + tqueue_.push( af); } - // resume active-fiber == start or yield to - context::active()->resume(); + // resume active-fiber == f + f->resume(); +} + +bool +scheduler::wait_until_( context * af, + std::chrono::steady_clock::time_point const& timeout_time, + detail::spinlock_lock & lk) { + BOOST_ASSERT( nullptr != af); + BOOST_ASSERT( context::active() == af); + BOOST_ASSERT( af->is_running() ); + // set active-fiber to state_waiting + af->set_waiting(); + // release lock + lk.unlock(); + // push active-fiber to waiting-queue + af->time_point( timeout_time); + wqueue_.push( af); + // switch to another fiber + run( af); + // fiber has been resumed + // check if fiber was interrupted + this_fiber::interruption_point(); + // check if timeout has reached + return std::chrono::steady_clock::now() < timeout_time; } void @@ -103,7 +127,9 @@ scheduler::spawn( context * f) { } void -scheduler::run() { +scheduler::run( context * af) { + BOOST_ASSERT( nullptr != af); + BOOST_ASSERT( context::active() == af); for (;;) { // destroy terminated fibers from terminated-queue tqueue_.clear(); @@ -115,7 +141,7 @@ scheduler::run() { if ( f) { BOOST_ASSERT_MSG( f->is_ready(), "fiber with invalid state in ready-queue"); // resume fiber f - resume_( f); + resume_( af, f); return; } else { // no fibers ready to run; the thread should sleep @@ -126,64 +152,50 @@ scheduler::run() { } void -scheduler::wait( detail::spinlock_lock & lk) { +scheduler::wait( context * af, detail::spinlock_lock & lk) { wait_until( + af, std::chrono::steady_clock::time_point( (std::chrono::steady_clock::duration::max)() ), lk); } -bool -scheduler::wait_until_( std::chrono::steady_clock::time_point const& timeout_time, - detail::spinlock_lock & lk) { - BOOST_ASSERT( context::active()->is_running() ); - // set active-fiber to state_waiting - context::active()->set_waiting(); - // release lock - lk.unlock(); - // push active-fiber to waiting-queue - context::active()->time_point( timeout_time); - wqueue_.push( context::active() ); - // switch to another fiber - run(); - // fiber has been resumed - // check if fiber was interrupted - this_fiber::interruption_point(); - // check if timeout has reached - return std::chrono::steady_clock::now() < timeout_time; -} - void -scheduler::yield() { - BOOST_ASSERT( context::active()->is_running() ); +scheduler::yield( context * af) { + BOOST_ASSERT( nullptr != af); + BOOST_ASSERT( context::active() == af); + BOOST_ASSERT( af->is_running() ); // set active-fiber to state_waiting - context::active()->set_ready(); + af->set_ready(); // push active-fiber to wqueue_ - wqueue_.push( context::active() ); + wqueue_.push( af); // switch to another fiber - run(); + run( af); // fiber has been resumed // NOTE: do not check if fiber was interrupted // yield() should not be an interruption point } void -scheduler::join( context * f) { +scheduler::join( context * af, context * f) { + BOOST_ASSERT( nullptr != af); + BOOST_ASSERT( context::active() == af); + BOOST_ASSERT( af->is_running() ); BOOST_ASSERT( nullptr != f); - BOOST_ASSERT( f != context::active() ); + BOOST_ASSERT( f != af); // set active-fiber to state_waiting - context::active()->set_waiting(); + af->set_waiting(); // push active-fiber to waiting-queue - wqueue_.push( context::active() ); + wqueue_.push( af); // add active-fiber to joinig-list of f - if ( ! f->join( context::active() ) ) { + if ( ! f->join( af) ) { // f must be already terminated therefore we set // active-fiber to state_ready // FIXME: better state_running and no suspend - context::active()->set_ready(); + af->set_ready(); } // switch to another fiber - run(); + run( af); // fiber has been resumed // check if fiber was interrupted this_fiber::interruption_point();