From cff07a3645f9b33cf006614cfdff3122a4254da5 Mon Sep 17 00:00:00 2001 From: Oliver Kowalke Date: Thu, 10 Jan 2013 19:51:34 +0100 Subject: [PATCH] interruption --- include/boost/fiber/detail/fiber_base.hpp | 24 ++++++- include/boost/fiber/detail/flags.hpp | 8 ++- .../boost/fiber/detail/interrupt_flags.hpp | 6 +- include/boost/fiber/fiber.hpp | 2 + include/boost/fiber/interruption.hpp | 52 ++++---------- src/detail/fiber_base.cpp | 8 +-- src/fiber.cpp | 12 +++- src/interruption.cpp | 72 +++++++++++++++++++ test/test_condition.cpp | 25 +++++++ test/test_fiber.cpp | 23 ------ 10 files changed, 158 insertions(+), 74 deletions(-) create mode 100644 src/interruption.cpp diff --git a/include/boost/fiber/detail/fiber_base.hpp b/include/boost/fiber/detail/fiber_base.hpp index 2977b029..dab6c0e7 100644 --- a/include/boost/fiber/detail/fiber_base.hpp +++ b/include/boost/fiber/detail/fiber_base.hpp @@ -45,12 +45,12 @@ private: atomic< std::size_t > use_count_; atomic< state_t > state_; + atomic< int > flags_; atomic< int > priority_; context::fcontext_t caller_; context::fcontext_t * callee_; - int flags_; exception_ptr except_; - spinlock mtx_; + spinlock joining_mtx_; std::vector< ptr_t > joining_; protected: @@ -141,6 +141,26 @@ public: bool preserve_fpu() const BOOST_NOEXCEPT { return 0 != ( flags_ & flag_preserve_fpu); } + bool interruption_enabled() const BOOST_NOEXCEPT + { return 0 == ( flags_ & flag_interruption_blocked); } + + bool interruption_blocked() const BOOST_NOEXCEPT + { return 0 != ( flags_ & flag_interruption_blocked); } + + void interruption_blocked( bool blck) BOOST_NOEXCEPT + { + if ( blck) + flags_ |= flag_interruption_blocked; + else + flags_ &= ~flag_interruption_blocked; + } + + bool interruption_requested() const BOOST_NOEXCEPT + { return 0 != ( flags_ & flag_interruption_requested); } + + void request_interruption() BOOST_NOEXCEPT + { flags_ |= flag_interruption_requested; } + bool is_terminated() const BOOST_NOEXCEPT { return state_terminated == state_; } diff --git a/include/boost/fiber/detail/flags.hpp b/include/boost/fiber/detail/flags.hpp index 5e300f5f..4f6ab797 100644 --- a/include/boost/fiber/detail/flags.hpp +++ b/include/boost/fiber/detail/flags.hpp @@ -21,9 +21,11 @@ namespace detail { enum flag_t { - flag_force_unwind = 1 << 1, - flag_unwind_stack = 1 << 2, - flag_preserve_fpu = 1 << 3 + flag_force_unwind = 1 << 1, + flag_unwind_stack = 1 << 2, + flag_preserve_fpu = 1 << 3, + flag_interruption_blocked = 1 << 4, + flag_interruption_requested = 1 << 5 }; }}} diff --git a/include/boost/fiber/detail/interrupt_flags.hpp b/include/boost/fiber/detail/interrupt_flags.hpp index c30b29bd..29fc51b8 100644 --- a/include/boost/fiber/detail/interrupt_flags.hpp +++ b/include/boost/fiber/detail/interrupt_flags.hpp @@ -4,8 +4,8 @@ // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) -#ifndef BOOST_STRATIFIED_DETAIL_INTERRUPT_FLAGS_H -#define BOOST_STRATIFIED_DETAIL_INTERRUPT_FLAGS_H +#ifndef BOOST_FIBERS_DETAIL_INTERRUPT_FLAGS_H +#define BOOST_FIBERS_DETAIL_INTERRUPT_FLAGS_H #include @@ -32,4 +32,4 @@ typedef char interrupt_type; # include BOOST_ABI_SUFFIX #endif -#endif // BOOST_STRATIFIED_DETAIL_INTERRUPT_FLAGS_H +#endif // BOOST_FIBERS_DETAIL_INTERRUPT_FLAGS_H diff --git a/include/boost/fiber/fiber.hpp b/include/boost/fiber/fiber.hpp index 46ebb567..d8d7b33a 100644 --- a/include/boost/fiber/fiber.hpp +++ b/include/boost/fiber/fiber.hpp @@ -336,6 +336,8 @@ public: void join(); + void interrupt() BOOST_NOEXCEPT; + void cancel(); }; diff --git a/include/boost/fiber/interruption.hpp b/include/boost/fiber/interruption.hpp index 698b7c58..78088611 100644 --- a/include/boost/fiber/interruption.hpp +++ b/include/boost/fiber/interruption.hpp @@ -4,8 +4,10 @@ // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) -#ifndef BOOST_THIS_STRATUM_INTERRUPTION_H -#define BOOST_THIS_STRATUM_INTERRUPTION_H +// based on boost.thread + +#ifndef BOOST_THIS_FIBER_INTERRUPTION_H +#define BOOST_THIS_FIBER_INTERRUPTION_H #include @@ -13,8 +15,6 @@ #include #include -#include -#include #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_PREFIX @@ -33,23 +33,9 @@ private: bool set_; public: - disable_interruption() : - set_( ( fibers::scheduler::get_instance_()->active_fibers_->impl_->interrupt & fibers::detail::INTERRUPTION_BLOCKED) != 0) - { - if ( ! set_) - fibers::scheduler::get_instance_()->active_fibers_->impl_->interrupt |= fibers::detail::INTERRUPTION_BLOCKED; - } + disable_interruption() BOOST_NOEXCEPT; - ~disable_interruption() - { - try - { - if ( ! set_) - fibers::scheduler::get_instance_()->active_fibers_->impl_->interrupt &= ~fibers::detail::INTERRUPTION_BLOCKED; - } - catch (...) - {} - } + ~disable_interruption() BOOST_NOEXCEPT; }; class restore_interruption : private noncopyable @@ -58,29 +44,21 @@ private: disable_interruption & disabler_; public: - explicit restore_interruption( disable_interruption & disabler) : - disabler_( disabler) - { - if ( ! disabler_.set_) - fibers::scheduler::get_instance_()->active_fibers_->impl_->interrupt &= ~fibers::detail::INTERRUPTION_BLOCKED; - } + explicit restore_interruption( disable_interruption & disabler) BOOST_NOEXCEPT; - ~restore_interruption() - { - try - { - if ( ! disabler_.set_) - fibers::scheduler::get_instance_()->active_fibers_->impl_->interrupt |= fibers::detail::INTERRUPTION_BLOCKED; - } - catch (...) - {} - } + ~restore_interruption() BOOST_NOEXCEPT; }; +bool interruption_enabled() BOOST_NOEXCEPT; + +bool interruption_requested() BOOST_NOEXCEPT; + +void interruption_point(); + }} #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_SUFFIX #endif -#endif // BOOST_THIS_STRATUM_INTERRUPTION_H +#endif // BOOST_THIS_FIBER_INTERRUPTION_H diff --git a/src/detail/fiber_base.cpp b/src/detail/fiber_base.cpp index 2f9c5933..bf200c3c 100644 --- a/src/detail/fiber_base.cpp +++ b/src/detail/fiber_base.cpp @@ -24,12 +24,12 @@ namespace detail { fiber_base::fiber_base( context::fcontext_t * callee, bool preserve_fpu) : use_count_( 0), state_( state_ready), + flags_( 0), priority_( 0), caller_(), callee_( callee), - flags_( 0), except_(), - mtx_(), + joining_mtx_(), joining_() { if ( preserve_fpu) flags_ |= flag_preserve_fpu; } @@ -75,7 +75,7 @@ fiber_base::terminate() if ( ! is_terminated() ) unwind_stack(); // fiber_base::terminate() is called by ~fiber_object() - // therefore protecting by mtx_ is not required + // therefore protecting by joining_mtx_ is not required // and joining_ is not required to be cleared BOOST_FOREACH( fiber_base::ptr_t & p, joining_) { p->set_ready(); } @@ -85,7 +85,7 @@ void fiber_base::join( ptr_t const& p) { // protect against concurrent access to joining_ - unique_lock< spinlock > lk( mtx_); + unique_lock< spinlock > lk( joining_mtx_); if ( is_terminated() ) return; joining_.push_back( p); } diff --git a/src/fiber.cpp b/src/fiber.cpp index 6d9a04b0..82c75347 100644 --- a/src/fiber.cpp +++ b/src/fiber.cpp @@ -47,7 +47,7 @@ void fiber::join() { BOOST_ASSERT( impl_); -#if 0 + if ( boost::this_fiber::is_fiberized() && boost::this_fiber::get_id() == get_id() ) boost::throw_exception( fiber_resource_error( @@ -57,12 +57,20 @@ fiber::join() boost::throw_exception( fiber_resource_error( system::errc::invalid_argument, "boost fiber: fiber not joinable") ); -#endif + detail::scheduler::instance().join( impl_); BOOST_ASSERT( impl_->is_terminated() ); } +void +fiber::interrupt() +{ + BOOST_ASSERT( impl_); + + impl_->request_interruption(); +} + void fiber::cancel() { diff --git a/src/interruption.cpp b/src/interruption.cpp new file mode 100644 index 00000000..83058057 --- /dev/null +++ b/src/interruption.cpp @@ -0,0 +1,72 @@ + +// 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) + +#define BOOST_FIBERS_SOURCE + +#include + +#include +#include +#include +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace this_fiber { + +disable_interruption::disable_interruption() : + set_( ( fibers::detail::scheduler::instance().active()->interruption_blocked() ) ) +{ + if ( ! set_) + fibers::detail::scheduler::instance().active()->interruption_blocked( true); +} + +disable_interruption::~disable_interruption() +{ + if ( ! set_) + fibers::detail::scheduler::instance().active()->interruption_blocked( false); +} + +restore_interruption::restore_interruption( disable_interruption & disabler) : + disabler_( disabler) +{ + if ( ! disabler_.set_) + fibers::detail::scheduler::instance().active()->interruption_blocked( false); +} + +restore_interruption::~restore_interruption() +{ + if ( ! disabler_.set_) + fibers::detail::scheduler::instance().active()->interruption_blocked( true); +} + +bool interruption_enabled() BOOST_NOEXCEPT +{ + fibers::detail::fiber_base::ptr_t f( fibers::detail::scheduler::instance().active() ); + return f && f->interruption_enabled(); +} + +bool interruption_requested() BOOST_NOEXCEPT +{ + fibers::detail::fiber_base::ptr_t f( fibers::detail::scheduler::instance().active() ); + if ( ! f) return false; + return f->interruption_requested(); +} + +void interruption_point() +{ + if ( interruption_enabled() && interruption_requested() ) + throw fibers::fiber_interrupted(); +} + +}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif diff --git a/test/test_condition.cpp b/test/test_condition.cpp index 11c06e25..df707f76 100644 --- a/test/test_condition.cpp +++ b/test/test_condition.cpp @@ -44,6 +44,16 @@ struct condition_test_data int awoken; }; +void condition_test_fiber(condition_test_data* data) +{ + boost::unique_lock lock(data->mutex); + BOOST_CHECK(lock ? true : false); + while (!(data->notified > 0)) + data->condition.wait(lock); + BOOST_CHECK(lock ? true : false); + data->awoken++; +} + struct cond_predicate { cond_predicate(int& var, int val) : _var(var), _val(val) { } @@ -187,6 +197,20 @@ void do_test_condition_waits() BOOST_CHECK_EQUAL(data.awoken, 5); } +void test_condition_wait_is_a_interruption_point() +{ + boost::fibers::round_robin ds; + boost::fibers::scheduling_algorithm( & ds); + + condition_test_data data; + + boost::fibers::fiber f(boost::bind(&condition_test_fiber, &data)); + + f.interrupt(); + f.join(); + BOOST_CHECK_EQUAL(data.awoken,0); +} + void test_one_waiter_notify_one() { boost::fibers::round_robin ds; @@ -326,6 +350,7 @@ boost::unit_test::test_suite * init_unit_test_suite( int, char* []) test->add( BOOST_TEST_CASE( & test_two_waiter_notify_one) ); test->add( BOOST_TEST_CASE( & test_two_waiter_notify_all) ); test->add( BOOST_TEST_CASE( & test_condition_waits) ); + test->add( BOOST_TEST_CASE( & test_condition_wait_is_a_interruption_point) ); return test; } diff --git a/test/test_fiber.cpp b/test/test_fiber.cpp index a0d8962e..b9eda033 100644 --- a/test/test_fiber.cpp +++ b/test/test_fiber.cpp @@ -221,29 +221,6 @@ void test_complete() if ( s2.joinable() ) s2.join(); } -void test_cancel() -{ - boost::fibers::round_robin ds; - boost::fibers::scheduling_algorithm( & ds); - - { - boost::fibers::fiber s( f2); - BOOST_CHECK( s); - s.cancel(); - BOOST_CHECK( ! s); - } - - { - // spawn fiber s - // s spawns an new fiber s' in its fiber-fn - // s' yields in its fiber-fn - // s cancels s' and completes - boost::fibers::fiber s( f3); - if ( s.joinable() ) s.join(); - BOOST_CHECK( ! s); - } -} - void test_join_in_thread() { boost::fibers::round_robin ds;