From 97e132abd62d3ed28c7cd3db0e3e130fc48c7da8 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Sat, 20 Jun 2015 12:00:11 -0400 Subject: [PATCH 1/6] Add BOOST_ASSERT_MSG in case properties() called when null. This could either be because the thread's current sched_algorithm is not a subclass of sched_algorithm_with_properties (e.g. set_scheduling_algorithm() was never called for this thread, so round_robin is in use), or because the fiber has not yet reached execution. A fiber's properties are instantiated when it is first scheduled. --- include/boost/fiber/fiber.hpp | 4 +++- include/boost/fiber/operations.hpp | 6 +++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/include/boost/fiber/fiber.hpp b/include/boost/fiber/fiber.hpp index 418d032a..577cf8dc 100644 --- a/include/boost/fiber/fiber.hpp +++ b/include/boost/fiber/fiber.hpp @@ -137,7 +137,9 @@ public: template< typename PROPS > PROPS & properties() { - return dynamic_cast< PROPS & >( * impl_->get_properties() ); + fiber_properties* props = impl_->get_properties(); + BOOST_ASSERT_MSG(props, "fiber::properties not set"); + return dynamic_cast< PROPS & >( * props ); } }; diff --git a/include/boost/fiber/operations.hpp b/include/boost/fiber/operations.hpp index f79a8236..0d54589d 100644 --- a/include/boost/fiber/operations.hpp +++ b/include/boost/fiber/operations.hpp @@ -10,6 +10,7 @@ #include // std::unique_lock #include +#include #include #include @@ -51,7 +52,10 @@ void sleep_for( std::chrono::duration< Rep, Period > const& timeout_duration) { template< typename PROPS > PROPS & properties() { - return dynamic_cast< PROPS & >( * fibers::detail::scheduler::instance()->active()->get_properties() ); + fibers::fiber_properties* props = + fibers::detail::scheduler::instance()->active()->get_properties(); + BOOST_ASSERT_MSG(props, "this_fiber::properties not set"); + return dynamic_cast< PROPS & >( * props ); } } // this_fiber From 15a17ca35d6861c32897864949174d06e2d00c71 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Sun, 21 Jun 2015 22:39:00 -0400 Subject: [PATCH 2/6] Introduce sched_algorithm_with_properties<>::awakened() overload. sched_algorithm_with_properties<> must intercept awakened() calls before forwarding control to the subclass override. That pretty much requires two different virtual methods: one for sched_algorithm subclasses WITHOUT properties, the other for sched_algorithm_with_properties subclasses. Originally we used a different name (awakened_props()), but that was bothersome. It makes more sense to use a different awakened() overload instead, one that passes in the relevant properties object. Fix examples/priority.cpp accordingly. Also delegate instantiation of a new properties object to new_properties() virtual method. Overriding this method allows you to customize allocation, instead of sched_algorithm_with_properties<> unconditionally putting the new properties object on the heap. Validate the new_properties() return value. --- examples/priority.cpp | 8 ++++---- include/boost/fiber/algorithm.hpp | 26 ++++++++++++++++++++------ 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/examples/priority.cpp b/examples/priority.cpp index f6a28d1d..2c6991c7 100644 --- a/examples/priority.cpp +++ b/examples/priority.cpp @@ -56,10 +56,10 @@ public: {} // For a subclass of sched_algorithm_with_properties<>, it's important to - // override awakened_props(), NOT awakened(). - virtual void awakened_props(boost::fibers::fiber_context* f) + // override the correct awakened() overload. + virtual void awakened(boost::fibers::fiber_context* f, priority_props& props) { - int f_priority = properties(f).get_priority(); + int f_priority = props.get_priority(); // With this scheduler, fibers with higher priority values are // preferred over fibers with lower priority values. But fibers with // equal priority values are processed in round-robin fashion. So when @@ -75,7 +75,7 @@ public: f->nxt = *fp; *fp = f; - std::cout << "awakened(" << properties(f).name << "): "; + std::cout << "awakened(" << props.name << "): "; describe_ready_queue(); } diff --git a/include/boost/fiber/algorithm.hpp b/include/boost/fiber/algorithm.hpp index 045267de..b113bf58 100644 --- a/include/boost/fiber/algorithm.hpp +++ b/include/boost/fiber/algorithm.hpp @@ -7,6 +7,7 @@ #define BOOST_FIBERS_ALGORITHM_H #include +#include #include #include @@ -43,15 +44,20 @@ struct sched_algorithm_with_properties : public sched_algorithm_with_properties_ typedef sched_algorithm_with_properties_base super; // Mark this override 'final': sched_algorithm_with_properties subclasses - // must override awakened_props() instead. Otherwise you'd have to - // remember to start every subclass awakened() override with: - // sched_algorithm_with_properties::awakened(fb); + // must override awakened() with properties parameter instead. Otherwise + // you'd have to remember to start every subclass awakened() override + // with: sched_algorithm_with_properties::awakened(fb); virtual void awakened( fiber_context * f) final { fiber_properties * props = super::get_properties( f); if ( ! props) { // TODO: would be great if PROPS could be allocated on the new // fiber's stack somehow - props = new PROPS( f); + props = new_properties( f); + // It is not good for new_properties() to return 0. + BOOST_ASSERT_MSG(props, "new_properties() must return non-NULL"); + // new_properties() must return instance of (a subclass of) PROPS + BOOST_ASSERT_MSG(dynamic_cast(props), + "new_properties() must return properties class"); super::set_properties( f, props); } // Set sched_algo_ again every time this fiber becomes READY. That @@ -60,11 +66,11 @@ struct sched_algorithm_with_properties : public sched_algorithm_with_properties_ props->set_sched_algorithm( this); // Okay, now forward the call to subclass override. - awakened_props( f); + awakened( f, properties(f) ); } // subclasses override this method instead of the original awakened() - virtual void awakened_props( fiber_context *) = 0; + virtual void awakened( fiber_context *, PROPS& ) = 0; // used for all internal calls PROPS& properties( fiber_context * f) { @@ -79,6 +85,14 @@ struct sched_algorithm_with_properties : public sched_algorithm_with_properties_ void property_change_( fiber_context * f, fiber_properties * props ) final { property_change( f, * static_cast< PROPS * >( props) ); } + + // Override this to customize instantiation of PROPS, e.g. use a different + // allocator. Each PROPS instance is associated with a particular + // fiber_context. + virtual fiber_properties * new_properties( fiber_context * f) + { + return new PROPS( f); + } }; }} From e290073b98552931020bf6f43b3ef903a51175a6 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 26 Jun 2015 14:40:16 -0400 Subject: [PATCH 3/6] Make fiber_context::resume() compatible with Boost 1.58.0. In Boost 1.58, execution_context has a resume() member, not an operator()(). Call ctx_.resume() instead of ctx_(). --- include/boost/fiber/fiber_context.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/fiber/fiber_context.hpp b/include/boost/fiber/fiber_context.hpp index 6037d83c..e5382df2 100644 --- a/include/boost/fiber/fiber_context.hpp +++ b/include/boost/fiber/fiber_context.hpp @@ -331,7 +331,7 @@ public: void resume() { BOOST_ASSERT( is_running() ); // set by the scheduler-algorithm - ctx_(); + ctx_.resume(); } std::chrono::high_resolution_clock::time_point const& time_point() const noexcept { From bbdd10139302959bf1c49652d7ac1ea7ae0c76c4 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 10 Jul 2015 09:47:11 -0400 Subject: [PATCH 4/6] Snapshot of incomplete documentation review. --- doc/customization.qbk | 51 +-------------- doc/fiber.qbk | 83 ++++++++++++++++-------- doc/fibers.qbk | 4 +- doc/overview.qbk | 16 +++-- doc/scheduling.qbk | 146 ++++++++++++++++++------------------------ doc/stack.qbk | 1 + 6 files changed, 137 insertions(+), 164 deletions(-) diff --git a/doc/customization.qbk b/doc/customization.qbk index edaaebc2..cd44b856 100644 --- a/doc/customization.qbk +++ b/doc/customization.qbk @@ -12,61 +12,16 @@ implementation. A fiber-scheduler must implement interface __algo__. __boost_fiber__ provides scheduler [class_link round_robin]. -In order to use a custom 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]. +[heading fiber_context] - -[class_heading sched_algorithm] - - #include - - struct sched_algorithm { - virtual ~sched_algorithm() {} - - virtual void awakened( fiber_context *) = 0; - - virtual fiber_context * pick_next() = 0; - }; - - void set_scheduling_algorithm( sched_algorithm *); - -[member_heading algorithm..awakened] - - virtual void awakened( fiber_context * f) = 0; - -[variablelist -[[Effects:] [Marks fiber `f`, to be ready to run.]] -] - -[member_heading algorithm..pick_next] - - virtual fiber_context * pick_next() = 0; - -[variablelist -[[Effects:] [Depending on the scheduling algorithm, this function returns the -fiber which has to be resumed next.]] -] - - -[ns_function_heading fibers..set_scheduling_algorithm] - - void set_scheduling_algorithm( sched_algorithm * a); - -[variablelist -[[Effects:] [Registers `a` as scheduling algorithm.]] -] - -< TODO > +The fiber_context class is mostly supposed to be opaque. However, certain +members are available for use by a custom scheduling algorithm. [endsect] diff --git a/doc/fiber.qbk b/doc/fiber.qbk index 1100bd03..85adc25a 100644 --- a/doc/fiber.qbk +++ b/doc/fiber.qbk @@ -51,7 +51,7 @@ [heading Tutorial] Each __fiber__ represents a micro-thread which will be launched and managed -cooperatively by a scheduler. Objects of type __fiber__ are only moveable. +cooperatively by a scheduler. Objects of type __fiber__ are move-only. boost::fibers::fiber f1; // not-a-fiber @@ -90,7 +90,7 @@ The spawned __fiber__ is enqueued in the list of ready-to-run fibers at construc [heading Exceptions] -A exception thrown by the function or callable object passed to the __fiber__ +An exception escaping from 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__. @@ -182,6 +182,18 @@ __interruption_enabled__. * __sleep_until__ * __yield__ * __interruption_point__ +* [member_link bounded_channel..push] +* [member_link bounded_channel..pop] +* [member_link bounded_channel..value_pop] +* [member_link unbounded_channel..push] +* [member_link unbounded_channel..pop] +* [member_link unbounded_channel..value_pop] +* [member_link future..wait] +* [member_link future..get] +* [member_link future..get_exception_ptr] +* [member_link shared_future..wait] +* [member_link shared_future..get] +* [member_link shared_future..get_exception_ptr] [#class_fiber_id] [heading Fiber IDs] @@ -272,11 +284,13 @@ operators on __fiber_id__ yield a total order for every non-equal __fiber_id__. [variablelist [[Preconditions:] [`Fn` must be copyable or movable.]] -[[Effects:] [`fn` is copied into internal storage for access by the new fiber.]] +[[Effects:] [`fn` is copied or moved into internal storage for access by the new fiber.]] [[Postconditions:] [`*this` refers to the newly created fiber of execution.]] [[Throws:] [__fiber_exception__ if an error occurs.]] [[Note:] [StackAllocator is required to allocate a stack for the internal -__econtext__.]] +__econtext__. If StackAllocator is not explicitly passed, a +__fixedsize_stack__ is used by default.]] +[[See also:] [[link stack]]] ] [heading Move constructor] @@ -313,7 +327,7 @@ value of `other.get_id()` prior to the assignment.]] [[Throws:] [Nothing.]] [[Note:] [The programmer must ensure that the destructor is never executed while the fiber is still __joinable__. Even if you know (by calling -[member_link fiber..operator safe_bool]) that the fiber has completed, you must +[member_link fiber..operator bool]) that the fiber has completed, you must still call either __join__ or __detach__ before destroying the `fiber` object.]] ] @@ -325,7 +339,7 @@ still call either __join__ or __detach__ before destroying the `fiber` object.] [[Returns:] [`true` if `*this` refers to a fiber of execution, which may or may not have completed; otherwise `false`.]] [[Throws:] [Nothing]] -[[See also:] [[member_link fiber..operator safe_bool]]] +[[See also:] [[member_link fiber..operator bool]]] ] [member_heading fiber..join] @@ -385,18 +399,26 @@ interruption enabled.]] [[Throws:] [Nothing]] ] -< TODO > -[#fiber_properties] -[heading Member function `template< typename PROPS > PROPS & properties()`] +[template_member_heading fiber..properties] template< typename PROPS > PROPS & properties(); [variablelist -[[Preconditions:] [`*this` refers to a fiber of execution.]] -[[Effects:] []] -[[Throws:] []] -[[Note:] []] +[[Preconditions:] [`*this` refers to a fiber of execution. [function_link +set_scheduling_algorithm] has been called from this thread with a pointer to a +(subclass) instance of [template_link sched_algorithm_with_properties] with the +same template argument `PROPS`. `*this` has been scheduled for execution at +least once.]] +[[Returns:] [a reference to the scheduler properties instance for `*this`.]] +[[Throws:] [`std::bad_cast` if `set_scheduling_algorithm()` was passed a +pointer to a `sched_algorithm_with_properties` subclass with some other +template parameter than `PROPS`.]] +[[Note:] [[template_link sched_algorithm_with_properties] provides a way for a +user-coded scheduler to associate extended properties, such as priority, with +a fiber instance. This method allows access to those user-provided properties +-- but only after this fiber has been scheduled for the first time.]] +[[See also:] [[link custom]]] ] [member_heading fiber..operator bool] @@ -419,7 +441,7 @@ distinguish whether the referenced fiber of execution is still running.]] [[Returns:] [`true` if `*this` does not refer to a fiber of execution or if its fiber of execution has terminated, `false` otherwise.]] [[Throws:] [Nothing]] -[[See also:] [[member_link fiber..operator safe_bool], __joinable__]] +[[See also:] [[member_link fiber..operator bool], __joinable__]] ] [member_heading fiber..swap] @@ -452,8 +474,6 @@ prior to the call.]] [[Effects:] [Directs __boost_fiber__ to use `scheduler`, which must be a concrete subclass of __algo__, as the scheduling algorithm for all fibers in the current thread.]] -[[Returns:] [Previous __algo__ instance set for the current thread, 0 if this -is the first __boost_fiber__ entry point called by the current thread.]] [[Note:] [If you want a given thread to use a non-default scheduling algorithm, make that thread call `set_scheduling_algorithm()` before any other __boost_fiber__ entry point. If no scheduler has been set for the current @@ -465,6 +485,7 @@ lifespan of the referenced `scheduler` object. The caller must eventually destroy the passed `scheduler`, just as it must allocate it in the first place.]] [[Throws:] [Nothing]] +[[See also:] [[link scheduling], [link custom]]] ] @@ -520,7 +541,7 @@ or both represent __not_a_fiber__, `false` otherwise.]] bool operator!=( id const& other) const noexcept; [variablelist -[[Returns:] [`! (other == * this)]] +[[Returns:] [[`! (other == * this)]]] [[Throws:] [Nothing.]] ] @@ -585,7 +606,7 @@ implementation-defined total order of `fiber::id` values places `*this` before fibers::fiber::id get_id() noexcept; void yield(); template< typename Clock, typename Duration > - void sleep_until( std::chrono::time_point< Clock, Duration > const& sleep_time) + void sleep_until( std::chrono::time_point< Clock, Duration > const& abs_time) template< typename Rep, typename Period > void sleep_for( std::chrono::duration< Rep, Period > const& timeout_duration); template< typename PROPS > @@ -616,7 +637,7 @@ executing fiber.]] #include template< typename Clock, typename Duration > - void sleep_until( std::chrono::time_point< Clock, Duration > const& sleep_time) + void sleep_until( std::chrono::time_point< Clock, Duration > const& abs_time) [variablelist [[Effects:] [Suspends the current fiber until the time point specified by @@ -637,8 +658,8 @@ guarantees about how soon after `abs_time` it might resume.]] [variablelist [[Effects:] [Suspends the current fiber until the time duration specified by `rel_time` has elapsed.]] -[[Throws:] [Nothing if operations of std::chrono::duration do not throw -exceptions. __fiber_interrupted__ if the current fiber is interrupted.]] +[[Throws:] [Any exceptions thrown by operations of `std::chrono::duration`. __fiber_interrupted__ if the current fiber is interrupted.]] [[Note:] [`sleep_for()` is one of the predefined __interruption_points__.]] [[Note:][The current fiber will not resume before `rel_time` has elapsed, but there are no guarantees about how soon after that it might resume.]] @@ -653,9 +674,11 @@ there are no guarantees about how soon after that it might resume.]] [variablelist [[Effects:] [Reliquishes execution control, allowing other fibers to run.]] [[Throws:] [__fiber_resource_error__ if an error occurs.]] +[[Note:] [`yield()` is 'not an interruption point. A fiber that calls +`yield()` is not suspended: it is immediately passed to the scheduler as ready +to run.]] ] -< TODO > [ns_function_heading this_fiber..properties] #include @@ -664,9 +687,19 @@ there are no guarantees about how soon after that it might resume.]] PROPS & properties(); [variablelist -[[Effects:] []] -[[Throws:] []] -[[Note:] []] +[[Preconditions:] [[function_link set_scheduling_algorithm] has been called +from this thread with a pointer to a (subclass) instance of [template_link +sched_algorithm_with_properties] with the same template argument `PROPS`.]] +[[Returns:] [a reference to the scheduler properties instance for the +currently running fiber.]] +[[Throws:] [`std::bad_cast` if `set_scheduling_algorithm()` was passed a +pointer to a `sched_algorithm_with_properties` subclass with some other +template parameter than `PROPS`.]] +[[Note:] [[template_link sched_algorithm_with_properties] provides a way for a +user-coded scheduler to associate extended properties, such as priority, with +a fiber instance. This function allows access to those user-provided +properties.]] +[[See also:] [[link custom]]] ] [ns_function_heading this_fiber..interruption_point] diff --git a/doc/fibers.qbk b/doc/fibers.qbk index f7f98b70..d67e5194 100644 --- a/doc/fibers.qbk +++ b/doc/fibers.qbk @@ -87,7 +87,7 @@ [def __barrier__ [class_link barrier]] [def __condition__ [class_link condition_variable]] [def __disable_interruption__ [class_link disable_interruption]] -[def __econtext__ ['execution_context]] +[def __econtext__ [@boost:/libs/context/doc/html/context/econtext.html ['execution_context]]] [def __fiber__ [class_link fiber]] [def __fiber_exception__ `fiber_exception`] [def __fiber_group__ [class_link fiber_group]] @@ -112,7 +112,7 @@ [def __segmented_stack_stack__ ['segmented_stack-stack]] [def __shared_future__ [template_link shared_future]] [def __stack_allocator_concept__ [link stack_allocator_concept ['stack-allocator concept]]] -[def __stack_context__ [class_link stack_context]] +[def __stack_context__ [@boost:/libs/context/doc/html/context/stack/stack_context.html `stack_context`]] [def __timed_mutex__ [class_link timed_mutex]] [def __wait_for__ [member_link future..wait_for]] [def __wait__ [member_link future..wait]] diff --git a/doc/overview.qbk b/doc/overview.qbk index d69a1bf1..3129d3a5 100644 --- a/doc/overview.qbk +++ b/doc/overview.qbk @@ -17,8 +17,8 @@ Each fiber has its own stack. A fiber can save the current execution state, including all registers and CPU flags, the instruction pointer, and the stack pointer and later restore this state. -The idea is to have multiple execution paths running on a single thread using a -sort of cooperative scheduling (versus threads, which are preemptively scheduled). The +The idea is to have multiple execution paths running on a single thread using +cooperative scheduling (versus threads, which are preemptively scheduled). The running fiber decides explicitly when it should yield to allow another fiber to run (context switching). __boost_fiber__ internally uses __econtext__ from __boost_context__; the classes in @@ -68,6 +68,11 @@ A fiber launched on a particular thread will always run on that thread. A fiber can count on thread-local storage; however that storage will be shared among all fibers running on the same thread. +The fiber synchronization objects provided by this library will not, by +default, safely synchronize fibers running on different threads. However, they +can be coerced to provide that safety by building the library with +`BOOST_FIBERS_THREADSAFE` defined. Please see [link synchronization]. + For fiber-local storage, please see __fsp__. [#blocking] @@ -90,15 +95,12 @@ entire thread. Fiber authors are encouraged to consistently use asynchronous I/O. __boost_asio__ explicitly supports fibers; other asynchronous I/O operations can straightforwardly be adapted for __boost_fiber__. -Synchronization between a fiber running on one thread and a fiber running on a -different thread is an advanced topic. - -[warning This library is ['not] an official Boost library] +[warning This library is ['not] an official Boost library.] __boost_fiber__ depends upon __boost_context__. Boost version 1.58.0 or greater is required. -[info This library is C++14-only!] +[note This library is C++14-only!] [endsect] diff --git a/doc/scheduling.qbk b/doc/scheduling.qbk index 3310a822..f17698c8 100644 --- a/doc/scheduling.qbk +++ b/doc/scheduling.qbk @@ -7,15 +7,29 @@ [section:scheduling Scheduling] -Fibers are managed by a scheduler which coordinates the sequence of fibers -running or waiting. -Each thread is required to have its own scheduler. By default, __boost_fiber__ -creates for each thread a scheduler ([class_link round_robin]). +The fibers in a thread are coordinated by a fiber manager. Fibers trade +control cooperatively, rather than preemptively: the currently-running fiber +retains control until it invokes some operation that passes control to the +manager. Each time a fiber suspends (or yields), the fiber manager consults a +scheduler to determine which fiber will run next. + +__boost_fiber__ provides the fiber manager, but the scheduler is a +customization point. (See [link custom].) + +Each thread has its own scheduler. By default, __boost_fiber__ implicitly +instantiates [class_link round_robin] for each thread. + +To prevent the library from heap-allocating the default scheduler for a given +thread, that thread must call [function_link set_scheduling_algorithm] before +any other __boost_fiber__ entry point. -A fiber-scheduler must implement interface __algo__. You are explicitly permitted to code your own __algo__ subclass, and to pass it to [function_link set_scheduling_algorithm]. +A scheduler class must implement interface __algo__. __boost_fiber__ provides +one scheduler: [class_link round_robin]. + + void thread_fn() { my_fiber_scheduler mfs; boost::fibers::set_scheduling_algorithm( & mfs); @@ -25,9 +39,13 @@ it to [function_link set_scheduling_algorithm]. [class_heading sched_algorithm] +`sched_algorithm` is the abstract base class defining the interface that a +fiber scheduler must implement. + #include - struct sched_algorithm { + struct sched_algorithm + { virtual ~sched_algorithm() {} virtual void awakened( fiber_context *) = 0; @@ -40,7 +58,9 @@ it to [function_link set_scheduling_algorithm]. virtual void awakened( fiber_context * f) = 0; [variablelist -[[Effects:] [Marks fiber `f`, to be ready to run.]] +[[Effects:] [Informs the scheduler that fiber `f` is ready to run.]] +[[Note:] [A typical scheduler implementation places `f` into a queue.]] +[[See also:] [[class_link round_robin]]] ] [member_heading sched_algorithm..pick_next] @@ -48,89 +68,51 @@ it to [function_link set_scheduling_algorithm]. virtual fiber_context * pick_next() = 0; [variablelist -[[Effects:] [Depending on the scheduling algorithm, this function returns the -fiber which is to be resumed next.]] -] - -[member_heading sched_algorithm..priority] - - virtual void priority( fiber_context *, int) noexcept = 0; - -[variablelist -[[Effects:] [Resets the priority of fiber `f`.]] -] - - -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 one -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; - }; - -[member_heading sched_algorithm..awakened] - - virtual void awakened( detail::worker_fiber * f) = 0; - -[variablelist -[[Effects:] [Marks fiber `f`, to be ready to run.]] -] - -[member_heading sched_algorithm..pick_next] - - virtual detail::worker_fiber * pick_next() = 0; - -[variablelist -[[Effects:] [Depending on the scheduling algorithm, this function returns the -fiber which is to be resumed next.]] -] - -[member_heading sched_algorithm..priority] - - virtual void priority( detail::worker_fiber *, int) noexcept = 0; - -[variablelist -[[Effects:] [Resets the priority of fiber `f`.]] +[[Returns:] [the fiber which is to be resumed next, or nullptr if there is no +ready fiber.]] +[[Note:] [A typical scheduler implementation chooses the head of the ready queue.]] +[[See also:] [[class_link round_robin]]] ] [class_heading round_robin] -This class implements __algo__ and schedules fibers in round-robin fashion -(ignores `fiber::priority()`). +This class implements __algo__ and schedules fibers in round-robin fashion. -[info The example section provides a scheduler used for migrating fibers -(work-stealing) between threads (different schedulers).] + #include + + class round_robin: public sched_algorithm + { + virtual void awakened( fiber_context *); + + virtual fiber_context * pick_next(); + }; + + void set_scheduling_algorithm( sched_algorithm *); + +[member_heading round_robin..awakened] + + virtual void awakened( fiber_context * f); + +[variablelist +[[Effects:] [Enqueues fiber `f` onto a ready queue.]] +] + +[member_heading round_robin..pick_next] + + virtual fiber_context * pick_next(); + +[variablelist +[[Returns:] [the fiber at the head of the ready queue, or 0 if the queue is +empty.]] +[[Note:] [Placing ready fibers onto a queue, and returning them from the head +of that queue, shares control between ready fibers in round-robin fashion.]] +] -< TODO > [heading customized scheduler] - +[/ sched_algorithm_with_properties] +[/ fiber_properties] +[/ allude to fiber_context?] [endsect] diff --git a/doc/stack.qbk b/doc/stack.qbk index 2ca8ae0d..993048b0 100644 --- a/doc/stack.qbk +++ b/doc/stack.qbk @@ -46,6 +46,7 @@ to `allocate()` results in undefined behaviour.] [note The memory for the stack is not required to be aligned; alignment takes place inside __econtext__.] +See also [@boost:/libs/context/doc/html/context/stack.html Boost.Context stack allocation]. [class_heading protected_fixedsize_stack] From 1dadc51ec4cb2ae18bc71254dc8321f581f07fe8 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Sun, 19 Jul 2015 22:57:56 -0400 Subject: [PATCH 5/6] Add reference documentation for custom scheduling classes. Also resolve certain previously-dangling internal documentation cross-references: custom, synchronization, scheduling, stack. Add documentation placeholders for mutex (and friends) lock(), try_lock(), unlock() methods. Add documentation placeholders for future (and shared_future) get_exception_ptr() methods. Remove documentation for obsolete future (and shared_future) swap(), operator safe_bool() and operator!() methods. --- doc/customization.qbk | 1 + doc/fiber.qbk | 2 +- doc/fibers.qbk | 6 ++ doc/future.qbk | 88 ++++------------ doc/mutexes.qbk | 142 +++++++++++++++++++++++++- doc/scheduling.qbk | 231 +++++++++++++++++++++++++++++++++++++++--- doc/stack.qbk | 1 + 7 files changed, 386 insertions(+), 85 deletions(-) diff --git a/doc/customization.qbk b/doc/customization.qbk index cd44b856..f37c67e1 100644 --- a/doc/customization.qbk +++ b/doc/customization.qbk @@ -5,6 +5,7 @@ http://www.boost.org/LICENSE_1_0.txt ] +[#custom] [section:custom Customization] __boost_fiber__ allows to customize the scheduling algorithm by a user-defined diff --git a/doc/fiber.qbk b/doc/fiber.qbk index 85adc25a..79cff29f 100644 --- a/doc/fiber.qbk +++ b/doc/fiber.qbk @@ -674,7 +674,7 @@ there are no guarantees about how soon after that it might resume.]] [variablelist [[Effects:] [Reliquishes execution control, allowing other fibers to run.]] [[Throws:] [__fiber_resource_error__ if an error occurs.]] -[[Note:] [`yield()` is 'not an interruption point. A fiber that calls +[[Note:] [`yield()` is ['not] an interruption point. A fiber that calls `yield()` is not suspended: it is immediately passed to the scheduler as ready to run.]] ] diff --git a/doc/fibers.qbk b/doc/fibers.qbk index d67e5194..7780146a 100644 --- a/doc/fibers.qbk +++ b/doc/fibers.qbk @@ -64,6 +64,11 @@ ] [template static_member_link[class_name method_name] [member_link [class_name]..[method_name]]] +[template data_member_heading[class_name member_name] +[hding [class_name]_[member_name]..Data member [`[member_name]]] +] +[template data_member_link[class_name member_name] [member_link [class_name]..[member_name]]] + [template function_heading[function_name] [hding [function_name]..Non-member function [`[function_name]()]] ] @@ -160,6 +165,7 @@ [include fiber.qbk] [include scheduling.qbk] [include stack.qbk] +[#synchronization] [section:synchronization Synchronization] [include mutexes.qbk] [include condition_variables.qbk] diff --git a/doc/future.qbk b/doc/future.qbk index 5ac0a7a7..63c14693 100644 --- a/doc/future.qbk +++ b/doc/future.qbk @@ -58,12 +58,6 @@ A __future__ contains a shared state which is not shared with any other future. future & operator=( future const& other) = delete; - void swap( future & other) noexcept; - - operator safe_bool() const noexcept; - - bool operator!() const noexcept; - bool valid() const noexcept; shared_future< R > share(); @@ -72,6 +66,8 @@ A __future__ contains a shared state which is not shared with any other future. R & get(); // member only of future< R & > template specialization void get(); // member only of future< void > template specialization + std::exception_ptr get_exception_ptr(); + void wait() const; template< class Rep, class Period > @@ -120,33 +116,6 @@ After construction is `false == other.valid()`]] [[Throws:] [Nothing.]] ] -[member_heading future..swap] - - void swap( future & other) noexcept; - -[variablelist -[[Effects:] [Swaps the shared state between other and `this`.]] -[[Throws:] [Nothing.]] -] - -[member_heading future..operator safe_bool] - - operator safe_bool() const noexcept; - -[variablelist -[[Effects:] [Returns `true` if future is valid.]] -[[Throws:] [Nothing.]] -] - -[operator_heading future..operator_not..operator!] - - bool operator!() const noexcept; - -[variablelist -[[Effects:] [Returns `false` if future is valid.]] -[[Throws:] [Nothing.]] -] - [member_heading future..valid] bool valid() const noexcept; @@ -173,11 +142,20 @@ After construction is `false == other.valid()`]] [variablelist [[Precondition:] [`true == valid()`]] -[[Effects:] [Returns the result.]] +[[Returns:] [the result. If result not yet set, waits.]] [[Postcondition:] [`false == valid()`]] [[Throws:] [__future_error__ with error condition __no_state__.]] ] +[member_heading future..get_exception_ptr] + + std::exception_ptr get_exception_ptr(); + +[variablelist +[[Precondition:] [`true == valid()`]] +[[Returns:] [the exception pointer, if set. If no result is set, waits.]] +] + [member_heading future..wait] void wait(); @@ -233,12 +211,6 @@ A __shared_future__ contains a shared state which might be shared with other fut shared_future & operator=( shared_future const& other) noexcept; - void swap( shared_future & other) noexcept; - - operator safe_bool() const noexcept; - - bool operator!() const noexcept; - bool valid() const noexcept; R const& get(); // member only of generic shared_future template @@ -310,33 +282,6 @@ return * this; [[Throws:] [Nothing.]] ] -[member_heading shared_future..swap] - - void swap( shared_future & other) noexcept; - -[variablelist -[[Effects:] [Swaps the shared state between other and `this`.]] -[[Throws:] [Nothing.]] -] - -[member_heading shared_future..operator safe_bool] - - operator safe_bool() const noexcept; - -[variablelist -[[Effects:] [Returns `true` if future is valid.]] -[[Throws:] [Nothing.]] -] - -[operator_heading shared_future..operator_not..operator!] - - bool operator!() const noexcept; - -[variablelist -[[Effects:] [Returns `false` if future is valid.]] -[[Throws:] [Nothing.]] -] - [member_heading shared_future..valid] bool valid() const noexcept; @@ -359,6 +304,15 @@ return * this; [[Throws:] [__future_error__ with error condition __no_state__.]] ] +[member_heading shared_future..get_exception_ptr] + + std::exception_ptr get_exception_ptr(); + +[variablelist +[[Precondition:] [`true == valid()`]] +[[Returns:] [the exception pointer, if set. If no result is set, waits.]] +] + [member_heading shared_future..wait] void wait(); diff --git a/doc/mutexes.qbk b/doc/mutexes.qbk index 5b341433..dc2269e6 100644 --- a/doc/mutexes.qbk +++ b/doc/mutexes.qbk @@ -19,7 +19,7 @@ mutex( mutex const& other) = delete; mutex & operator=( mutex const& other) = delete; - + void lock(); bool try_lock(); void unlock(); @@ -32,6 +32,31 @@ __lock__, __try_lock__ and __unlock__ shall be permitted. Any fiber blocked in __lock__ is suspended in the scheduler until the owning fiber releases the lock by calling __unlock__. +[member_heading mutex..lock] + + void lock(); + +[variablelist +[[Effects:] []] +] + +[member_heading mutex..try_lock] + + bool try_lock(); + +[variablelist +[[Effects:] []] +[[Returns:] []] +] + +[member_heading mutex..unlock] + + void unlock(); + +[variablelist +[[Effects:] []] +] + [class_heading timed_mutex] @@ -60,6 +85,51 @@ the lock on a given instance of __timed_mutex__ at any time. Multiple concurrent calls to __lock__, __try_lock__, __try_lock_until__, __try_lock_for__ and __unlock__ shall be permitted. +[member_heading timed_mutex..lock] + + void lock(); + +[variablelist +[[Effects:] []] +] + +[member_heading timed_mutex..try_lock] + + bool try_lock(); + +[variablelist +[[Effects:] []] +[[Returns:] []] +] + +[member_heading timed_mutex..unlock] + + void unlock(); + +[variablelist +[[Effects:] []] +] + +[template_member_heading timed_mutex..try_lock_until] + + template< typename Clock, typename Duration > + bool try_lock_until( std::chrono::time_point< Clock, Duration > const& timeout_time) + +[variablelist +[[Effects:] []] +[[Returns:] []] +] + +[template_member_heading timed_mutex..try_lock_for] + + template< typename Rep, typename Period > + bool try_lock_for( std::chrono::duration< Rep, Period > const& timeout_duration) + +[variablelist +[[Effects:] []] +[[Returns:] []] +] + [class_heading recursive_mutex] @@ -87,6 +157,31 @@ additional level of ownership of the mutex. __unlock__ must be called once for each level of ownership acquired by a single fiber before ownership can be acquired by another fiber. +[member_heading recursive_mutex..lock] + + void lock(); + +[variablelist +[[Effects:] []] +] + +[member_heading recursive_mutex..try_lock] + + bool try_lock(); + +[variablelist +[[Effects:] []] +[[Returns:] []] +] + +[member_heading recursive_mutex..unlock] + + void unlock(); + +[variablelist +[[Effects:] []] +] + [class_heading recursive_timed_mutex] @@ -121,5 +216,50 @@ ownership of the mutex. __unlock__ must be called once for each level of ownership acquired by a single fiber before ownership can be acquired by another fiber. +[member_heading recursive_timed_mutex..lock] + + void lock(); + +[variablelist +[[Effects:] []] +] + +[member_heading recursive_timed_mutex..try_lock] + + bool try_lock(); + +[variablelist +[[Effects:] []] +[[Returns:] []] +] + +[member_heading recursive_timed_mutex..unlock] + + void unlock(); + +[variablelist +[[Effects:] []] +] + +[template_member_heading recursive_timed_mutex..try_lock_until] + + template< typename Clock, typename Duration > + bool try_lock_until( std::chrono::time_point< Clock, Duration > const& timeout_time) + +[variablelist +[[Effects:] []] +[[Returns:] []] +] + +[template_member_heading recursive_timed_mutex..try_lock_for] + + template< typename Rep, typename Period > + bool try_lock_for( std::chrono::duration< Rep, Period > const& timeout_duration) + +[variablelist +[[Effects:] []] +[[Returns:] []] +] + [endsect] diff --git a/doc/scheduling.qbk b/doc/scheduling.qbk index f17698c8..56076188 100644 --- a/doc/scheduling.qbk +++ b/doc/scheduling.qbk @@ -5,6 +5,7 @@ http://www.boost.org/LICENSE_1_0.txt ] +[#scheduling] [section:scheduling Scheduling] The fibers in a thread are coordinated by a fiber manager. Fibers trade @@ -17,7 +18,7 @@ __boost_fiber__ provides the fiber manager, but the scheduler is a customization point. (See [link custom].) Each thread has its own scheduler. By default, __boost_fiber__ implicitly -instantiates [class_link round_robin] for each thread. +instantiates [class_link round_robin] as the scheduler for each thread. To prevent the library from heap-allocating the default scheduler for a given thread, that thread must call [function_link set_scheduling_algorithm] before @@ -26,9 +27,6 @@ any other __boost_fiber__ entry point. You are explicitly permitted to code your own __algo__ subclass, and to pass it to [function_link set_scheduling_algorithm]. -A scheduler class must implement interface __algo__. __boost_fiber__ provides -one scheduler: [class_link round_robin]. - void thread_fn() { my_fiber_scheduler mfs; @@ -36,6 +34,9 @@ one scheduler: [class_link round_robin]. ... } +A scheduler class must implement interface __algo__. __boost_fiber__ provides +one scheduler: [class_link round_robin]. + [class_heading sched_algorithm] @@ -58,8 +59,12 @@ fiber scheduler must implement. virtual void awakened( fiber_context * f) = 0; [variablelist -[[Effects:] [Informs the scheduler that fiber `f` is ready to run.]] -[[Note:] [A typical scheduler implementation places `f` into a queue.]] +[[Effects:] [Informs the scheduler that fiber `f` is ready to run. Fiber `f` +might be newly launched, or it might have been blocked but has just been +awakened, or it might have called [ns_function_link this_fiber..yield].]] +[[Note:] [This method advises the scheduler to add fiber `f` to its collection +of fibers ready to run. A typical scheduler implementation places `f` into a +queue.]] [[See also:] [[class_link round_robin]]] ] @@ -68,16 +73,18 @@ fiber scheduler must implement. virtual fiber_context * pick_next() = 0; [variablelist -[[Returns:] [the fiber which is to be resumed next, or nullptr if there is no +[[Returns:] [the fiber which is to be resumed next, or `nullptr` if there is no ready fiber.]] -[[Note:] [A typical scheduler implementation chooses the head of the ready queue.]] +[[Note:] [This is where the scheduler actually specifies the fiber which is to +run next. A typical scheduler implementation chooses the head of the ready +queue.]] [[See also:] [[class_link round_robin]]] ] [class_heading round_robin] -This class implements __algo__ and schedules fibers in round-robin fashion. +This class implements __algo__, scheduling fibers in round-robin fashion. #include @@ -88,8 +95,6 @@ This class implements __algo__ and schedules fibers in round-robin fashion. virtual fiber_context * pick_next(); }; - void set_scheduling_algorithm( sched_algorithm *); - [member_heading round_robin..awakened] virtual void awakened( fiber_context * f); @@ -106,13 +111,207 @@ This class implements __algo__ and schedules fibers in round-robin fashion. [[Returns:] [the fiber at the head of the ready queue, or 0 if the queue is empty.]] [[Note:] [Placing ready fibers onto a queue, and returning them from the head -of that queue, shares control between ready fibers in round-robin fashion.]] +of that queue, shares the thread between ready fibers in round-robin fashion.]] ] -[heading customized scheduler] -[/ sched_algorithm_with_properties] -[/ fiber_properties] -[/ allude to fiber_context?] +[heading Custom Scheduler Fiber Properties] + +A scheduler class directly derived from __algo__ can use any information +available from [class_link fiber_context] to implement the `sched_algorithm` +interface. But a custom scheduler might need to track additional properties +for a fiber. For instance, a priority-based scheduler would need to track a +fiber's priority. + +__boost_fiber__ provides a mechanism by which your custom scheduler can +associate custom properties with each fiber. + +[class_heading fiber_properties] + +A custom fiber properties class must be derived from `fiber_properties`. + + #include + + class fiber_properties + { + public: + fiber_properties( back_ptr f ); + + virtual ~fiber_properties() {} + + protected: + void notify(); + }; + +[heading Constructor] + + fiber_properties( back_ptr f ); + +[variablelist +[[Effects:] [Constructs base-class component of custom subclass.]] +[[Note:] [Your subclass constructor must accept a `back_ptr` and pass it to +the base-class `fiber_properties` constructor.]] +] + +[member_heading fiber_properties..notify] + + void notify(); + +[variablelist +[[Effects:] [Pass control to the custom [template_link +sched_algorithm_with_properties] subclass's [member_link +sched_algorithm_with_properties..property_change] method.]] +[[Note:] [A custom scheduler's [member_link +sched_algorithm_with_properties..pick_next] method might dynamically select +from the ready fibers, or [member_link +sched_algorithm_with_properties..awakened] might instead insert each ready +fiber into some form of ready queue for `pick_next()`. In the latter case, if +application code modifies a fiber property (e.g. priority) that should affect +that fiber's relationship to other ready fibers, the custom scheduler must be +given the opportunity to reorder its ready queue. The custom property subclass +should implement an access method to modify such a property; that access +method should call `notify()` once the new property value has been stored. +This passes control to the custom scheduler's `property_change()` method, +allowing the custom scheduler to reorder its ready queue appropriately. Use at +your discretion. Of course, if you define a property which does not affect the +behavior of the `pick_next()` method, you need not call `notify()` when that +property is modified.]] +] + +[template_heading sched_algorithm_with_properties] + +A custom scheduler that depends on a custom properties class `PROPS` should be +derived from `sched_algorithm_with_properties`. `PROPS` should be +derived from [class_link fiber_properties]. + + #include + + template< typename PROPS > + struct sched_algorithm_with_properties + { + // override this method instead of sched_algorithm::awakened() + virtual void awakened( fiber_context * f, PROPS& properties ) = 0; + + virtual fiber_context * pick_next(); + + // obtain f's associated PROPS instance + PROPS& properties( fiber_context * f); + + // override this to be notified by PROPS::notify() + virtual void property_change( fiber_context * f, PROPS & properties); + + // Override this to customize instantiation of PROPS, e.g. use a different + // allocator. Each PROPS instance is associated with a particular + // fiber_context. + virtual fiber_properties * new_properties( fiber_context * f); + }; + +[member_heading sched_algorithm_with_properties..awakened] + + virtual void awakened( fiber_context * f, PROPS& properties ); + +[variablelist +[[Effects:] [Informs the scheduler that fiber `f` is ready to run, like +[member_link sched_algorithm..awakened]. Passes the fiber's associated `PROPS` +instance.]] +[[Note:] [A `sched_algorithm_with_properties<>` subclass must override this +method instead of `sched_algorithm::awakened()`.]] +] + +[member_heading sched_algorithm_with_properties..pick_next] + + virtual fiber_context * pick_next(); + +[variablelist +[[Returns:] [the fiber which is to be resumed next, or `nullptr` if there is no +ready fiber.]] +[[Note:] [same as [member_link sched_algorithm..pick_next]]] +] + +[member_heading sched_algorithm_with_properties..properties] + + PROPS& properties( fiber_context * f); + +[variablelist +[[Returns:] [the `PROPS` instance associated with fiber `f`.]] +[[Note:] [The fiber's associated `PROPS` instance is already passed to +`awakened()` and `property_change()`. However, every [class_link +sched_algorithm] subclass is expected to track a collection of ready +[class_link fiber_context] instances. This method allows your custom scheduler +to retrieve the [class_link fiber_properties] subclass instance for any +`fiber_context` in its collection.]] +] + +[member_heading sched_algorithm_with_properties..property_change] + + virtual void property_change( fiber_context * f, PROPS & properties); + +[variablelist +[[Effects:] [Notify the custom scheduler of a possibly-relevant change to a +property belonging to fiber `f`. `properties` contains the new values of +all relevant properties.]] +[[Note:] [This method is only called when a custom [class_link +fiber_properties] subclass explicitly calls [member_link +fiber_properties..notify].]] +] + +[member_heading sched_algorithm_with_properties..new_properties] + + virtual fiber_properties * new_properties( fiber_context * f); + +[variablelist +[[Returns:] [A new instance of [class_link fiber_properties] subclass +`PROPS`.]] +[[Note:] [By default, `sched_algorithm_with_properties<>::new_properties()` +simply returns `new PROPS(f)`, placing the `PROPS` instance on the heap. +Override this method to allocate `PROPS` some other way. The returned +`fiber_properties` pointer must point to the `PROPS` instance to be associated +with fiber `f`.]] +] + +[class_heading fiber_context] + +While you are free to treat `fiber_context*` as an opaque token, certain +`fiber_context` members may be useful to a custom scheduler implementation. + +(Most `fiber_context` members are implementation details; most of interest are +implementations of [class_link fiber] methods.) + + #include + + class fiber_context + { + public: + + fiber_context * nxt; + + static fiber_context * main_fiber(); + + }; + +[data_member_heading fiber_context..nxt] + + fiber_context * nxt; + +[variablelist +[[Note:] [This link pointer may be used to help implement a custom scheduler's +ready queue. It is overwritten by the fiber manager every time this +`fiber_context` is returned by [member_link sched_algorithm..pick_next], but +between the time the fiber manager passes this `fiber_context` to [member_link +sched_algorithm..awakened] (or [member_link +sched_algorithm_with_properties..awakened]) and the time your `pick_next()` +returns it to the fiber manager, it is available for use by your custom +scheduler.]] +] + +[static_member_heading fiber_context..main_fiber] + + static fiber_context * main_fiber(); + +[variablelist +[[Returns:] [the `fiber_context` associated with the "main" fiber of the +thread: the one implicitly created by the thread itself, rather than one +explicitly created by __boost_fiber__.]] +] [endsect] diff --git a/doc/stack.qbk b/doc/stack.qbk index 993048b0..52e1a7a2 100644 --- a/doc/stack.qbk +++ b/doc/stack.qbk @@ -5,6 +5,7 @@ http://www.boost.org/LICENSE_1_0.txt ] +[#stack] [section:stack Stack allocation] A __fiber__ uses internally a __econtext__ which manages a set of registers and a stack. From a4de0bb7a583c1cf861f068ff937072eef3b8fa5 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Mon, 20 Jul 2015 09:40:01 -0400 Subject: [PATCH 6/6] More documentation. Add note explaining why synchronization objects can neither be moved nor copied. Introduce line breaks for some code lines that get broken strangely by doc toolchain. Eliminate reference to mutex::scoped_lock. --- doc/condition_variables.qbk | 54 ++++++++++++++++++++++--------------- doc/fibers.qbk | 9 +++++++ doc/future.qbk | 2 +- doc/queue.qbk | 14 +++++----- doc/stack.qbk | 26 +++++++++--------- 5 files changed, 63 insertions(+), 42 deletions(-) diff --git a/doc/condition_variables.qbk b/doc/condition_variables.qbk index 7030453f..fd89d6ec 100644 --- a/doc/condition_variables.qbk +++ b/doc/condition_variables.qbk @@ -18,7 +18,7 @@ class condition_variable; class condition_variable_any; -The class `condition_variable` provides a mechanism for one fiber to wait for +The class `condition_variable` provides a mechanism for a fiber to wait for notification on `condition_variable`. When the fiber awakens from the wait, then it checks to see if the appropriate condition is now true, and continues if so. If the condition is not true, then the fiber calls `wait` again to resume @@ -26,7 +26,7 @@ waiting. In the simplest case, this condition is just a boolean variable: boost::fibers::condition_variable cond; boost::fibers::mutex mtx; - bool data_ready; + bool data_ready = false; void process_data(); @@ -107,16 +107,22 @@ optimize as described for `boost::thread::condition_variable`. void wait( LockType & lk, Pred predicate); template< typename LockType, typename Clock, typename Duration > - cv_status wait_until( LockType & lk, std::chrono::time_point< Clock, Duration > const& timeout_time); + cv_status wait_until( LockType & lk, + std::chrono::time_point< Clock, Duration > const& timeout_time); template< typename LockType, typename Clock, typename Duration, typename Pred > - bool wait_until( LockType & lk, std::chrono::time_point< Clock, Duration > const& timeout_time, Pred pred); + bool wait_until( LockType & lk, + std::chrono::time_point< Clock, Duration > const& timeout_time, + Pred pred); template< typename LockType, typename Rep, typename Period > - cv_status wait_for( LockType & lk, std::chrono::duration< Rep, Period > const& timeout_duration); + cv_status wait_for( LockType & lk, + std::chrono::duration< Rep, Period > const& timeout_duration); template< typename LockType, typename Rep, typename Period, typename Pred > - bool wait_for( LockType & lk, std::chrono::duration< Rep, Period > const& timeout_duration, Pred pred); + bool wait_for( LockType & lk, + std::chrono::duration< Rep, Period > const& timeout_duration, + Pred pred); }; typedef condition_variable condition_variable_any; @@ -204,36 +210,39 @@ fibers calling `wait` on `*this` must wait on `lk` objects governing the ['same] `mutex`. Three distinct objects are involved in any `condition_variable::wait()` call: the `condition_variable` itself, the `mutex` coordinating access between fibers and a lock object (e.g. -`mutex::scoped_lock`). In some sense it would be nice if the +`std::lock_guard`). In some sense it would be nice if the `condition_variable`'s constructor could accept the related `mutex` object, enforcing agreement across all `wait()` calls; but the existing APIs prevent that. Instead we must require the `wait()` call to accept a reference to the local lock object. It is an error to reuse a given `condition_variable` instance with lock objects that reference ['different] underlying `mutex` -objects. It would be like a road intersection with traffic lights disconnected -from one another: sooner or later a collision will result.]] +objects. It would be like a road intersection with traffic lights independent +of one another: sooner or later a collision will result.]] ] [template_member_heading condition_variable..wait_until] template< typename LockType, typename Clock, typename Duration > - cv_status wait_until( LockType & lk, std::chrono::time_point< Clock, Duration > const& timeout_time); + cv_status wait_until( LockType & lk, + std::chrono::time_point< Clock, Duration > const& timeout_time); template< typename LockType, typename Clock, typename Duration, typename Pred > - bool wait_until( LockType & lk, std::chrono::time_point< Clock, Duration > const& timeout_time, Pred pred); + bool wait_until( LockType & lk, + std::chrono::time_point< Clock, Duration > const& timeout_time, + Pred pred); [variablelist [[Precondition:] [`lk` is locked by the current fiber, and either no other fiber is currently waiting on `*this`, or the execution of the `mutex()` member function on the `lk` objects supplied in the calls to `wait`, `wait_for` or `wait_until` in all the fibers currently waiting on `*this` would return the -same value as `lk->mutex()` for this call to `wait`.]] +same value as `lk.mutex()` for this call to `wait_until`.]] [[Effects:] [Atomically call `lk.unlock()` and blocks the current fiber. The fiber will unblock when notified by a call to `this->notify_one()` or -`this->notify_all()`, when the time as reported by `clock_type::now()` +`this->notify_all()`, when the system time would be equal to or later than the specified `timeout_time`, or spuriously. When the fiber is unblocked (for whatever reason), the lock is reacquired by -invoking `lk.lock()` before the call to `wait` returns. The lock is also +invoking `lk.lock()` before the call to `wait_until` returns. The lock is also reacquired by invoking `lk.lock()` if the function exits with an exception. The member function accepting `pred` is shorthand for: `` @@ -253,27 +262,30 @@ __interrupt__ on the __fiber__ object associated with the current fiber of execution.]] [[Returns:] [The overload without `pred` returns `cv_status::no_timeout` if awakened by `notify_one()` or `notify_all()`, or `cv_status::timeout` if -awakened because `clock_type::now()` is past `timeout_time`.]] +awakened because the system time is past `timeout_time`.]] [[Returns:] [The overload accepting `pred` returns `false` if the call is returning because the time specified by `timeout_time` was reached and the predicate returns `false`, `true` otherwise.]] -[[Note:] [See [*Note] for `wait()`.]] +[[Note:] [See [*Note] for [member_link condition_variable..wait].]] ] [template_member_heading condition_variable..wait_for] template< typename LockType, typename Rep, typename Period > - cv_status wait_for( LockType & lk, std::chrono::duration< Rep, Period > const& timeout_duration); + cv_status wait_for( LockType & lk, + std::chrono::duration< Rep, Period > const& timeout_duration); template< typename LockType, typename Rep, typename Period, typename Pred > - bool wait_for( LockType & lk, std::chrono::duration< Rep, Period > const& timeout_duration, Pred pred); + bool wait_for( LockType & lk, + std::chrono::duration< Rep, Period > const& timeout_duration, + Pred pred); [variablelist [[Precondition:] [`lk` is locked by the current fiber, and either no other fiber is currently waiting on `*this`, or the execution of the `mutex()` member function on the `lk` objects supplied in the calls to `wait`, `wait_for` or `wait_until` in all the fibers currently waiting on `*this` would return the -same value as `lk->mutex()` for this call to `wait`.]] +same value as `lk.mutex()` for this call to `wait_for`.]] [[Effects:] [Atomically call `lk.unlock()` and blocks the current fiber. The fiber will unblock when notified by a call to `this->notify_one()` or `this->notify_all()`, when a time interval equal to or greater than the @@ -281,7 +293,7 @@ specified `timeout_duration` has elapsed, or spuriously. When the fiber is unblocked (for whatever reason), the lock is reacquired by invoking `lk.lock()` before the call to `wait` returns. The lock is also reacquired by invoking `lk.lock()` if the function exits with an exception. -The member function accepting `pred` is shorthand for: `` +The `wait_for()` member function accepting `pred` is shorthand for: `` while ( ! pred() ) { if ( cv_status::timeout == wait_for( lk, timeout_duration) ) { @@ -303,7 +315,7 @@ awakened because at least `timeout_duration` has elapsed.]] [[Returns:] [The overload accepting `pred` returns `false` if the call is returning because at least `timeout_duration` has elapsed and the predicate returns `false`, `true` otherwise.]] -[[Note:] [See [*Note] for `wait()`.]] +[[Note:] [See [*Note] for [member_link condition_variable..wait].]] ] [endsect] diff --git a/doc/fibers.qbk b/doc/fibers.qbk index 7780146a..fada0608 100644 --- a/doc/fibers.qbk +++ b/doc/fibers.qbk @@ -116,6 +116,7 @@ [def __segmented_stack__ [class_link segmented_stack]] [def __segmented_stack_stack__ ['segmented_stack-stack]] [def __shared_future__ [template_link shared_future]] +[def __stack_allocator__ ['stack_allocator]] [def __stack_allocator_concept__ [link stack_allocator_concept ['stack-allocator concept]]] [def __stack_context__ [@boost:/libs/context/doc/html/context/stack/stack_context.html `stack_context`]] [def __timed_mutex__ [class_link timed_mutex]] @@ -167,6 +168,14 @@ [include stack.qbk] [#synchronization] [section:synchronization Synchronization] + +In general, __boost_fiber__ synchronization objects can neither be moved nor +copied. A synchronization object acts as a mutually-agreed rendezvous point +between different fibers. If such an object were copied somewhere else, the +new copy would have no consumers. If such an object were ['moved] somewhere +else, leaving the original instance in an unspecified state, existing +consumers would behave strangely. + [include mutexes.qbk] [include condition_variables.qbk] [include barrier.qbk] diff --git a/doc/future.qbk b/doc/future.qbk index 63c14693..cf9cabaa 100644 --- a/doc/future.qbk +++ b/doc/future.qbk @@ -249,7 +249,7 @@ After construction is `false == other.valid()`.]] [heading Copy constructor] - shared_future( shared_future cosnt& other) noexcept; + shared_future( shared_future const& other) noexcept; [variablelist [[Effects:] [Constructs a future with the shared state of other. diff --git a/doc/queue.qbk b/doc/queue.qbk index 5ac7d909..85559a37 100644 --- a/doc/queue.qbk +++ b/doc/queue.qbk @@ -95,10 +95,11 @@ channel operations return the state of the channel. channel_op_status try_pop( value_type & va); template< typename Rep, typename Period > channel_op_status pop_wait_for( value_type & va, - std::chrono::duration< Rep, Period > const& timeout_duration); + std::chrono::duration< Rep, Period > const& timeout_duration); template< typename Clock, typename Duration > channel_op_status pop_wait_until( value_type & va, - std::chrono::time_point< Clock, Duration > const& timeout_time); + std::chrono::time_point< Clock, + Duration > const& timeout_time); }; [member_heading unbounded_channel..is_closed] @@ -118,7 +119,8 @@ channel operations return the state of the channel. [variablelist [[Effects:] [Deactivates the channel. No values can be put after calling `this->close()`. Fibers blocked in `this->pop()`, `this->pop_wait_for()` -or `this->pop_wait_until()` will return `closed`.]] +or `this->pop_wait_until()` will return `closed`. Fibers blocked in +`this->value_pop()` will receive an exception.]] [[Throws:] [Nothing.]] [[Note:] [`close()` is like closing a pipe. It informs waiting consumers that no more values will arrive.]] @@ -140,7 +142,7 @@ non-empty.]] channel_op_status push( value_type && va); [variablelist -[[Effects:] [Enchannels the value in the channel and wakes up a fiber blocked on +[[Effects:] [Enqueues the value in the channel and wakes up a fiber blocked on `this->pop()`, `this->pop_wait_for()` or `this->pop_wait_until()`.]] [[Throws:] [Nothing.]] ] @@ -150,9 +152,9 @@ non-empty.]] channel_op_status pop( value_type & va); [variablelist -[[Effects:] [Dechannels a value from the channel. If the channel `is_empty()`, the +[[Effects:] [Dequeues a value from the channel. If the channel `is_empty()`, the fiber gets suspended until at least one new item is `push()`ed (return value -`success` and `va` contains dechanneld value) or the channel gets `close()`d +`success` and `va` contains dequeued value) or the channel gets `close()`d (return value `closed`).]] [[Throws:] [__fiber_interrupted__]] ] diff --git a/doc/stack.qbk b/doc/stack.qbk index 52e1a7a2..5645bce7 100644 --- a/doc/stack.qbk +++ b/doc/stack.qbk @@ -16,7 +16,7 @@ which is required to model a __stack_allocator_concept__. [heading __stack_allocator_concept__] A __stack_allocator__ must satisfy the __stack_allocator_concept__ requirements shown in the following table, in which `a` is an object of a -__stack_allocator__ type, `sctx` is a `stack_context`, and `size` is a `std::size_t`: +__stack_allocator__ type, `sctx` is a __stack_context__, and `size` is a `std::size_t`: [table [[expression][return type][notes]] @@ -27,7 +27,7 @@ __stack_allocator__ type, `sctx` is a `stack_context`, and `size` is a `std::siz ] [ [`a.allocate()`] - [`stack_context`] + [__stack_context__] [creates a stack] ] [ @@ -41,8 +41,8 @@ __stack_allocator__ type, `sctx` is a `stack_context`, and `size` is a `std::siz against exceeding the context's available stack size rather than leaving it as undefined behaviour.] -[important Calling `deallocate()` with a `stack_context` not previously passed -to `allocate()` results in undefined behaviour.] +[important Calling `deallocate()` with a __stack_context__ not obtained from +`allocate()` results in undefined behaviour.] [note The memory for the stack is not required to be aligned; alignment takes place inside __econtext__.] @@ -57,9 +57,9 @@ It appends a guard page at the end of each stack to protect against exceeding the stack. If the guard page is accessed (read or write operation) a segmentation fault/access violation is generated by the operating system. -[important Using __pfixedsize_stack__ is expensive. That is, launching a -new fiber with a new stack is expensive; the allocated stack is just as -efficient to use as any other stack.] +[important Using __pfixedsize_stack__ is expensive. Launching a new fiber with +a stack of this type incurs the overhead of setting the memory protection; +once allocated, this stack is just as efficient to use as __fixedsize_stack__.] [note The appended `guard page` is [*not] mapped to physical memory, only virtual addresses are used.] @@ -76,19 +76,17 @@ end of each stack. The memory is simply managed by `std::malloc()` and [class_heading segmented_stack] -__boost_fiber__ supports usage of a __segmented_stack__, e. g. the size of +__boost_fiber__ supports usage of a __segmented_stack__, i.e. the stack grows on demand. The fiber is created with a minimal stack size -and will be increased as required. +which will be increased as required. Class __segmented_stack__ models the __stack_allocator_concept__. In contrast to __pfixedsize_stack__ and __fixedsize_stack__ it creates a stack which grows on demand. [note Segmented stacks are currently only supported by [*gcc] from version -[*4.7] [*clang] from version [*3.4] onwards. In order to use a -__segmented_stack__ __boost_fiber__ must be built with +[*4.7] and [*clang] from version [*3.4] onwards. In order to use a +__segmented_stack__, __boost_fiber__ must be built with property `segmented-stacks`, e.g. [*toolset=gcc segmented-stacks=on] at b2/bjam -command line. - -[endsect] +command line.] [endsect]