diff --git a/doc/barrier.qbk b/doc/barrier.qbk index 2b1f77ba..6fd9f927 100644 --- a/doc/barrier.qbk +++ b/doc/barrier.qbk @@ -15,6 +15,27 @@ barrier they must wait until all `n` fibers have arrived. Once the `n`-th fiber has reached the barrier, all the waiting fibers can proceed, and the barrier is reset. +The fact that the barrier automatically resets is significant. Consider a case +in which you launch some number of fibers and want to wait only until the +first of them has completed. You might be tempted to use a `barrier(2)` as the +synchronization mechanism, making each new fiber call its [member_link +barrier..wait] method, then calling `wait()` in the launching fiber to wait +until the first other fiber completes. + +That will in fact unblock the launching fiber. The unfortunate part is that it +will continue blocking the ['remaining] fibers. + +Consider the following scenario: + +# Fiber "main" launches fibers A, B, C and D, then calls `barrier::wait()`. +# Fiber C finishes first and likewise calls `barrier::wait()`. +# Fiber "main" is unblocked, as desired. +# Fiber B calls `barrier::wait()`. Fiber B is ['blocked]! +# Fiber A calls `barrier::wait()`. Fibers A and B are unblocked. +# Fiber D calls `barrier::wait()`. Fiber D is blocked indefinitely. + +(See also [link wait_first_simple_section when_any, simple completion].) + [note It is unwise to tie the lifespan of a barrier to any one of its participating fibers. Although conceptually all waiting fibers awaken "simultaneously," because of the nature of fibers, in practice they will diff --git a/doc/customization.qbk b/doc/customization.qbk index ba0f3b10..872738a2 100644 --- a/doc/customization.qbk +++ b/doc/customization.qbk @@ -85,19 +85,12 @@ allocating `priority_props` instances on the heap. [heading Replace Default Scheduler] -You must call [function_link set_scheduling_algorithm] at the start of each +You must call [function_link use_scheduling_algorithm] at the start of each thread on which you want __boost_fiber__ to use your custom scheduler rather than its own default [class_link round_robin]. Specifically, you must call -`set_scheduling_algorithm()` before performing any other __boost_fiber__ +`use_scheduling_algorithm()` before performing any other __boost_fiber__ operations on that thread. -It works to instantiate your custom [template_link -sched_algorithm_with_properties] subclass on the stack at the start of your -thread function, passing the instance pointer to `set_scheduling_algorithm()`. -This ensures that your scheduler instance will live as long as the thread -itself. (Of course `main()` is, in effect, the thread function for the -application's main thread.) - [main] [heading Use Properties] diff --git a/doc/fiber.qbk b/doc/fiber.qbk index 83a1db8d..304c4930 100644 --- a/doc/fiber.qbk +++ b/doc/fiber.qbk @@ -24,7 +24,8 @@ template< typename PROPS > struct sched_algorithm_with_properties; class round_robin; - void set_scheduling_algorithm( sched_algorithm * al) + template< typename SchedAlgo, typename ... Args > + void use_scheduling_algorithm( Args && ... args); std::size_t ready_fibers(); template< typename Rep, typename Period > @@ -111,6 +112,13 @@ __not_a_fiber__. The fiber object may then safely be destroyed. boost::fibers::fiber( some_fn).detach(); +__boost_fiber__ provides a number of ways to wait for a running fiber to +complete. You can coordinate even with a detached fiber using a [class_link +mutex], or [class_link condition_variable], or any of the other [link +synchronization synchronization objects] provided by the library. + +If a detached fiber is still running when the thread's main fiber terminates, +that fiber will be [link interruption interrupted] and shut down. [heading Joining] @@ -277,7 +285,8 @@ operators on __fiber_id__ yield a total order for every non-equal __fiber_id__. void swap( fiber & l, fiber & r) noexcept; - void set_scheduling_algorithm( sched_algorithm * al) + template< typename SchedAlgo, typename ... Args > + void use_scheduling_algorithm( Args && ... args); std::size_t ready_fibers(); @@ -429,14 +438,13 @@ interruption enabled.]] [variablelist [[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.]] +use_scheduling_algorithm] has been called from this thread with a subclass 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`.]] +[[Throws:] [`std::bad_cast` if `use_scheduling_algorithm()` was called with 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 @@ -489,24 +497,21 @@ prior to the call.]] [[Effects:] [Same as `l.swap( r)`.]] ] -[function_heading set_scheduling_algorithm] +[function_heading use_scheduling_algorithm] - void set_scheduling_algorithm( sched_algorithm* scheduler ); + template< typename SchedAlgo, typename ... Args > + void use_scheduling_algorithm( Args && ... args); [variablelist -[[Effects:] [Directs __boost_fiber__ to use `scheduler`, which must be a +[[Effects:] [Directs __boost_fiber__ to use `SchedAlgo`, which must be a concrete subclass of __algo__, as the scheduling algorithm for all fibers in -the current thread.]] +the current thread. Pass any required `SchedAlgo` constructor arguments as +`args`.]] [[Note:] [If you want a given thread to use a non-default scheduling -algorithm, make that thread call `set_scheduling_algorithm()` before any other +algorithm, make that thread call `use_scheduling_algorithm()` before any other __boost_fiber__ entry point. If no scheduler has been set for the current thread by the time __boost_fiber__ needs to use it, the library will create a default [class_link round_robin] instance for this thread.]] -[[Note:] [`set_scheduling_algorithm()` does ['not] take ownership of the -passed `sched_algorithm*`: __boost_fiber__ does not claim responsibility for the -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 Scheduling], [link custom Customization]]] ] @@ -744,14 +749,14 @@ to run.]] PROPS & properties(); [variablelist -[[Preconditions:] [[function_link set_scheduling_algorithm] has been called -from this thread with a pointer to a (subclass) instance of [template_link +[[Preconditions:] [[function_link use_scheduling_algorithm] has been called +from this thread with a subclass 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`.]] +[[Throws:] [`std::bad_cast` if `use_scheduling_algorithm()` was called with 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 diff --git a/doc/future.qbk b/doc/future.qbk index 14d234bf..de52e956 100644 --- a/doc/future.qbk +++ b/doc/future.qbk @@ -129,10 +129,22 @@ After construction [^[post_valid] == other.valid()]]] [variablelist [[Effects:] [Destroys the [xfuture]; ownership is abandoned[end]]] [[Throws:] [Nothing.]] +[[Note:] [[`~[xfuture]()] does ['not] block the calling fiber.]] ] ] [future_dtor_variablelist future .] +Consider a sequence such as: + +# instantiate [template_link promise] +# obtain its `future<>` via [member_link promise..get_future] +# launch [class_link fiber], capturing `promise<>` +# destroy `future<>` +# call [member_link promise..set_value] + +The final `set_value()` call succeeds, but the value is silently discarded: no +additional `future<>` can be obtained from that `promise<>`. + [operator_heading future..operator_assign..operator=] future & operator=( future && other) noexcept; diff --git a/doc/performance.qbk b/doc/performance.qbk index 6b420731..9d165d95 100644 --- a/doc/performance.qbk +++ b/doc/performance.qbk @@ -18,27 +18,31 @@ synchronization, capable of synchronizing fibers running on different threads. The columns labeled [*fiber (raw)] were compiled with [link cross_thread_sync `BOOST_FIBERS_NO_ATOMICS`]. -[table [@../../performance/overhead_join.cpp Overhead of creating and joining] +[table Overhead of creating and joining [[thread] [fiber (atomics)] [fiber (raw)] [tbb] [qthread]] [[31 \u00b5s] [1.1 \u00b5s] [955 ns] [570 ns] [620 ns]] ] +(from [@../../performance/overhead_join.cpp overhead_join.cpp]) -[table [@../../performance/overhead_detach.cpp Overhead of detach] +[table Overhead of detach [[thread] [fiber (atomics)] [fiber (raw)]] [[20 \u00b5s] [3.2 \u00b5s] [3.2 \u00b5s]] ] +(from [@../../performance/overhead_detach.cpp overhead_detach.cpp]) -[table [@../../performance/overhead_yield.cpp Overhead of yield] +[table Overhead of yield [[thread] [fiber (atomics)] [fiber (raw)]] [[38 \u00b5s] [1.3 \u00b5s] [1.1 \u00b5s]] ] +(from [@../../performance/overhead_yield.cpp overhead_yield.cpp]) -[table [@../../performance/overhead_future.cpp Overhead of waiting on a future] +[table Overhead of waiting on a future [[thread] [fiber (atomics)] [fiber (raw)]] [[32 \u00b5s] [3.0 \u00b5s] [2.4 \u00b5s]] ] +(from [@../../performance/overhead_future.cpp overhead_future.cpp]) -[table [@../../performance/scale_join.cpp Scaling of creating and joining] +[table Scaling of creating and joining [[average of] [thread] [fiber (atomics)] [fiber (raw)]] [[10] [50.65 \u00b5s] [4.83 \u00b5s] [3.76 \u00b5s]] [[50] [52.99 \u00b5s] [4.84 \u00b5s] [2.78 \u00b5s]] @@ -48,6 +52,6 @@ The columns labeled [*fiber (raw)] were compiled with [link cross_thread_sync [[5000] [42.30 \u00b5s] [5.07 \u00b5s] [4.57 \u00b5s]] [[10000] [41.07 \u00b5s] [5.12 \u00b5s] [4.21 \u00b5s]] ] - +(from [@../../performance/scale_join.cpp scale_join.cpp]) [endsect] diff --git a/doc/scheduling.qbk b/doc/scheduling.qbk index 236b3af4..ad01d137 100644 --- a/doc/scheduling.qbk +++ b/doc/scheduling.qbk @@ -20,13 +20,12 @@ customization point. (See [link custom Customization].) Each thread has its own scheduler. By default, __boost_fiber__ implicitly instantiates [class_link round_robin] as the scheduler for each thread. -You are explicitly permitted to code your own __algo__ subclass, and to pass -it to [function_link set_scheduling_algorithm]. +You are explicitly permitted to code your own __algo__ subclass, and to +specify it to [function_link use_scheduling_algorithm]. void thread_fn() { - my_fiber_scheduler mfs; - boost::fibers::set_scheduling_algorithm( & mfs); + boost::fibers::use_scheduling_algorithm(); ... } diff --git a/doc/when_any.qbk b/doc/when_any.qbk index cac4d150..4d170e19 100644 --- a/doc/when_any.qbk +++ b/doc/when_any.qbk @@ -84,6 +84,7 @@ This function will feature in the example calls to the various functions presented below. [section when_any] +[#wait_first_simple_section] [section when_any, simple completion] The simplest case is when you only need to know that the first of a set of