2
0
mirror of https://github.com/boostorg/fiber.git synced 2026-02-16 13:22:17 +00:00
Files
fiber/src/scheduler.cpp
2015-09-18 17:48:57 +02:00

174 lines
5.0 KiB
C++

// Copyright Oliver Kowalke 2013.
// 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 "boost/fiber/scheduler.hpp"
#include <chrono>
#include <boost/assert.hpp>
#include "boost/fiber/context.hpp"
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
namespace boost {
namespace fibers {
void
scheduler::resume_( context * active_ctx, context * ctx) {
BOOST_ASSERT( nullptr != active_ctx);
BOOST_ASSERT( nullptr != ctx);
BOOST_ASSERT( active_ctx->get_scheduler() == ctx->get_scheduler() );
// fiber next-to-run is same as current active-fiber
// this might happen in context of this_fiber::yield()
if ( active_ctx == ctx) {
return;
}
// assign new fiber to active-fiber
context::active( ctx);
// resume active-fiber == ctx
ctx->resume();
}
context *
scheduler::get_next_() noexcept {
context * ctx( nullptr);
if ( ! ready_queue_.empty() ) {
ctx = & ready_queue_.front();
ready_queue_.pop_front();
}
return ctx;
}
void
scheduler::release_terminated_() {
terminated_queue_t::iterator e( terminated_queue_.end() );
for ( terminated_queue_t::iterator i( terminated_queue_.begin() );
i != e;) {
context * ctx = & ( * i);
i = terminated_queue_.erase( i);
intrusive_ptr_release( ctx);
}
}
scheduler::scheduler() noexcept :
main_ctx_( nullptr),
dispatcher_ctx_(),
ready_queue_(),
terminated_queue_(),
shutdown_( false),
ready_queue_ev_() {
}
scheduler::~scheduler() noexcept {
BOOST_ASSERT( nullptr != main_ctx_);
BOOST_ASSERT( nullptr != dispatcher_ctx_.get() );
BOOST_ASSERT( context::active() == main_ctx_);
// signal dispatcher context termination
shutdown_ = true;
// resume pending fibers
resume_( main_ctx_, get_next_() );
// deallocate dispatcher-context
dispatcher_ctx_.reset();
// set main-context to nullptr
main_ctx_ = nullptr;
}
void
scheduler::set_main_context( context * main_ctx) noexcept {
BOOST_ASSERT( nullptr != main_ctx);
main_ctx_ = main_ctx;
main_ctx_->set_scheduler( this);
}
void
scheduler::set_dispatcher_context( intrusive_ptr< context > dispatcher_ctx) noexcept {
BOOST_ASSERT( dispatcher_ctx);
dispatcher_ctx_.swap( dispatcher_ctx);
// add dispatcher context to ready-queue
// so it is the first element in the ready-queue
// if the main context tries to suspend the first time
// the dispatcher context is resumed and
// scheduler::dispatch() is executed
dispatcher_ctx_->set_scheduler( this);
ready_queue_.push_back( * dispatcher_ctx_.get() );
}
void
scheduler::dispatch() {
while ( ! shutdown_) {
release_terminated_();
context * ctx( nullptr);
// loop till we get next ready context
while ( nullptr == ( ctx = get_next_() ) ) {
// TODO: move context' from remote ready-queue to local ready-queue
// move ready context' from sleep-queue to ready-queue
// no ready context, wait till signaled
ready_queue_ev_.reset(
(std::chrono::steady_clock::time_point::max)());
}
// push dispatcher context to ready-queue
// so that ready-queue never becomes empty
auto active_ctx = context::active();
ready_queue_.push_back( * active_ctx);
resume_( active_ctx, ctx);
// TODO: pump external event-loop like boost::asio::io_service
// react on external interrupt signals
// react on requestsin work sahring scenario
// no ready context, wait till signaled
}
// interrupt all context' in ready- and sleep-queue
release_terminated_();
resume_( dispatcher_ctx_.get(), main_ctx_);
}
void
scheduler::set_ready( context * ctx) noexcept {
BOOST_ASSERT( nullptr != ctx);
BOOST_ASSERT( ! ctx->ready_is_linked() );
BOOST_ASSERT( ! ctx->is_terminated() );
BOOST_ASSERT( ! ctx->wait_is_linked() );
// set the scheduler for new context
ctx->set_scheduler( this);
// push new context to ready-queue
ready_queue_.push_back( * ctx);
}
void
scheduler::set_terminated( context * ctx) noexcept {
BOOST_ASSERT( nullptr != ctx);
BOOST_ASSERT( ctx->is_terminated() );
BOOST_ASSERT( ! ctx->ready_is_linked() );
BOOST_ASSERT( ! ctx->wait_is_linked() );
terminated_queue_.push_back( * ctx);
}
void
scheduler::yield( context * active_ctx) noexcept {
BOOST_ASSERT( nullptr != active_ctx);
BOOST_ASSERT( ! active_ctx->is_terminated() );
BOOST_ASSERT( ! active_ctx->ready_is_linked() );
BOOST_ASSERT( ! active_ctx->wait_is_linked() );
// push active context to ready-queue
ready_queue_.push_back( * active_ctx);
resume_( active_ctx, get_next_() );
}
void
scheduler::re_schedule( context * active_ctx) noexcept {
BOOST_ASSERT( nullptr != active_ctx);
resume_( active_ctx, get_next_() );
}
}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif