diff --git a/doc/customization.qbk b/doc/customization.qbk new file mode 100644 index 00000000..0a4724fa --- /dev/null +++ b/doc/customization.qbk @@ -0,0 +1,85 @@ +[/ + 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 +] + +[section:custom Customization] + +__boost_fiber__ allows to customize the scheduling algorithm by a user-defined +implementation. +A fiber-scheduler must implement interface __algo__. __boost_fiber__ provides +scheduler [class_link round_robin]. + +To prevent the library from heap-allocating a default scheduler for a given +thread, that thread must call [function_link set_scheduling_algorithm] before +any other __boost_fiber__ entry point. + + void thread_fn() + { + my_fiber_scheduler mfs; + boost::fibers::set_scheduling_algorithm( & mfs); + ... + } + +You are explicitly permitted to code your own __algo__ subclass, and to pass +it to [function_link set_scheduling_algorithm]. + + +[class_heading sched_algorithm] + + #include + + struct sched_algorithm + { + virtual ~sched_algorithm() {} + + virtual void awakened( detail::worker_fiber *) = 0; + + virtual detail::worker_fiber * pick_next() = 0; + + virtual void priority( detail::worker_fiber *, int) noexcept = 0; + }; + + void set_scheduling_algorithm( sched_algorithm *); + +[member_heading algorithm..awakened] + + virtual void awakened( detail::worker_fiber * f) = 0; + +[variablelist +[[Effects:] [Marks fiber `f`, to be ready to run.]] +] + +[member_heading algorithm..pick_next] + + virtual detail::worker_fiber * pick_next() = 0; + +[variablelist +[[Effects:] [Depending on the scheduling algorithm, this function returns the +fiber which has to be resumed next.]] +] + +[member_heading algorithm..priority] + + virtual void priority( detail::worker_fiber *, int) noexcept = 0; + +[variablelist +[[Effects:] [Reschedule the fiber depending on the priority.]] +] + + +[ns_function_heading fibers..set_scheduling_algorithm] + + void set_scheduling_algorithm( sched_algorithm * a); + +[variablelist +[[Effects:] [Registers `a` as scheduling algorithm.]] +] + + +[info The exsample section provides an scheduler used for migrating fibers +(work-stealing) between threads (different schedulers).] + +[endsect] diff --git a/doc/extension.qbk b/doc/extension.qbk deleted file mode 100644 index f3a223fd..00000000 --- a/doc/extension.qbk +++ /dev/null @@ -1,11 +0,0 @@ -[/ - 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 -] - -[section:extension Extension] - - -[endsect] diff --git a/doc/fiber.qbk b/doc/fiber.qbk index 69049468..d5d44d48 100644 --- a/doc/fiber.qbk +++ b/doc/fiber.qbk @@ -94,9 +94,10 @@ might have suspended. [heading Exceptions] -Exceptions thrown by the function or callable object passed to the __fiber__ -constructor are consumed by the framework. If you need to know which -exception was thrown, use __future__ and __packaged_task__. +A exception thrown by the function or callable object passed to the __fiber__ +constructor calls `std::terminate()`. +If you need to know which exception was thrown, use __future__ and +__packaged_task__. [heading Detaching] @@ -207,7 +208,7 @@ operators on __fiber_id__ yield a total order for every non-equal __fiber_id__. class fiber { public: - typedef uspecified-class id; + class id; fiber() noexcept; @@ -462,7 +463,7 @@ interruption enabled.]] [[Throws:] [Nothing]] [[Note:] [The meaning of particular `int` values is determined by the specific __algo__ passed to [function_link set_scheduling_algorithm]. [class_link -round_robin] and [class_link round_robin_ws] ignore `fiber::priority()`.]] +round_robin] ignores `fiber::priority()`.]] ] [member_heading fiber..priority] @@ -576,6 +577,113 @@ ensure that the instance is destroyed on thread termination.)]] [endsect] [/ section Class fiber] +[#class_id] +[section:id Class fiber::id] + + #include + + class id + { + public: + id() noexcept; + + bool operator==( id const& other) const noexcept; + + bool operator!=( id const& other) const noexcept; + + bool operator<( id const& other) const noexcept; + + bool operator>( id const& other) const noexcept; + + bool operator<=( id const& other) const noexcept; + + bool operator>=( id const& other) const noexcept; + + template< typename charT, class traitsT > + friend std::basic_ostream< charT, traitsT > & + operator<<( std::basic_ostream< charT, traitsT > & os, id const& other); + }; + +[heading Constructor] + + id() noexcept; + +[variablelist +[[Effects:] [Represents an instance of __not_a_fiber__.]] +[[Throws:] [Nothing.]] +] + +[operator_heading id..operator==] + + bool operator==( id const& other) const noexcept; + +[variablelist +[[Returns:] [`true` if `*this` and `other` represent the same fiber, +or both represent __not_a_fiber__, `false` otherwise.] +[[Throws:] [Nothing.]] +] + +[operator_heading id..operator!=] + + bool operator!=( id const& other) const noexcept; + +[variablelist +[[Returns:] [`! (other == * this)] +[[Throws:] [Nothing.]] +] + +[operator_heading id..operator<] + + bool operator<( id const& other) const noexcept; + +[variablelist +[[Returns:] [`true` if `*this != other` is true and the +implementation-defined total order of `fiber::id` values places `*this` before +`other`, false otherwise.]] +[[Throws:] [Nothing.]] +] + +[operator_heading id..operator>] + + bool operator>( id const& other) const noexcept; + +[variablelist +[[Returns:] [`other < * this`] +[[Throws:] [Nothing.]] +] + +[operator_heading id..operator<=] + + bool operator<=( id const& other) const noexcept; + +[variablelist +[[Returns:] [`! (other < * this)`] +[[Throws:] [Nothing.]] +] + +[operator_heading id..operator>=] + + bool operator>=( id const& other) const noexcept; + +[variablelist +[[Returns:] [`! (* this < other)`] +[[Throws:] [Nothing.]] +] + +[operator_heading operator<<] + + template< typename charT, class traitsT > + std::basic_ostream< charT, traitsT > & + operator<<( std::basic_ostream< charT, traitsT > & os, id const& other); + +[variablelist +[[Efects:] [Writes the representation of `other` to stream `os`.] +[[Returns:] [`os`]] +] + + +[endsect] [/ section Class fiber::id] + [#class_attributes] [section:attributes Class attributes] @@ -589,15 +697,15 @@ fiber's context. { std::size_t size; - attributes() BOOST_NOEXCEPT; + attributes() noexcept; - explicit attributes( std::size_t size_) BOOST_NOEXCEPT; + explicit attributes( std::size_t size_) noexcept; }; [heading Constructor] - attributes(); - attributes( std::size_t size); + attributes() noexcept; + attributes( std::size_t size) noexcept; [variablelist [[Effects:] [Parameter `size` determines the stack size.]] diff --git a/doc/fibers.qbk b/doc/fibers.qbk index 0a2f2360..cf693a20 100644 --- a/doc/fibers.qbk +++ b/doc/fibers.qbk @@ -122,7 +122,7 @@ [def __already_retrieved__ `future_errc::future_already_retrieved`] [def __already_satisfied__ `future_errc::future_already_satisfied`] -[def __algo__ [class_link algorithm]] +[def __algo__ [class_link sched_algorithm]] [def __async__ `async()`] [def __barrier_wait__ [member_link barrier..wait]] [def __cond_wait__ [member_link condition_variable..wait]] @@ -165,4 +165,6 @@ [include fls.qbk] [include asio.qbk] [include performance.qbk] +[include rational.qbk] +[include customization.qbk] [include acknowledgements.qbk] diff --git a/doc/future.qbk b/doc/future.qbk index c6639e62..47cf9490 100644 --- a/doc/future.qbk +++ b/doc/future.qbk @@ -37,7 +37,7 @@ Timed wait-operations (__wait_for__ and __wait_until__) return the state of the [[Note:] [Not implemented yet.]] ] -[note Launch policy `deferred`, which indicates you simply want to defer the function call until a later time (lazy evaluation), is not supported yet.] +[warning Launch policy `deferred`, which indicates you simply want to defer the function call until a later time (lazy evaluation), is not supported yet.] [template_heading future] @@ -393,4 +393,20 @@ return * this; [[Throws:] [__future_error__ with error condition __no_state__.]] ] +[ns_function_heading fibers..async] + + #include + + template< typename Fn > + future< typename result_of< Fn >::type > async( Fn && fn); + +[variablelist +[[Effects:] [Executes `fn` in a fiber and returns an associated future.]] +[[Result:] `future< typename result_of< Fn >::type >` representing the shared state associated with +the asynchronous execution of `fn`.] +[[Throws:] [__fiber_error__ if an error occurs.]] +] + +[warning Launch policy `deferred`, which indicates you simply want to defer the function call until a later time (lazy evaluation), is not supported yet.] + [endsect] diff --git a/doc/rational.qbk b/doc/rational.qbk index f7f733d4..0cd05042 100644 --- a/doc/rational.qbk +++ b/doc/rational.qbk @@ -9,11 +9,59 @@ [heading Distinction between coroutines and fibers] +The fiber library extends the coroutine library by adding a scheduler and +synchronization mechanisms. + + * a coroutine yields + + * a fiber blocks + +When a coroutine yields, it passes control directly to its caller (or, in the +case of symmetric coroutines, a designated other coroutine). +When a fiber blocks, it implicitly passes control to the fiber scheduler. +Coroutines have no scheduler because they need no scheduler. +[footnote [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4024.pdf +'N4024: Distinguishing coroutines and fibers']]. + + [heading `fiber_manager`with free-functions] +Some compiler store the `this`-pointer on the stack so that the fiber, which has +been migrated to another scheduler (thread), access the invalid instance if it +will be resumed from `fiber_manager::run()`. + + [heading synchronization between fibers running in different threads] +Synchronization classes from __boost_thread__ do block the entire thread. In +contrast to this the synchronization clases from __boost_fiber__ do block only +the fiber, so that the thread is able to schedule and run other fibers in the +meantime. +The synchronization classes from __boost_fiber__ are designed to be thread-safe, +e.g. it is possible to synchronize fibers running in different schedulers +(different threads) or running int the same scheduler (same thread). + + [heading migrating fibers between threads] +Fibers can be migrated between different schedulers, e.g. threads. The library +contains an example demonstrating how a scheduler could be implemented to allow +the migration of fibers. The example allows to move fibers from the ready-queue +(fibers with state READY, e.g. ready to be executed) of scheduler `X` to the +ready-queue of scheduler `Y`. It is important that this operation is +thread-safe. If the fiber is put to the ready-queue of scheduler `Y` it might be +selected for resumtion in the next step, e.g. the fiber will be executed in the +context of the thread running scheudler `Y`. + + +[heading variadric template args for async()/packaged_task<>] + +Variadric template args for `asnyc()` implies variadic args support for +`packaged_task<>`. Because C++03 does not provide variadric template args the author +desided not to support this featrure for `asnyc()` and `packaged_task<>` otherwise +the template signature would be look different btween C++03 and C++11. +In further versions of this library variadric template args will be supported +for C++03 with the usage of pre-processor macros and template specialization. + [endsect] diff --git a/doc/scheduling.qbk b/doc/scheduling.qbk index 492bfd78..8ad4f6e3 100644 --- a/doc/scheduling.qbk +++ b/doc/scheduling.qbk @@ -12,82 +12,11 @@ running or waiting. Each thread is required to have its own scheduler. By default, __boost_fiber__ allocates for each thread a scheduler ([class_link round_robin]) on the heap. -To prevent the library from heap-allocating a default scheduler for a given -thread, that thread must call [function_link set_scheduling_algorithm] before -any other __boost_fiber__ entry point. - - void thread_fn() - { - my_fiber_scheduler mfs; - boost::fibers::set_scheduling_algorithm( & mfs); - ... - } - -A fiber-scheduler must implement interface __algo__. __boost_fiber__ provides -scheduler [class_link round_robin]. - -You are explicitly permitted to code your own __algo__ subclass, and to pass -it to [function_link set_scheduling_algorithm]. - - -[class_heading sched_algorithm] - - #include - - struct sched_algorithm - { - virtual ~sched_algorithm() {} - - virtual void awakened( detail::worker_fiber *) = 0; - - virtual detail::worker_fiber * pick_next() = 0; - - virtual void priority( detail::worker_fiber *, int) noexcept = 0; - }; - - void set_scheduling_algorithm( sched_algorithm *); - -[member_heading algorithm..awakened] - - virtual void awakened( detail::worker_fiber * f) = 0; - -[variablelist -[[Effects:] [Marks fiber `f`, to be ready to run.]] -] - -[member_heading algorithm..pick_next] - - virtual detail::worker_fiber * pick_next() = 0; - -[variablelist -[[Effects:] [Depending on the scheduling algorithm, this function returns the -fiber which has to be resumed next.]] -] - -[member_heading algorithm..priority] - - virtual void priority( detail::worker_fiber *, int) noexcept = 0; - -[variablelist -[[Effects:] [Reschedule the fiber depending on the priority.]] -] - - -[ns_function_heading fibers..set_scheduling_algorithm] - - void set_scheduling_algorithm( sched_algorithm * a); - -[variablelist -[[Effects:] [Registers `a` as scheduling algorithm.]] -] - [class_heading round_robin] -This class implements __algo__ and schedules fibers in round-robin fashion. - -[info The exsample section provides an scheduler used for migrating fibers -(work-stealing) between threads (different schedulers).] +This class implements __algo__ and schedules fibers in round-robin fashion +(ignores `fiber::priority()`). [endsect] diff --git a/examples/cpp03/Jamfile.v2 b/examples/cpp03/Jamfile.v2 index cc58d7c9..22b73ec6 100644 --- a/examples/cpp03/Jamfile.v2 +++ b/examples/cpp03/Jamfile.v2 @@ -39,8 +39,8 @@ exe ping_pong : ping_pong.cpp ; exe segmented_stack : segmented_stack.cpp ; exe simple : simple.cpp test_fiber.cpp ; exe migrate_fibers - : migrate_fibers.cpp - workstealing_round_robin.cpp + : migration/migrate_fibers.cpp + migration/workstealing_round_robin.cpp ; exe asio/daytime_client : asio/daytime_client.cpp ; diff --git a/examples/cpp03/asio/publish_subscribe/server.cpp b/examples/cpp03/asio/publish_subscribe/server.cpp index 60671281..f3c3a2e2 100644 --- a/examples/cpp03/asio/publish_subscribe/server.cpp +++ b/examples/cpp03/asio/publish_subscribe/server.cpp @@ -192,9 +192,11 @@ public: // published message is stored in buffer 'data_' boost::unique_lock< boost::fibers::mutex > lk( mtx_); cond_.wait( lk); + std::string data( data_); + lk.unlock(); // message '' terminates subscription - if ( "" == std::string( data_) ) break; + if ( "" == data) break; // async. write message to socket connected with // subscriber @@ -202,7 +204,7 @@ public: // the fiber is suspended in the meanwhile boost::asio::async_write( socket_, - boost::asio::buffer( data_, max_length), + boost::asio::buffer( data, data.size() ), yield[ec]); if ( ec) throw std::runtime_error("publishing message failed"); } diff --git a/examples/cpp03/migrate_fibers.cpp b/examples/cpp03/migration/migrate_fibers.cpp similarity index 100% rename from examples/cpp03/migrate_fibers.cpp rename to examples/cpp03/migration/migrate_fibers.cpp diff --git a/examples/cpp03/workstealing_round_robin.cpp b/examples/cpp03/migration/workstealing_round_robin.cpp similarity index 100% rename from examples/cpp03/workstealing_round_robin.cpp rename to examples/cpp03/migration/workstealing_round_robin.cpp diff --git a/examples/cpp03/workstealing_round_robin.hpp b/examples/cpp03/migration/workstealing_round_robin.hpp similarity index 100% rename from examples/cpp03/workstealing_round_robin.hpp rename to examples/cpp03/migration/workstealing_round_robin.hpp diff --git a/src/fiber_manager.cpp b/src/fiber_manager.cpp index ec51f9a2..dfa234e6 100644 --- a/src/fiber_manager.cpp +++ b/src/fiber_manager.cpp @@ -95,7 +95,7 @@ void fm_set_sched_algo( sched_algorithm * algo) fiber_manager * fm = detail::scheduler::instance(); BOOST_ASSERT( 0 != fm); - + fm->sched_algo_ = algo; fm->def_algo_.reset(); }