summaryrefslogtreecommitdiff
path: root/libs/thread/doc
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@baserock.org>2015-04-08 03:09:47 +0000
committer <>2015-05-05 14:37:32 +0000
commitf2541bb90af059680aa7036f315f052175999355 (patch)
treea5b214744b256f07e1dc2bd7273035a7808c659f /libs/thread/doc
parented232fdd34968697a68783b3195b1da4226915b5 (diff)
downloadboost-tarball-master.tar.gz
Imported from /home/lorry/working-area/delta_boost-tarball/boost_1_58_0.tar.bz2.HEADboost_1_58_0master
Diffstat (limited to 'libs/thread/doc')
-rw-r--r--libs/thread/doc/async_executors.qbk935
-rw-r--r--libs/thread/doc/changes.qbk62
-rw-r--r--libs/thread/doc/external_locking.qbk26
-rw-r--r--libs/thread/doc/future_ref.qbk20
-rwxr-xr-xlibs/thread/doc/futures.qbk2
-rw-r--r--libs/thread/doc/mutex_concepts.qbk8
-rw-r--r--libs/thread/doc/parallel.qbk482
-rw-r--r--libs/thread/doc/scoped_thread.qbk26
-rw-r--r--libs/thread/doc/sync_queues_ref.qbk26
-rw-r--r--libs/thread/doc/synchronized_value_ref.qbk22
-rw-r--r--libs/thread/doc/thread.qbk7
-rw-r--r--libs/thread/doc/thread_ref.qbk12
12 files changed, 1464 insertions, 164 deletions
diff --git a/libs/thread/doc/async_executors.qbk b/libs/thread/doc/async_executors.qbk
index 4b22c620c..c9311bdd5 100644
--- a/libs/thread/doc/async_executors.qbk
+++ b/libs/thread/doc/async_executors.qbk
@@ -10,7 +10,7 @@
[warning These features are experimental and subject to change in future versions. There are not too much tests yet, so it is possible that you can find out some trivial bugs :(]
-[note These features are based on the [@http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2013/n3785.pdf [*N3785 - Executors and Schedulers revision 3]] C++1y proposal from Chris Mysen, Niklas Gustafsson, Matt Austern, Jeffrey Yasskin. The text that follows has been adapted from tis paper to show the differences.]
+[note These features are based on the [@http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2013/n3785.pdf [*N3785 - Executors and Schedulers revision 3]] C++1y proposal from Chris Mysen, Niklas Gustafsson, Matt Austern, Jeffrey Yasskin. The text that follows has been adapted from this paper to show the differences.]
Executors are objects that can execute units of work packaged as function objects. Boost.Thread differs from N3785 mainly in the an Executor doesn't needs to inherit from an abstract class Executor. Static polymorphism is used instead and type erasure is used internally.
@@ -158,6 +158,24 @@ In order to manage with all the clocks, there are two alternatives:
The library chose the first of those options, largely for simplicity.
]
+[heading Scheduled work]
+
+The approach of this library respect to scheduled work of the N3785 proposal is quite different. Instead of adding the scheduled operations to a specific scheduled_executor polymorphic interface, we opt by adding a specific `scheduler` class that is not an executor and knows how to manage with the scheduling of timed tasks `submit_at`/`submit_after`.
+
+
+`scheduler` provides executor factories `at`/`after` given a specific `time_point` or a `duration`. The built executors wrap a reference to this scheduler and the time at which the submitted task will be executed.
+
+If we want to schedule these operations on an existing executor (as `serial_executor` does), these classes provide a `on` factory taking another executor as parameter and wraps both instance on the returned executor.
+
+ sch.on(tp).after(seconds(i)).submit(boost::bind(fn,i));
+
+This has several advantages:
+
+* The scheduled operations are available for all the executors via wrappers.
+* The template functions could accept any chrono `time_point` and `duration` respectively as we are not working with virtual functions.
+
+In order to manage with all the clocks, this library propose generic solution. `scheduler<Clock>` know how to manage with the `submit_at`/`submit_after` `Clock::time_point`/`Clock::duration` tasks. Note that the durations on different clocks differ.
+
[heading Not Handled Exceptions]
As in N3785 and based on the same design decision than `std`/`boost::thread` if a user closure throws an exception, the executor must call the `std::terminate` function.
Note that when we combine `boost::async` and `Executors`, the exception will be caught by the closure associated to the returned future, so that the exception is stored on the returned future, as for the other `async` overloads.
@@ -182,49 +200,108 @@ An alternative is to make async return a cancelable_task but this will need also
[/
The library would provide in the future a cancelable_task that could support cancelation.
- class cancelation_state {
- std::atomic<bool> requested; std::atomic<bool> enabled; std::condition_variable* cond; std::mutex cond_mutex;
+ class cancelation_state
+ {
+ std::atomic<bool> requested;
+ std::atomic<bool> enabled;
+ std::condition_variable* cond;
+ std::mutex cond_mutex;
public:
- cancelation_state(): thread_cond(0) {} void cancel() { requested.store(true,std::memory_order_relaxed); std::lock_guard<std::mutex> lk(cond_mutex); if(cond) { cond->notify_all(); } } bool cancellation_requested() const { return requested.load(std::memory_order_relaxed); }
- void enable() { enable.store(true,std::memory_order_relaxed); } void disable() { enable.store(false,std::memory_order_relaxed); } bool cancellation_enabled() const { return enabled.load(std::memory_order_relaxed); } void set_condition_variable(std::condition_variable& cv) { std::lock_guard<std::mutex> lk(cond_mutex); cond = &cv; } void clear_condition_variable() { std::lock_guard<std::mutex> lk(cond_mutex); cond = 0; } struct clear_cv_on_destruct { ~clear_cv_on_destruct() { this_thread_interrupt_flag.clear_condition_variable(); } };
- void cancelation_point();
- void cancelable_wait(std::condition_variable& cv, std::unique_lock<std::mutex>& lk) { cancelation_point(); this_cancelable_state.set_condition_variable(cv); this_cancelable_state::clear_cv_on_destruct guard; interruption_point();
- cv.wait_for(lk, std::chrono::milliseconds(1)); this_cancelable_state.clear_condition_variable(); cancelation_point(); }
- class disable_cancelation
+ cancelation_state() :
+ thread_cond(0)
+ {
+ }
+ void cancel()
+ {
+ requested.store(true, std::memory_order_relaxed);
+ std::lock_guard < std::mutex > lk(cond_mutex);
+ if (cond)
{
- public:
- disable_cancelation(const disable_cancelation&) = delete;
- disable_cancelation& operator=(const disable_cancelation&) = delete;
- disable_cancelation(cancelable_closure& closure) noexcept;
- ~disable_cancelation() noexcept;
- };
- class restore_cancelation
+ cond->notify_all();
+ }
+ }
+ bool cancellation_requested() const
+ {
+ return requested.load(std::memory_order_relaxed);
+ }
+ void enable()
+ {
+ enable.store(true, std::memory_order_relaxed);
+ }
+ void disable()
+ {
+ enable.store(false, std::memory_order_relaxed);
+ }
+ bool cancellation_enabled() const
+ {
+ return enabled.load(std::memory_order_relaxed);
+ }
+ void set_condition_variable(std::condition_variable& cv)
+ {
+ std::lock_guard < std::mutex > lk(cond_mutex);
+ cond = &cv;
+ }
+ void clear_condition_variable()
+ {
+ std::lock_guard < std::mutex > lk(cond_mutex);
+ cond = 0;
+ }
+ struct clear_cv_on_destruct
+ {
+ ~clear_cv_on_destruct()
{
- public:
- restore_cancelation(const restore_cancelation&) = delete;
- restore_cancelation& operator=(const restore_cancelation&) = delete;
- explicit restore_cancelation(cancelable_closure& closure, disable_cancelation& disabler) noexcept;
- ~restore_cancelation() noexcept;
- };
+ this_thread_interrupt_flag.clear_condition_variable();
+ }
+ };
+ void cancelation_point();
+ void cancelable_wait(std::condition_variable& cv, std::unique_lock<std::mutex>& lk)
+ {
+ cancelation_point();
+ this_cancelable_state.set_condition_variable(cv);
+ this_cancelable_state::clear_cv_on_destruct guard;
+ interruption_point();
+ cv.wait_for(lk, std::chrono::milliseconds(1));
+ this_cancelable_state.clear_condition_variable();
+ cancelation_point();
+ }
+ class disable_cancelation
+ {
+ public:
+ disable_cancelation(const disable_cancelation&)= delete;
+ disable_cancelation& operator=(const disable_cancelation&)= delete;
+ disable_cancelation(cancelable_closure& closure)
+ noexcept ;
+ ~disable_cancelation() noexcept;
+ };
+ class restore_cancelation
+ {
+ public:
+ restore_cancelation(const restore_cancelation&) = delete;
+ restore_cancelation& operator=(const restore_cancelation&) = delete;
+ explicit restore_cancelation(cancelable_closure& closure, disable_cancelation& disabler) noexcept;
+ ~restore_cancelation() noexcept;
+ };
};
-
- template <class Closure>
- struct cancelable_closure_mixin : cancelable_closure {
- void operator() {
- cancel_point();
- this->Closure::run();
- }
+
+ template <class Closure>
+ struct cancelable_closure_mixin: cancelable_closure
+ {
+ void operator()
+ {
+ cancel_point();this->Closure::run();
+ }
};
-
- struct my_clousure : cancelable_closure_mixin<my_clousure>
+
+ struct my_clousure: cancelable_closure_mixin<my_clousure>
{
- void run() {
- while () {
- cancel_point();
- }
- }
+ void run()
+ {
+ while ()
+ {
+ cancel_point();
+ }
+ }
}
-
]
[heading Current executor]
@@ -242,33 +319,23 @@ The reason is that the user can always use a thread_local variable and reset it
}
);
+[
[heading Default executor]
The library authors share some of the concerns of the C++ standard committee (introduction of a new single shared resource, a singleton, could make it difficult to make it portable to all the environments) and that this library doesn't need to provide a default executor for the time been.
-The user can always define his default executor himself and use the `at_thread_entry ` member function to set the default constructor.
-
- thread_local default_executor_state_type default_executor_state;
- executor* default_executor() { return default_executor_state.default_executor(); }
-
- // in main
- MyDefaultExecutor myDefaultExecutor(
- // at_thread_entry
- [](MyDefaultExecutor& ex) {
- default_executor_state.set_default_executor(ex);
- }
- );
+The user can always define his default executor himself.
- basic_thread_pool pool(
- // at_thread_entry
- [&myDefaultExecutor](basic_thread_pool& pool) {
- default_executor_state.set_default_executor(myDefaultExecutor);
- }
- );
+ boost::generic_executor_ref default_executor()
+ {
+ static boost::basic_thread_pool tp(4);
+ return generic_executor_ref(tp);
+ }
[endsect]
+
[/////////////////////]
[section:ref Reference]
@@ -344,7 +411,7 @@ If invoked closure throws an exception the executor will call std::terminate, as
[variablelist
-[[Effects:] [close the executor `e` for submissions.]]
+[[Effects:] [close the scheduler/executor `e` for submissions.]]
[[Remark:] [The worker threads will work until there is no more closures to run.]]
@@ -364,7 +431,7 @@ If invoked closure throws an exception the executor will call std::terminate, as
[[Return type:] [`bool`.]]
-[[Return:] [whether the pool is closed for submissions.]]
+[[Return:] [whether the scheduler/executor is closed for submissions.]]
[[Throws:] [Whatever exception that can be throw while ensuring the thread safety.]]
@@ -512,7 +579,7 @@ Polymorphic adaptor of a model of Executor to an executor.
template <typename ...Args>
executor_adaptor(Args&& ... args);
- Executor& underlying_executor();
+ Executor& underlying_executor() noexcept;
void close();
bool closed();
@@ -558,14 +625,12 @@ Polymorphic adaptor of a model of Executor to an executor.
[/////////////////////////////////////]
[section:underlying_executor Function member `underlying_executor()`]
- Executor& underlying_executor();
+ Executor& underlying_executor() noexcept;
[variablelist
[[Return:] [The underlying executor instance. ]]
-[[Throws:] [Nothing.]]
-
]
@@ -587,7 +652,7 @@ Executor abstract base class.
generic_executor_ref& operator=(generic_executor_ref const&);
template <class Executor>
- executor(Executor& ex);
+ generic_executor_ref(Executor& ex);
generic_executor_ref() {};
void close() = 0;
@@ -604,29 +669,552 @@ Executor abstract base class.
[endsect]
+[//////////////////////////////////////////////////////////]
+[section: scheduler Template Class `scheduler `]
+
+Scheduler providing time related functions. Note that `scheduler` is not an Executor.
+
+ #include <boost/thread/executors/scheduler.hpp>
+ namespace boost {
+
+ template <class Clock=steady_clock>
+ class scheduler
+ {
+ public:
+ using work = boost::function<void()> ;
+ using clock = Clock;
+
+ scheduler(scheduler const&) = delete;
+ scheduler& operator=(scheduler const&) = delete;
+
+ scheduler();
+ ~scheduler();
+
+ void close();
+ bool closed();
+
+ template <class Duration, typename Closure>
+ void submit_at(chrono::time_point<clock,Duration> abs_time, Closure&& closure);
+ template <class Rep, class Period, typename Closure>
+ void submit_after(chrono::duration<Rep,Period> rel_time, Closure&& closure);
+
+ template <class Duration>
+ at_executor<scheduler> submit_at(chrono::time_point<clock,Duration> abs_time);
+ template <class Rep, class Period>
+ at_executor<scheduler> submit_after(chrono::duration<Rep,Period> rel_time);
+
+ template <class Executor>
+ scheduler_executor_wrapper<scheduler, Executor> on(Executor& ex);
+
+ };
+ }
+
+[/////////////////////////////////////]
+[section:constructor Constructor `scheduler()`]
+
+ scheduler();
+
+[variablelist
+
+[[Effects:] [Constructs a `scheduler`. ]]
+
+[[Throws:] [Nothing. ]]
+
+]
+
+
+[endsect]
+[/////////////////////////////////////]
+[section:destructor Destructor `~scheduler()`]
+
+ ~scheduler();
+
+[variablelist
+
+[[Effects:] [Destroys the scheduler.]]
+
+[[Synchronization:] [The completion of all the closures happen before the completion of the executor destructor.]]
+
+]
+
+[endsect]
+[/////////////////////////////////////]
+[section:submit_at Template Function Member `submit_at()`]
+
+ template <class Clock, class Duration, typename Closure>
+ void submit_at(chrono::time_point<Clock,Duration> abs_time, Closure&& closure);
+
+[variablelist
+
+[[Effects:] [Schedule a `closure` to be executed at `abs_time`. ]]
+
+[[Throws:] [Nothing.]]
+
+]
+
+
+[endsect]
+[/////////////////////////////////////]
+[section:submit_after Template Function Member `submit_after()`]
+
+ template <class Rep, class Period, typename Closure>
+ void submit_after(chrono::duration<Rep,Period> rel_time, Closure&& closure);
+
+[variablelist
+
+[[Effects:] [Schedule a `closure` to be executed after `rel_time`. ]]
+
+[[Throws:] [Nothing.]]
+
+]
+
+
+[endsect]
+
+[endsect]
+
+[//////////////////////////////////////////////////////////]
+[section:at_executor Template Class `at_executor`]
+
+
+ #include <boost/thread/executors/scheduler.hpp>
+ namespace boost {
+
+ template <class Scheduler>
+ class at_executor
+ {
+ public:
+ using work = Scheduler::work;
+ using clock = Scheduler::clock;
+
+ at_executor(at_executor const&) = default;
+ at_executor(at_executor &&) = default;
+ at_executor& operator=(at_executor const&) = default;
+ at_executor& operator=(at_executor &&) = default;
+
+ at_executor(Scheduler& sch, clock::time_point const& tp);
+ ~at_executor();
+
+ void close();
+ bool closed();
+
+ Scheduler& underlying_scheduler();
+
+ template <class Closure>
+ void submit(Closure&& closure);
+ template <class Duration, typename Work>
+ void submit_at(chrono::time_point<clock,Duration> abs_time, Closure&& closure);
+ template <class Rep, class Period, typename Work>
+ void submit_after(chrono::duration<Rep,Period> rel_time, Closure&& closure);
+
+ template <class Executor>
+ resubmit_at_executor<Scheduler, Executor> on(Executor& ex);
+
+ };
+ }
+
+[/////////////////////////////////////]
+[section:constructor Constructor `at_executor(Scheduler&)`]
+
+ at_executor(Scheduler& sch, clock::time_point const& tp);
+
+[variablelist
+
+[[Effects:] [Constructs a `at_executor`. ]]
+
+[[Throws:] [Nothing. ]]
+
+]
+
+
+[endsect]
+[/////////////////////////////////////]
+[section:destructor Destructor `~at_executor()`]
+
+ ~at_executor();
+
+[variablelist
+
+[[Effects:] [Destroys the `at_executor`.]]
+
+[[Synchronization:] [The completion of all the closures happen before the completion of the executor destructor.]]
+
+]
+
+[endsect]
+[/////////////////////////////////////]
+[section:underlying_scheduler Function member `underlying_scheduler()`]
+
+ Scheduler& underlying_scheduler() noexcept;
+
+[variablelist
+
+[[Return:] [The underlying scheduler instance. ]]
+
+]
+
+[endsect]
+[/////////////////////////////////////]
+[section:submit_at Template Function Member `submit()`]
+
+ template <typename Closure>
+ void submit(Closure&& closure);
+
+[variablelist
+
+[[Effects:] [Schedule the `closure` to be executed at the `abs_time` given at construction time. ]]
+
+[[Throws:] [Nothing.]]
+
+]
+
+[endsect]
+[/////////////////////////////////////]
+[section:submit_at Template Function Member `submit_at()`]
+
+ template <class Clock, class Duration, typename Closure>
+ void submit_at(chrono::time_point<Clock,Duration> abs_time, Closure&& closure);
+
+[variablelist
+
+[[Effects:] [Schedule a `closure` to be executed at `abs_time`. ]]
+
+[[Throws:] [Nothing.]]
+
+]
+
+
+[endsect]
+[/////////////////////////////////////]
+[section:submit_after Template Function Member `submit_after()`]
+
+ template <class Rep, class Period, typename Closure>
+ void submit_after(chrono::duration<Rep,Period> rel_time, Closure&& closure);
+
+[variablelist
+
+[[Effects:] [Schedule a `closure` to be executed after `rel_time`. ]]
+
+[[Throws:] [Nothing.]]
+
+]
+
+
+[endsect]
+
+[endsect]
+
-[/
[//////////////////////////////////////////////////////////]
-[section:scheduled_executor Template Class `scheduled_executor`]
+[section:scheduler_executor_wrapper Template Class `scheduler_executor_wrapper`]
+
+ #include <boost/thread/executors/scheduler.hpp>
+ namespace boost {
+
+ template <class Scheduler, class Executor>
+ class scheduler_executor_wrapper
+ {
+ public:
+ using work = Scheduler::work;
+ using clock = Scheduler::clock;
+
+ scheduler_executor_wrapper(scheduler_executor_wrapper const&) = default;
+ scheduler_executor_wrapper(scheduler_executor_wrapper &&) = default;
+ scheduler_executor_wrapper& operator=(scheduler_executor_wrapper const&) = default;
+ scheduler_executor_wrapper& operator=(scheduler_executor_wrapper &&) = default;
+
+ scheduler_executor_wrapper(Scheduler& sch, Executor& ex);
+
+ ~scheduler_executor_wrapper();
+
+ void close();
+ bool closed();
+
+ Executor& underlying_executor();
+ Scheduler& underlying_scheduler();
+
+ template <class Closure>
+ void submit(Closure&& closure);
+ template <class Duration, typename Work>
+ void submit_at(chrono::time_point<clock,Duration> abs_time, Closure&& closure);
+ template <class Rep, class Period, typename Work>
+ void submit_after(chrono::duration<Rep,Period> rel_time, Closure&& closure);
+
+ template <class Duration>
+ resubmit_at_executor<Scheduler, Executor> at(chrono::time_point<clock,Duration> abs_time);
+ template <class Rep, class Period>
+ resubmit_at_executor<Scheduler, Executor> after(chrono::duration<Rep,Period> rel_time);
+
+ };
+ }
+
+[/////////////////////////////////////]
+[section:constructor Constructor `scheduler_executor_wrapper(Scheduler&, Executor&)`]
+
+ scheduler_executor_wrapper(Scheduler& sch, Executor& ex);
+
+[variablelist
+
+[[Effects:] [Constructs a `scheduler_executor_wrapper`. ]]
+
+[[Throws:] [Nothing. ]]
+
+]
+
+[endsect]
+[/////////////////////////////////////]
+[section:destructor Destructor `~scheduler_executor_wrapper()`]
+
+ ~scheduler_executor_wrapper();
+
+[variablelist
+
+[[Effects:] [Destroys the `scheduler_executor_wrapper`.]]
+
+[[Synchronization:] [The completion of all the closures happen before the completion of the executor destructor.]]
+
+]
+
+[endsect]
+[/////////////////////////////////////]
+[section:underlying_scheduler Function member `underlying_scheduler()`]
+
+ Scheduler& underlying_scheduler() noexcept;
+
+[variablelist
+
+[[Return:] [The underlying scheduler instance. ]]
+
+]
+
+[endsect]
+[/////////////////////////////////////]
+[section:underlying_executor Function member `underlying_executor()`]
+
+ Executor& underlying_executor() noexcept;
+
+[variablelist
+
+[[Return:] [The underlying executor instance. ]]
+
+]
+
+[endsect]
+[/////////////////////////////////////]
+[section:submit_at Template Function Member `submit()`]
+
+ template <typename Closure>
+ void submit(Closure&& closure);
+
+[variablelist
+
+[[Effects:] [Submit the `closure` on the underlying executor. ]]
+
+[[Throws:] [Nothing.]]
+
+]
+
+[endsect]
+[/////////////////////////////////////]
+[section:submit_at Template Function Member `submit_at()`]
+
+ template <class Clock, class Duration, typename Closure>
+ void submit_at(chrono::time_point<Clock,Duration> abs_time, Closure&& closure);
+
+[variablelist
+
+[[Effects:] [Resubmit the `closure` to be executed on the underlying executor at `abs_time`. ]]
+
+[[Throws:] [Nothing.]]
+
+]
+
+[endsect]
+[/////////////////////////////////////]
+[section:submit_after Template Function Member `submit_after()`]
+
+ template <class Rep, class Period, typename Closure>
+ void submit_after(chrono::duration<Rep,Period> rel_time, Closure&& closure);
+
+[variablelist
+
+[[Effects:] [Resubmit the `closure` to be executed on the underlying executor after `rel_time`. ]]
+
+[[Throws:] [Nothing.]]
+
+]
+
+[endsect]
+
+[endsect]
+
+
+[//////////////////////////////////////////////////////////]
+[section:resubmit_at_executor Template Class `resubmit_at_executor`]
+
+`Executor` wrapping an `Scheduler`, an `Executor` and a `time_point` providing an `Executor` interface.
+
+ #include <boost/thread/executors/scheduler.hpp>
+ namespace boost {
+
+ template <class Scheduler, class Executor>
+ class resubmit_at_executor
+ {
+ public:
+ using work = Scheduler::work;
+ using clock = Scheduler::clock;
+
+ resubmit_at_executor(resubmit_at_executor const&) = default;
+ resubmit_at_executor(resubmit_at_executor &&) = default;
+ resubmit_at_executor& operator=(resubmit_at_executor const&) = default;
+ resubmit_at_executor& operator=(resubmit_at_executor &&) = default;
+
+ template <class Duration>
+ resubmit_at_executor(Scheduler& sch, Executor& ex, clock::time_point<Duration> const& tp);
+ ~resubmit_at_executor();
+
+ void close();
+ bool closed();
+
+ Executor& underlying_executor();
+ Scheduler& underlying_scheduler();
+
+ template <class Closure>
+ void submit(Closure&& closure);
+ template <class Duration, typename Work>
+ void submit_at(chrono::time_point<clock,Duration> abs_time, Closure&& closure);
+ template <class Rep, class Period, typename Work>
+ void submit_after(chrono::duration<Rep,Period> rel_time, Closure&& closure);
+
+ };
+ }
+
+
+[/////////////////////////////////////]
+[section:constructor Constructor `resubmit_at_executor(Scheduler&, Executor&, clock::time_point<Duration>)`]
+
+ template <class Duration>
+ resubmit_at_executor(Scheduler& sch, Executor& ex, clock::time_point<Duration> const& tp);
+
+[variablelist
+
+[[Effects:] [Constructs a `resubmit_at_executor`. ]]
+
+[[Throws:] [Nothing. ]]
+
+]
+
+
+[endsect]
+[/////////////////////////////////////]
+[section:destructor Destructor `~resubmit_at_executor()`]
+
+ ~resubmit_at_executor();
+
+[variablelist
+
+[[Effects:] [Destroys the executor_adaptor.]]
+
+[[Synchronization:] [The completion of all the closures happen before the completion of the executor destructor.]]
+
+]
+
+[endsect]
+[/////////////////////////////////////]
+[section:underlying_executor Function member `underlying_executor()`]
+
+ Executor& underlying_executor() noexcept;
+
+[variablelist
+
+[[Return:] [The underlying executor instance. ]]
+
+]
+
+[endsect]
+[/////////////////////////////////////]
+[section:underlying_scheduler Function member `underlying_scheduler()`]
+
+ Scheduler& underlying_scheduler() noexcept;
+
+[variablelist
+
+[[Return:] [The underlying scheduler instance. ]]
+
+]
+
+[endsect]
+[/////////////////////////////////////]
+[section:submit_at Template Function Member `submit()`]
+
+ template <typename Closure>
+ void submit(Closure&& closure);
+
+[variablelist
+
+[[Effects:] [Resubmit the `closure` to be executed on the underlying executor at the `abs_time` given at construction time. ]]
+
+[[Throws:] [Nothing.]]
+
+]
+
+[endsect]
+[/////////////////////////////////////]
+[section:submit_at Template Function Member `submit_at()`]
+
+ template <class Clock, class Duration, typename Closure>
+ void submit_at(chrono::time_point<Clock,Duration> abs_time, Closure&& closure);
+
+[variablelist
+
+[[Effects:] [Resubmit the `closure` to be executed on the underlying executor at `abs_time`. ]]
+
+[[Throws:] [Nothing.]]
+
+]
+
+[endsect]
+[/////////////////////////////////////]
+[section:submit_after Template Function Member `submit_after()`]
+
+ template <class Rep, class Period, typename Closure>
+ void submit_after(chrono::duration<Rep,Period> rel_time, Closure&& closure);
+
+[variablelist
+
+[[Effects:] [Resubmit the `closure` to be executed on the underlying executor after `rel_time`. ]]
+
+[[Throws:] [Nothing.]]
+
+]
+
+[endsect]
+
+[endsect]
+
+
+[//////////////////////////////////////////////////////////]
+[/
+[section:scheduled_executor_ref Template Class `scheduled_executor_ref`]
Executor providing time related functions.
- #include <boost/thread/scheduled_executor.hpp>
+ #include <boost/thread/executors/scheduled_executor_ref.hpp>
namespace boost {
template <class Executor>
- class scheduled_executor
+ class scheduled_executor_ref
{
Executor& ex;
public:
typedef executor::work work;
- scheduled_executor(scheduled_executor const&) = delete;
- scheduled_executor& operator=(scheduled_executor const&) = delete;
+ scheduled_executor_ref(scheduled_executor_ref const&) = delete;
+ scheduled_executor_ref& operator=(scheduled_executor_ref const&) = delete;
template <class Rep, class Period>
- scheduled_executor(Executor& ex, chrono::duration<Rep, Period> granularity=chrono::milliseconds(100));
+ scheduled_executor_ref(Executor& ex, chrono::duration<Rep, Period> granularity=chrono::milliseconds(100));
- Executor& underlying_executor();
+ Executor& underlying_executor() noexcept;
void close();
bool closed();
@@ -640,10 +1228,6 @@ Executor providing time related functions.
template <typename Pred>
bool reschedule_until(Pred const& pred);
- template <class Clock, class Duration>
- void submit_at(chrono::time_point<Clock,Duration> abs_time, work&& closure);
- template <class Rep, class Period>
- void submit_after(chrono::duration<Rep,Period> rel_time, work&& closure);
template <class Clock, class Duration, typename Closure>
void submit_at(chrono::time_point<Clock,Duration> abs_time, Closure&& closure);
template <class Rep, class Period, typename Closure>
@@ -652,14 +1236,14 @@ Executor providing time related functions.
}
[/////////////////////////////////////]
-[section:constructor Constructor `scheduled_executor(Executor&, chrono::duration<Rep, Period>)`]
+[section:constructor Constructor `scheduled_executor_ref(Executor&, chrono::duration<Rep, Period>)`]
template <class Rep, class Period>
- scheduled_executor(Executor& ex, chrono::duration<Rep, Period> granularity=chrono::milliseconds(100));
+ scheduled_executor_ref(Executor& ex, chrono::duration<Rep, Period> granularity=chrono::milliseconds(100));
[variablelist
-[[Effects:] [Constructs a scheduled_executor. ]]
+[[Effects:] [Constructs a scheduled_executor_ref. ]]
[[Throws:] [Nothing. ]]
@@ -668,9 +1252,9 @@ Executor providing time related functions.
[endsect]
[/////////////////////////////////////]
-[section:destructor Destructor `~scheduled_executor()`]
+[section:destructor Destructor `~scheduled_executor_ref()`]
- ~scheduled_executor();
+ ~scheduled_executor_ref();
[variablelist
@@ -684,29 +1268,74 @@ Executor providing time related functions.
[/////////////////////////////////////]
[section:underlying_executor Function member `underlying_executor()`]
- Executor& underlying_executor();
+ Executor& underlying_executor() noexcept;
[variablelist
[[Return:] [The underlying executor instance. ]]
+]
+
+[endsect]
+[/////////////////////////////////////]
+[section:submit_at Template Function Member `submit()`]
+
+ template <typename Closure>
+ void submit(Closure&& closure);
+
+[variablelist
+
+[[Effects:] [Resubmit the `closure` to be executed on the underlying executor. ]]
+
+[[Throws:] [Nothing.]]
+
+]
+
+[endsect]
+[/////////////////////////////////////]
+[section:submit_at Template Function Member `submit_at()`]
+
+ template <class Clock, class Duration, typename Closure>
+ void submit_at(chrono::time_point<Clock,Duration> abs_time, Closure&& closure);
+
+[variablelist
+
+[[Effects:] [Schedule a `closure` to be executed at `abs_time`. ]]
+
[[Throws:] [Nothing.]]
]
[endsect]
+[/////////////////////////////////////]
+[section:submit_after Template Function Member `submit_after()`]
+
+ template <class Rep, class Period, typename Closure>
+ void submit_after(chrono::duration<Rep,Period> rel_time, Closure&& closure);
+
+[variablelist
+
+[[Effects:] [Schedule a `closure` to be executed after `rel_time`. ]]
+
+[[Throws:] [Nothing.]]
+
+]
+
+[endsect]
+
[endsect]
]
[//////////////////////////////////////////////////////////]
-[section:serial_executor Class `serial_executor`]
+[section:serial_executor Template Class `serial_executor`]
A serial executor ensuring that there are no two work units that executes concurrently.
#include <boost/thread/serial_executor.hpp>
namespace boost {
+ template <class Executor>
class serial_executor
{
public:
@@ -716,7 +1345,7 @@ A serial executor ensuring that there are no two work units that executes concur
template <class Executor>
serial_executor(Executor& ex);
- generic_executor_ref underlying_executor();
+ Executor& underlying_executor() noexcept;
void close();
bool closed();
@@ -764,7 +1393,7 @@ A serial executor ensuring that there are no two work units that executes concur
[/////////////////////////////////////]
[section:underlying_executor Function member `underlying_executor()`]
- generic_executor_ref underlying_executor();
+ generic_executor_ref& underlying_executor() noexcept;
[variablelist
@@ -780,6 +1409,84 @@ A serial executor ensuring that there are no two work units that executes concur
[endsect]
[//////////////////////////////////////////////////////////]
+[section:generic_serial_executor Class `generic_serial_executor`]
+
+A serial executor ensuring that there are no two work units that executes concurrently.
+
+ #include <boost/thread/generic_serial_executor.hpp>
+ namespace boost {
+ class generic_serial_executor
+ {
+ public:
+ generic_serial_executor(generic_serial_executor const&) = delete;
+ generic_serial_executor& operator=(generic_serial_executor const&) = delete;
+
+ template <class Executor>
+ generic_serial_executor(Executor& ex);
+
+ generic_executor_ref& underlying_executor() noexcept;
+
+ void close();
+ bool closed();
+
+ template <typename Closure>
+ void submit(Closure&& closure);
+
+ bool try_executing_one();
+ template <typename Pred>
+ bool reschedule_until(Pred const& pred);
+
+ };
+ }
+
+[/////////////////////////////////////]
+[section:constructor Constructor `generic_serial_executor(Executor&)`]
+
+ template <class Executor>
+ generic_serial_executor(Executor& ex);
+
+[variablelist
+
+[[Effects:] [Constructs a serial_executor. ]]
+
+[[Throws:] [Nothing. ]]
+
+]
+
+
+[endsect]
+[/////////////////////////////////////]
+[section:destructor Destructor `~serial_executor()`]
+
+ ~generic_serial_executor();
+
+[variablelist
+
+[[Effects:] [Destroys the serial_executor.]]
+
+[[Synchronization:] [The completion of all the closures happen before the completion of the executor destructor.]]
+
+]
+
+[endsect]
+[/////////////////////////////////////]
+[section:underlying_executor Function member `underlying_executor()`]
+
+ Executor& underlying_executor() noexcept;
+
+[variablelist
+
+[[Return:] [The underlying executor instance. ]]
+
+]
+
+
+[endsect]
+
+[endsect]
+
+
+[//////////////////////////////////////////////////////////]
[section:inline_executor Class `inline_executor`]
A serial executor ensuring that there are no two work units that executes concurrently.
@@ -848,7 +1555,7 @@ A serial executor ensuring that there are no two work units that executes concur
A thread pool with up to a fixed number of threads.
- #include <boost/thread/work.hpp>
+ #include <boost/thread/executors/basic_thread_pool.hpp>
namespace boost {
class basic_thread_pool
{
@@ -892,7 +1599,7 @@ A thread pool with up to a fixed number of threads.
[/////////////////////////////////////]
[section:destructor Destructor `~basic_thread_pool()`]
- virtual ~basic_thread_pool();
+ ~basic_thread_pool();
[variablelist
@@ -905,6 +1612,64 @@ A thread pool with up to a fixed number of threads.
[endsect]
+[///////////////////////////////////////]
+[section:thread_executor Class `thread_executor`]
+
+A thread_executor with a threads for each task.
+
+ #include <boost/thread/executors/thread_executor.hpp>
+ namespace boost {
+ class thread_executor
+ {
+ public:
+
+ thread_executor(thread_executor const&) = delete;
+ thread_executor& operator=(thread_executor const&) = delete;
+
+ thread_executor();
+ template <class AtThreadEntry>
+ basic_thread_pool( unsigned const thread_count, AtThreadEntry at_thread_entry);
+ ~thread_executor();
+
+ void close();
+ bool closed();
+
+ template <typename Closure>
+ void submit(Closure&& closure);
+
+ };
+ }
+
+[/////////////////////////////////////]
+[section:constructor Constructor `thread_executor()`]
+
+[variablelist
+
+[[Effects:] [creates a thread_executor. ]]
+
+[[Throws:] [Whatever exception is thrown while initializing the needed resources. ]]
+
+]
+
+
+[endsect]
+[/////////////////////////////////////]
+[section:destructor Destructor `~thread_executor()`]
+
+ ~thread_executor();
+
+[variablelist
+
+[[Effects:] [Waits for closures (if any) to complete, then joins and destroys the threads.]]
+
+[[Synchronization:] [The completion of all the closures happen before the completion of the executor destructor.]]
+
+]
+[endsect]
+
+[endsect]
+
+
[/////////////////////////////////]
[section:loop_executor Class `loop_executor`]
diff --git a/libs/thread/doc/changes.qbk b/libs/thread/doc/changes.qbk
index 970f37f46..e158388b7 100644
--- a/libs/thread/doc/changes.qbk
+++ b/libs/thread/doc/changes.qbk
@@ -8,6 +8,61 @@
[section:changes History]
+[heading Version 4.5.0 - boost 1.58]
+
+[*Know Bugs:]
+
+* [@http://svn.boost.org/trac/boost/ticket/3926 #3926] thread_specific_ptr + dlopen library causes a SIGSEGV.
+* [@http://svn.boost.org/trac/boost/ticket/4833 #4833] MinGW/test_tss_lib: Support of automatic tss cleanup for native threading API not available
+* [@http://svn.boost.org/trac/boost/ticket/8600 #8600] wait_for_any hangs, if called with multiple copies of shared_future referencing same task
+* [@http://svn.boost.org/trac/boost/ticket/9118 #9118] Seg fault on thread join when llvm and libc++ are used
+* [@http://svn.boost.org/trac/boost/ticket/9309 #9309] test_latch fails often on clang-darwin-tot11
+* [@http://svn.boost.org/trac/boost/ticket/9311 #9311] ex_lambda_future fails on msvc-11.0
+* [@http://svn.boost.org/trac/boost/ticket/10942 #10942] Boost.Thread fails to build on Cygwin.
+
+[/
+* [@http://svn.boost.org/trac/boost/ticket/7319 #7319] Take care of c++std-lib-32966 issue
+* [@http://svn.boost.org/trac/boost/ticket/10651 #10651] boost::thread leaks memory when using the MinGW compiler.
+* [@http://svn.boost.org/trac/boost/ticket/10685 #10685] mfc_thread_init.hpp does not compile.
+* [@http://svn.boost.org/trac/boost/ticket/10967 #10967] Timed wait points inconsistently convert relative to absolute waits.
+]
+
+Please take a look at [@https://svn.boost.org/trac/boost/query?status=assigned&status=new&status=reopened&component=thread&type=!Feature+Requests&col=id&col=summary&order=id thread Know Bugs] to see the current state.
+
+Please take a look at [@http://www.boost.org/development/tests/master/developer/thread.html thread trunk regression test] to see the last regression test snapshot.
+
+[*Sever limitations:]
+
+There are some severe bugs that prevent the use of the library on concrete contexts, in particular:
+
+* on thread specific storage that prevent the library to be used with dynamic libraries ( [@http://svn.boost.org/trac/boost/ticket/3926 #3926], ),
+
+[*New Experimental Features:]
+
+* [@http://svn.boost.org/trac/boost/ticket/9600 #9600] Async: Add task_region
+* [@http://svn.boost.org/trac/boost/ticket/10611 #10611] Add emplace promise::set_value and emplace make_ready_future
+* [@http://svn.boost.org/trac/boost/ticket/10826 #10826] Add scheduled executor operations
+* [@http://svn.boost.org/trac/boost/ticket/11048 #11048] Add a serial_executor based on continuations
+
+[*Fixed Bugs:]
+
+* [@http://svn.boost.org/trac/boost/ticket/6787 #6787] boost::thread::sleep() hangs if system time is rolled back
+* [@http://svn.boost.org/trac/boost/ticket/10734 #10734] Submit method work differently on different executors, some throw exception and some silently ignore error (thread_executor and inline_executor)
+* [@http://svn.boost.org/trac/boost/ticket/10736 #10736] Task exceptions silently ignored. I think std::terminate solution from N3785 and std::thread is better choice and more consistent.
+* [@http://svn.boost.org/trac/boost/ticket/10737 #10737] In serial_executor we have infinite wait if task throw exception.
+* [@http://svn.boost.org/trac/boost/ticket/10822 #10822] Boost.Thread fails to compile on Android
+* [@http://svn.boost.org/trac/boost/ticket/10824 #10824] Boost.Thread 1.57 breaks Windows XP compatibility for SP2 and below.
+* [@http://svn.boost.org/trac/boost/ticket/10963 #10963] future<future<T>>::then Has No Implementation
+* [@http://svn.boost.org/trac/boost/ticket/10964 #10964] future<future<T>>::unwrap().then() Deadlocks
+* [@http://svn.boost.org/trac/boost/ticket/10968 #10968] The futures returned by async() and future::then() are not blocking.
+* [@http://svn.boost.org/trac/boost/ticket/10971 #10971] shared_future::get()/get_or() must be const
+* [@http://svn.boost.org/trac/boost/ticket/10972 #10972] shared_future::then() can be called multiple times.
+* [@http://svn.boost.org/trac/boost/ticket/10979 #10979] Support T& type deduction when the make_ready_future parameter is reference_wrapper<T>
+* [@http://svn.boost.org/trac/boost/ticket/10996 #10996] Thread physical_concurrency() is failing on Windows
+* [@http://svn.boost.org/trac/boost/ticket/11035 #11035] BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE not defined for Android
+* [@http://svn.boost.org/trac/boost/ticket/11053 #11053] The attached code results in a R6025 - pure virtual function call in run_thread_exit_callbacks
+
+
[heading Version 4.4.0 - boost 1.57]
[*Know Bugs:]
@@ -24,6 +79,7 @@
* [@http://svn.boost.org/trac/boost/ticket/10537 #10537] Application crash on throw exception
* [@http://svn.boost.org/trac/boost/ticket/10651 #10651] boost::thread leaks memory when using the MinGW compiler
+
Please take a look at [@https://svn.boost.org/trac/boost/query?status=assigned&status=new&status=reopened&component=thread&type=!Feature+Requests&col=id&col=summary&order=id thread Know Bugs] to see the current state.
Please take a look at [@http://www.boost.org/development/tests/master/developer/thread.html thread trunk regression test] to see the last snapshot.
@@ -557,9 +613,6 @@ been moved to __thread_id__.
The following features will be included in next releases.
-# Complete the C++11 missing features, in particular
- * [@http://svn.boost.org/trac/boost/ticket/6227 #6227] C++11 compliance: Use of variadic templates on Generic Locking Algorithms on compilers providing them.
-
# Add some minor features, in particular
* [@http://svn.boost.org/trac/boost/ticket/7589 #7589] Synchro: Add polymorphic lockables.
@@ -567,9 +620,6 @@ The following features will be included in next releases.
* [@http://svn.boost.org/trac/boost/ticket/8273 #8273] Synchro: Add externally locked streams.
* [@http://svn.boost.org/trac/boost/ticket/8514 #8514] Async: Add a thread_pool executor with work stealing.
-# Add extensions related to fork-join as:
- * [@http://svn.boost.org/trac/boost/ticket/9600 #9600] Async: Add task_region/task_run.
-
# And some additional extensions related to futures as:
* [@http://svn.boost.org/trac/boost/ticket/8517 #8517] Async: Add a variadic shared_future::then.
diff --git a/libs/thread/doc/external_locking.qbk b/libs/thread/doc/external_locking.qbk
index a485f3633..9c7b1ee46 100644
--- a/libs/thread/doc/external_locking.qbk
+++ b/libs/thread/doc/external_locking.qbk
@@ -98,7 +98,7 @@ or inheriting from a class which add these lockable functions.
The `basic_lockable_adapter` class helps to define the `BankAccount` class as
class BankAccount
- : public basic_lockable_adapter<thread_mutex>
+ : public basic_lockable_adapter<boost::mutex>
{
int balance_;
public:
@@ -138,7 +138,7 @@ Notice that now acct is being locked by Withdraw after it has already been locke
As `boost::mutex` is not recursive, we need to use its recursive version `boost::recursive_mutex`.
class BankAccount
- : public basic_lockable_adapter<recursive_mutex>
+ : public basic_lockable_adapter<boost::recursive_mutex>
{
// ...
@@ -147,7 +147,7 @@ As `boost::mutex` is not recursive, we need to use its recursive version `boost:
The caller-ensured locking approach is more flexible and the most efficient, but very dangerous. In an implementation using caller-ensured locking, BankAccount still holds a mutex, but its member functions don't manipulate it at all. Deposit and Withdraw are not thread-safe anymore. Instead, the client code is responsible for locking BankAccount properly.
class BankAccount
- : public basic_lockable_adapter<boost:mutex> {
+ : public basic_lockable_adapter<boost::mutex> {
int balance_;
public:
void Deposit(int amount) {
@@ -271,7 +271,7 @@ Now that we have such a strict `strict_lock`, how do we harness its power in def
A little code is worth 1,000 words, a (hacked into) saying goes, so here's the new BankAccount class:
class BankAccount
- : public basic_lockable_adapter<boost:recursive_mutex>
+ : public basic_lockable_adapter<boost::mutex>
{
int balance_;
public:
@@ -280,7 +280,7 @@ A little code is worth 1,000 words, a (hacked into) saying goes, so here's the n
balance_ += amount;
}
void Deposit(int amount) {
- strict_lock<boost:mutex> guard(*this); // Internally locked
+ strict_lock<BankAccount> guard(*this); // Internally locked
Deposit(amount, guard);
}
void Withdraw(int amount, strict_lock<BankAccount>&) {
@@ -288,7 +288,7 @@ A little code is worth 1,000 words, a (hacked into) saying goes, so here's the n
balance_ -= amount;
}
void Withdraw(int amount) {
- strict_lock<boost:mutex> guard(*this); // Internally locked
+ strict_lock<BankAccount> guard(*this); // Internally locked
Withdraw(amount, guard);
}
};
@@ -327,7 +327,7 @@ The scheme is useful because the likelihood of a programmer forgetting about any
Using `strict_lock` permits compile-time checking of the most common source of errors, and runtime checking of the less frequent problem.
Let's see how to enforce that the appropriate BankAccount object is locked. First, we need to add a member function to the `strict_lock` class template.
-The `bool strict_lock<T>::owns_lock(Loclable*)` function returns a reference to the locked object.
+The `bool strict_lock<T>::owns_lock(Lockable*)` function returns a reference to the locked object.
template <class Lockable> class strict_lock {
... as before ...
@@ -338,7 +338,7 @@ The `bool strict_lock<T>::owns_lock(Loclable*)` function returns a reference to
Second, BankAccount needs to use this function compare the locked object against this:
class BankAccount {
- : public basic_lockable_adapter<boost::recursive_mutex>
+ : public basic_lockable_adapter<boost::mutex>
int balance_;
public:
void Deposit(int amount, strict_lock<BankAccount>& guard) {
@@ -403,7 +403,7 @@ The solution is to use a little bridge template `externally_locked` that control
T& get(strict_lock<Lockable>& lock) {
#ifdef BOOST_THREAD_THROW_IF_PRECONDITION_NOT_SATISFIED
- if (!lock.owns_lock(&lockable_)) throw lock_error(); run time check throw if not locks the same
+ if (!lock.owns_lock(&lockable_)) throw lock_error(); //run time check throw if not locks the same
#endif
return obj_;
}
@@ -421,10 +421,10 @@ The solution is to use a little bridge template `externally_locked` that control
Instead of making `checkingAcct_` and `savingsAcct_` of type `BankAccount`, `AccountManager` holds objects of type `externally_locked<BankAccount, AccountManager>`:
class AccountManager
- : public basic_lockable_adapter<thread_mutex>
+ : public basic_lockable_adapter<boost::mutex>
{
public:
- typedef basic_lockable_adapter<thread_mutex> lockable_base_type;
+ typedef basic_lockable_adapter<boost::mutex> lockable_base_type;
AccountManager()
: checkingAcct_(*this)
, savingsAcct_(*this)
@@ -498,7 +498,7 @@ Now we need to state that both classes are `strict_lock`s.
Well let me show what this `nested_strict_lock` class looks like and the impacts on the `externally_locked` class and the `AccountManager::AMoreComplicatedFunction` function.
-First `nested_strict_lock` class will store on a temporary lock the `Locker`, and transfer the lock ownership on the constructor. On destruction it will restore the ownership. Note the use of `lock_traits` and that the `Locker` needs to have a reference to the mutex otherwise and exception is thrown.
+First `nested_strict_lock` class will store on a temporary lock the `Locker`, and transfer the lock ownership on the constructor. On destruction it will restore the ownership. Note the use of `lock_traits` and that the `Locker` needs to have a reference to the mutex otherwise an exception is thrown.
template <typename Locker >
class nested_strict_lock
@@ -510,7 +510,7 @@ First `nested_strict_lock` class will store on a temporary lock the `Locker`, an
nested_strict_lock(Locker& lock)
: lock_(lock) // Store reference to locker
- , tmp_lock_(lock.move()) // Move ownership to temporaty locker
+ , tmp_lock_(lock.move()) // Move ownership to temporary locker
{
#ifdef BOOST_THREAD_THROW_IF_PRECONDITION_NOT_SATISFIED
if (tmp_lock_.mutex()==0) {
diff --git a/libs/thread/doc/future_ref.qbk b/libs/thread/doc/future_ref.qbk
index 9fb73c173..d63e1aee3 100644
--- a/libs/thread/doc/future_ref.qbk
+++ b/libs/thread/doc/future_ref.qbk
@@ -1020,7 +1020,7 @@ There are not too much tests yet, so it is possible that you can find out some t
void swap(shared_future& other);
// retrieving the value
- see below get();
+ see below get() const;
exception_ptr get_exception_ptr(); // EXTENSION
@@ -1066,9 +1066,9 @@ There are not too much tests yet, so it is possible that you can find out some t
[///////////////////////////////////]
[section:get Member function `get()`]
- const R& get();
- R& get();
- void get();
+ const R& get() const;
+ R& get() const;
+ void get() const;
[variablelist
@@ -2362,7 +2362,7 @@ All `R` types must be the same. If any of the `future<R>` or `shared_future<R>`
[section:make_ready_future Non-member function `make_ready_future()` EXTENSION]
template <typename T>
- future<typename decay<T>::type> make_ready_future(T&& value); // EXTENSION
+ future<V> make_ready_future(T&& value); // EXTENSION
future<void> make_ready_future(); // EXTENSION
template <typename T>
future<T> make_ready_future(exception_ptr ex); // DEPRECATED
@@ -2371,11 +2371,15 @@ All `R` types must be the same. If any of the `future<R>` or `shared_future<R>`
[variablelist
+[[Remark:][
+where `V` is determined as follows: Let `U` be `decay_t<T>`. Then `V` is `X&`
+if `U` equals `reference_wrapper<X>`, otherwise `V` is `U`.
+]]
[[Effects:] [
-- value prototype: The value that is passed into the function is moved to the shared state of the returned function if it is an rvalue.
-Otherwise the value is copied to the shared state of the returned function.
+- value prototype: The value that is passed into the function is moved to the shared state of the returned future if it is an rvalue.
+Otherwise the value is copied to the shared state of the returned future.
-- exception: The exception that is passed into the function is copied to the shared state of the returned function.
+- exception: The exception that is passed into the function is copied to the shared state of the returned future.
.]]
diff --git a/libs/thread/doc/futures.qbk b/libs/thread/doc/futures.qbk
index 5a5bdfa2e..f05bde555 100755
--- a/libs/thread/doc/futures.qbk
+++ b/libs/thread/doc/futures.qbk
@@ -93,7 +93,7 @@ the result is ready, it is returned from __unique_future_get__ by rvalue-referen
appropriate for the type.
On the other hand, many instances of __shared_future__ may reference the same result. Instances can be freely copied and assigned,
-and __shared_future_get__ returns a non `const` reference so that multiple calls to __shared_future_get__ are safe. You can move an
+and __shared_future_get__ returns a `const` reference so that multiple calls to __shared_future_get__ are safe. You can move an
instance of __unique_future__ into an instance of __shared_future__, thus transferring ownership of the associated asynchronous
result, but not vice-versa.
diff --git a/libs/thread/doc/mutex_concepts.qbk b/libs/thread/doc/mutex_concepts.qbk
index 78ec58849..825d22c23 100644
--- a/libs/thread/doc/mutex_concepts.qbk
+++ b/libs/thread/doc/mutex_concepts.qbk
@@ -2664,7 +2664,7 @@ Only the specificities respect to __Lockable are described here.
[endsect]
[///////////////////////////////]
-[section:get2 `get(strict_lock<nested_strict_lock<Lock>>&)`]
+[section:get2 `get(nested_strict_lock<Lock>&)`]
template <class Lock>
T& get(nested_strict_lock<Lock>& lk);
@@ -2684,7 +2684,7 @@ Only the specificities respect to __Lockable are described here.
[endsect]
[///////////////////////////////]
-[section:get3 `get(strict_lock<nested_strict_lock<Lock>>&)`]
+[section:get3 `get(Lock&)`]
template <class Lock>
T& get(Lock& lk);
@@ -2826,7 +2826,7 @@ Only the specificities respect to __Lockable are described here.
[endsect]
[///////////////////////////////]
-[section:get2 `get(strict_lock<nested_strict_lock<Lock>>&)`]
+[section:get2 `get(nested_strict_lock<Lock>&)`]
template <class Lock>
T& get(nested_strict_lock<Lock>& lk);
@@ -2846,7 +2846,7 @@ Only the specificities respect to __Lockable are described here.
[endsect]
[///////////////////////////////]
-[section:get3 `get(strict_lock<nested_strict_lock<Lock>>&)`]
+[section:get3 `get(Lock&)`]
template <class Lock>
T& get(Lock& lk);
diff --git a/libs/thread/doc/parallel.qbk b/libs/thread/doc/parallel.qbk
new file mode 100644
index 000000000..7acee567a
--- /dev/null
+++ b/libs/thread/doc/parallel.qbk
@@ -0,0 +1,482 @@
+[/
+ / Copyright (c) 2014 Vicente J. Botet Escriba
+ /
+ / 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:parallel Parallel - Fork-Join -- EXPERIMENTAL]
+
+[section:fork_join Fork-Join]
+
+[warning These features are experimental and subject to change in future versions. There are not too much tests yet, so it is possible that you can find out some trivial bugs :(]
+
+[note These features are based on the [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4088.pdf [* n4088 - Task Region R3]] C++1y proposal from P. Halpern, A. Robison, A. Laksberg, H. Sutter, et al. The text that follows has been adapted from this paper to show the differences.]
+
+The major difference respect to the standard proposal is that we are able to use a common executor for several task regions.
+
+[note
+Up to now, Boost.Thread doesn't implement the parallel algorithms as defined in [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4105.pdf [* n4105 - Information technology – Programming languages, their environments and system software interfaces – Technical Specification for C++ Extensions for Parallelism]].
+]
+
+[////////////////////]
+[section Introduction]
+
+
+This module introduces a C++11/c++14 library function template `task_region` and a library class `task_region_handle`
+with member functions `run` and `wait` that together enable developers to write expressive and portable fork-join
+parallel code.
+
+The working draft for the Parallelism TS [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4105.pdf [*N4105]] augments the STL algorithms with the inclusion of parallel execution policies. Programmers use these as a basis to write additional high-level algorithms that can be implemented in terms of the provided parallel algorithms. However, the scope of n4105 does not include lower-level mechanisms to express arbitrary fork-join parallelism
+
+The `task_region`, `run` and the `wait` functions provided by this library are based on the `task_group` concept that is a part of the common subset of the PPL and the TBB libraries.
+
+[endsect] [/ Introduction]
+
+
+[/////////////////////////]
+[section:tutorial Tutorial]
+
+Consider an example of a parallel traversal of a tree, where a user-provided function compute is applied to each node of the tree, returning the sum of the results:
+
+ template<typename Func>
+ int traverse(node *n, Func&& compute)
+ {
+ int left = 0, right = 0;
+ task_region([&](task_region_handle& tr) {
+ if (n->left)
+ tr.run([&] { left = traverse(n->left, compute); });
+ if (n->right)
+ tr.run([&] { right = traverse(n->right, compute); });
+ });
+ return compute(n) + left + right;
+ }
+
+The example above demonstrates the use of two of the functions proposed in this paper, `task_region` and
+`task_region_handle::run`.
+The `task_region` function delineates a region in a program code potentially containing invocations of tasks
+spawned by the `run` member function of the `task_region_handle` class.
+
+The run function spawns a task, a unit of work that is allowed to execute in parallel with respect to the caller.
+Any parallel tasks spawned by `run` within the `task_region` are joined back to a single thread of execution at
+the end of the `task_region`.
+
+`run` takes a user-provided function object `f` and starts it asynchronously - i.e. it may return before the
+execution of `f` completes. The implementation's scheduler may choose to run `f` immediately or delay running
+`f` until compute resources become available.
+
+A `task_region_handle` can be constructed only by `task_region` because it has no public constructors.
+Thus, `run` can be invoked (directly or indirectly) only from a user-provided function passed to `task_region`:
+
+ void g();
+ void f(task_region_handle& tr)
+ {
+ tr.run(g); // OK, invoked from within task_region in h
+ }
+ void h()
+ {
+ task_region(f);
+ }
+
+ int main()
+ {
+ task_region_handle tr; // Error: no public constructor
+ tr.run(g); // No way to call run outside of a task_region
+ return 0;
+ }
+
+[endsect] [/ Tutorial]
+
+[////////////////]
+[section:examples Examples]
+
+[section:fib Parallel Fibonacci]
+
+
+This is surely the worst implementation of the Fibonacci function. Anyway, here it is, as it is simple and shows the fork-join structure clearly. `Fibonacci(n) = Fibonacci(n-1) + Fibonacci(n-2)`, so the task decomposition is trivial.
+
+ int fib_task_region(int n)
+ {
+ using boost::experimental::parallel::task_region;
+ using boost::experimental::parallel::task_region_handle;
+
+ if (n == 0) return 0;
+ if (n == 1) return 1;
+
+ int n1;
+ int n2;
+
+ task_region([&](task_region_handle& trh)
+ {
+ trh.run([&]
+ {
+ n1 = fib_task_region(n - 1);
+ });
+
+ n2 = fib_task_region(n - 2);
+ });
+
+ return n1 + n2;
+ }
+
+ int main()
+ {
+ for (int i = 0; i<10; ++i) {
+ std::cout << fib_task_region(i) << " ";
+ }
+ std::cout << std::endl;
+ }
+
+[endsect] [/ Fib]
+[section:fibex Parallel Fibonacci - Specific executor]
+
+The previous example make use of an implementation defined way to spawn the tasks. Often the user wants to master how the task must be spawned. There is an overload of `task_region` that accept an additional `Executor` parameter and a function that takes as parameter a `task_region_handle_gen<Executor>`. `task_region_handle_gen<Executor>` run uses this executor to spawn the tasks.
+
+ template <class Ex>
+ int fib_task_region_gen( Ex& ex, int n)
+ {
+ using boost::experimental::parallel::task_region;
+ using boost::experimental::parallel::task_region_handle_gen;
+
+ if (n == 0) return 0;
+ if (n == 1) return 1;
+
+ int n1;
+ int n2;
+
+ task_region(ex, [&](task_region_handle_gen<Ex>& trh) // (2)
+ {
+ trh.run([&]
+ {
+ n1 = fib_task_region(n - 1);
+ });
+
+ n2 = fib_task_region(n - 2);
+ });
+
+ return n1 + n2;
+ }
+
+ int main()
+ {
+ boost::basic_thread_pool tp; // (1)
+ for (int i = 0; i<10; ++i) {
+ std::cout << fib_task_region_gen(tp,i) << " ";
+ }
+ std::cout << std::endl;
+ return 0;
+ }
+
+
+The specific executor is declared in line (1) and it is used in line (2).
+
+[endsect] [/ Fib ex]
+
+
+
+
+[section:quick_sort Parallel Accumulate]
+
+
+[endsect] [/ Accumulate]
+
+[section:quick_sort Parallel Quick Sort]
+
+
+
+[endsect] [/ QuickSort]
+[endsect] [/ Examples]
+
+
+[////////////////////////]
+[section:rationale Design Rationale]
+
+
+[endsect] [/ Design Rationale]
+[endsect] [/ Fork-Join]
+
+[/////////////////////]
+[section:ref Reference -- EXPERIMENTAL]
+
+[/////////////////////////]
+[section:v1 Parallel V1]
+
+[section:exception_list Header `<experimental/exception_list.hpp>`]
+
+ namespace boost
+ {
+ namespace experimental
+ {
+ namespace parallel
+ {
+ inline namespace v1
+ {
+
+ class exception_list;
+
+ } // v1
+ } // parallel
+ } // experimental
+ } // boost
+
+
+[/////////////////////////]
+[section:exception_list Class `exception_list`]
+
+ namespace boost
+ {
+ namespace experimental
+ {
+ namespace parallel
+ {
+ inline namespace v1
+ {
+
+ class exception_list: public std::exception
+ {
+ public:
+ typedef 'implementation defined' const_iterator;
+
+ ~exception_list() noexcept {}
+
+ void add(exception_ptr const& e);
+ size_t size() const noexcept;
+ const_iterator begin() const noexcept;
+ const_iterator end() const noexcept;
+ const char* what() const noexcept;
+
+ };
+
+ } // v1
+ } // parallel
+ } // experimental
+ } // boost
+
+
+[endsect] [/ exception_list]
+
+[endsect] [/ exception_list.hpp]
+
+[endsect] [/ Parallel V1]
+
+[////////////////////////////////////////////////////////////////////]
+[section:v2 Parallel V2]
+[////////////////////////////////////////////////////////////////////]
+[section:concepts Concepts]
+[////////////////////////////////////////////////////////////////////]
+[section:regionCallable Concept `Region_Callable`]
+
+[endsect] [/ Region_Callable]
+[////////////////////////////////////////////////////////////////////]
+[section:taskCallable Concept `Task_Callable`]
+
+
+[endsect] [/ Task_Callable]
+[////////////////////////////////////////////////////////////////////]
+[endsect] [/ Concepts]
+[////////////////////////////////////////////////////////////////////]
+[section:task_region Header `<experimental/task_region.hpp>`]
+
+ namespace boost
+ {
+ namespace experimental
+ {
+ namespace parallel
+ {
+ inline namespace v2
+ {
+
+ class task_canceled_exception;
+
+ template <class Executor>
+ class task_region_handle_gen;
+
+ using default_executor = 'implementation defined';
+
+ class task_region_handle;
+
+ template <typename Executor, typename F>
+ void task_region_final(Executor& ex, F&& f);
+ template <typename F>
+ void task_region_final(F&& f);
+
+ template <typename Executor, typename F>
+ void task_region(Executor& ex, F&& f);
+ template <typename F>
+ void task_region(F&& f);
+
+ } // v2
+ } // parallel
+ } // experimental
+ } // boost
+
+
+[////////////////////////////////////////////////////////////////////]
+[section:task_canceled_exception Class `task_canceled_exception `]
+
+ namespace boost
+ {
+ namespace experimental
+ {
+ namespace parallel
+ {
+ inline namespace v2
+ {
+
+ class task_canceled_exception: public std::exception
+ {
+ public:
+ task_canceled_exception() noexcept;
+ task_canceled_exception(const task_canceled_exception&) noexcept;
+ task_canceled_exception& operator=(const task_canceled_exception&) noexcept;
+ virtual const char* what() const noexcept;
+ };
+
+ } // v2
+ } // parallel
+ } // experimental
+ } // boost
+
+[endsect] [/ task_canceled_exception]
+[////////////////////////////////////////////////////////////////////]
+[section:task_region_handle_gen Template Class `task_region_handle_gen<>`]
+
+ namespace boost
+ {
+ namespace experimental
+ {
+ namespace parallel
+ {
+ inline namespace v2
+ {
+
+ template <class Executor>
+ class task_region_handle_gen
+ {
+ protected:
+ task_region_handle_gen(Executor& ex);
+
+ ~task_region_handle_gen();
+
+ public:
+ task_region_handle_gen(const task_region_handle_gen&) = delete;
+ task_region_handle_gen& operator=(const task_region_handle_gen&) = delete;
+ task_region_handle_gen* operator&() const = delete;
+
+ template<typename F>
+ void run(F&& f);
+
+ void wait();
+ };
+
+ } // v2
+ } // parallel
+ } // experimental
+ } // boost
+
+[endsect] [/ task_region_handle_gen]
+[////////////////////////////////////////////////////////////////////]
+[section:default_executor Class `default_executor `]
+
+ namespace boost
+ {
+ namespace experimental
+ {
+ namespace parallel
+ {
+ inline namespace v2
+ {
+
+ using default_executor = 'implementation defined';
+
+ } // v2
+ } // parallel
+ } // experimental
+ } // boost
+
+[endsect] [/ default_executor]
+[////////////////////////////////////////////////////////////////////]
+[section:task_region_handle Class `task_region_handle `]
+
+ namespace boost
+ {
+ namespace experimental
+ {
+ namespace parallel
+ {
+ inline namespace v2
+ {
+
+ class task_region_handle :
+ public task_region_handle_gen<default_executor>
+ {
+ protected:
+ task_region_handle();
+ task_region_handle(const task_region_handle&) = delete;
+ task_region_handle& operator=(const task_region_handle&) = delete;
+ task_region_handle* operator&() const = delete;
+
+ };
+
+ } // v2
+ } // parallel
+ } // experimental
+ } // boost
+
+[endsect] [/ task_region_handle]
+[////////////////////////////////////////////////////////////////////]
+[section:task_region_final Template Function `task_region_final `]
+
+ namespace boost
+ {
+ namespace experimental
+ {
+ namespace parallel
+ {
+ inline namespace v2
+ {
+
+ template <typename Executor, typename F>
+ void task_region_final(Executor& ex, F&& f);
+ template <typename F>
+ void task_region_final(F&& f);
+
+ } // v2
+ } // parallel
+ } // experimental
+ } // boost
+
+[endsect] [/ task_region_final]
+[////////////////////////////////////////////////////////////////////]
+[section:task_region Template Function `task_region `]
+
+ namespace boost
+ {
+ namespace experimental
+ {
+ namespace parallel
+ {
+ inline namespace v2
+ {
+
+ template <typename Executor, typename F>
+ void task_region(Executor& ex, F&& f);
+ template <typename F>
+ void task_region(F&& f);
+
+ } // v2
+ } // parallel
+ } // experimental
+ } // boost
+
+[endsect] [/ task_region]
+
+
+
+
+[endsect] [/ task_region.hpp]
+[endsect] [/ Parallel V2]
+[endsect] [/ Reference]
+
+[endsect] [/ Parallel]
diff --git a/libs/thread/doc/scoped_thread.qbk b/libs/thread/doc/scoped_thread.qbk
index f98f52500..f92b07676 100644
--- a/libs/thread/doc/scoped_thread.qbk
+++ b/libs/thread/doc/scoped_thread.qbk
@@ -21,18 +21,18 @@
class scoped_thread;
void swap(scoped_thread& lhs,scoped_thread& rhs) noexcept;
-[section:motovation Motivation]
+[section:motivation Motivation]
Based on the scoped_thread class defined in C++ Concurrency in Action Boost.Thread defines a thread wrapper class that instead of calling terminate if the thread is joinable on destruction, call a specific action given as template parameter.
-While the scoped_thread class defined in C++ Concurrency in Action is closer to strict_scoped_thread class that doesn't allows any change in the wrapped thread, Boost.Thread provides a class scoped_thread that provides the same non-deprecated interface than __thread.
+While the scoped_thread class defined in C++ Concurrency in Action is closer to strict_scoped_thread class that doesn't allows any change in the wrapped thread, Boost.Thread provides a class scoped_thread that provides the same non-deprecated interface as __thread.
[endsect]
[section:tutorial Tutorial]
-Scoped Threads are wrappers around a thread that allows the user to state what to do at destruction time. One of the common uses is to join the thread at destruction time so this is the default behavior. This is the single difference respect to a thread. While thread call std::terminate() on the destructor is the thread is joinable, strict_scoped_thread<> or scoped_thread<> join the thread if joinable.
+Scoped Threads are wrappers around a thread that allows the user to state what to do at destruction time. One of the common uses is to join the thread at destruction time so this is the default behavior. This is the single difference respect to a thread. While thread call std::terminate() on the destructor if the thread is joinable, strict_scoped_thread<> or scoped_thread<> join the thread if joinable.
-The difference between strict_scoped_thread and scoped_thread is that the strict_scoped_thread hides completely the owned thread and so the user can do nothing with the owned thread other than the specific action given as parameter, while scoped_thread provide the same interface than __thread and forwards all the operations.
+The difference between strict_scoped_thread and scoped_thread is that the strict_scoped_thread hides completely the owned thread and so the user can do nothing with the owned thread other than the specific action given as parameter, while scoped_thread provide the same interface as __thread and forwards all the operations.
boost::strict_scoped_thread<> t1((boost::thread(f)));
//t1.detach(); // compile fails
@@ -114,15 +114,15 @@ The difference between strict_scoped_thread and scoped_thread is that the strict
};
-RAI __thread wrapper adding a specific destroyer allowing to master what can be done at destruction time.
+RAII __thread wrapper adding a specific destroyer allowing to master what can be done at destruction time.
CallableThread: A callable `void(thread&)`.
The default is a `join_if_joinable`.
-`std/boost::thread` destructor terminates the program if the __thread is not joinable.
-This wrapper can be used to join the thread before destroying it seems a natural need.
+Thread destructor terminates the program if the __thread is joinable.
+This wrapper can be used to join the thread before destroying it.
[heading Example]
@@ -233,13 +233,13 @@ This wrapper can be used to join the thread before destroying it seems a natural
void swap(scoped_thread& lhs,scoped_thread& rhs) noexcept;
-RAI __thread wrapper adding a specific destroyer allowing to master what can be done at destruction time.
+RAII __thread wrapper adding a specific destroyer allowing to master what can be done at destruction time.
CallableThread: A callable void(thread&).
The default is join_if_joinable.
-thread std::thread destructor terminates the program if the thread is not joinable.
-Having a wrapper that can join the thread before destroying it seems a natural need.
+Thread destructor terminates the program if the thread is joinable.
+This wrapper can be used to join the thread before destroying it.
Remark: `scoped_thread` is not a __thread as __thread is not designed to be derived from as a polymorphic type.
@@ -312,9 +312,9 @@ any) to `*this`.
[variablelist
-[[Effects:] [move the thread to own `t_`.]]
+[[Effects:] [Transfers ownership of the thread managed by `other` (if any) to the newly constructed scoped_thread instance.]]
-[[Postconditions:] [`*this.t_` refers to the newly created thread of execution and `this->get_id()!=thread::id()`.]]
+[[Postconditions:] [other.get_id()==thread::id() and get_id() returns the value of other.get_id() prior to the construction.]]
[[Throws:] [Nothing]]
@@ -329,7 +329,7 @@ any) to `*this`.
[variablelist
-[[Effects:] [Construct a internal thread in place.]]
+[[Effects:] [Construct an internal thread in place.]]
[[Postconditions:] [`*this.t_` refers to the newly created thread of execution and `this->get_id()!=thread::id()`.]]
diff --git a/libs/thread/doc/sync_queues_ref.qbk b/libs/thread/doc/sync_queues_ref.qbk
index a46f34fc5..eba795fc3 100644
--- a/libs/thread/doc/sync_queues_ref.qbk
+++ b/libs/thread/doc/sync_queues_ref.qbk
@@ -181,7 +181,7 @@ where
[variablelist
-[[Requires:] [Q::value_type is no throw copy movable. This is needed to ensure the exception safety.]]
+[[Requires:] [Q::value_type is no throw move constructible. This is needed to ensure the exception safety.]]
[[Effects:] [Waits until the queue is not empty and not closed. If the queue is empty and closed throws sync_queue_is_closed. Otherwise pull the element from the queue `q` and moves the pulled element.]]
@@ -206,7 +206,7 @@ where
[variablelist
-[/[Requires:] [Q::value_type is no throw copy movable. This is needed to ensure the exception safety. ]]
+[/[Requires:] [Q::value_type is no throw move assignable. This is needed to ensure the exception safety. ]]
[[Effects:] [Waits until the queue is not empty and not closed. If the queue is empty and closed throws sync_queue_is_closed. Otherwise pull the element from the queue `q` and moves the pulled element into a shared_ptr.]]
@@ -314,7 +314,7 @@ where
[variablelist
-[[Effects:] [Waits until the queue is not empty and then pull the element from the queue `q` and moves the pulled element into `lve` (this could need an allocation for unbounded queues).]]
+[[Effects:] [If the queue is not empty pulls the element from the queue `q` and moves the pulled element into `lve` (this could need an allocation for unbounded queues).]]
[[Synchronization:] [Prior pull-like operations on the same object synchronizes with this operation.]]
@@ -393,7 +393,7 @@ where
[endsect]
[/////////////////////////////////////]
-[section:nonblocking_push_back_m `s = q.nonblocking_push_back(rve());`]
+[section:nonblocking_push_back_m `s = q.nonblocking_push_back(rve);`]
[variablelist
@@ -427,7 +427,7 @@ where
[variablelist
-[[Effects:] [Waits until the queue is not empty and then pull the element from the queue `q` and moves the pulled element into `lve` (this could need an allocation for unbounded queues).]]
+[[Effects:] [If the queue is not empty pulls the element from the queue `q` and moves the pulled element into `lve` (this could need an allocation for unbounded queues).]]
[[Synchronization:] [Prior pull-like operations on the same object synchronizes with this operation.]]
@@ -636,12 +636,12 @@ Closed queues add the following valid expressions
namespace boost
{
- template <typename ValueType>
+ template <typename ValueType, class SizeType=std::size_t>
class queue_base
{
public:
typedef ValueType value_type;
- typedef std::size_t size_type;
+ typedef SizeType size_type;
// Constructors/Assignment/Destructors
virtual ~queue_base() {};
@@ -671,7 +671,7 @@ Closed queues add the following valid expressions
virtual queue_op_status wait_push_back(const value_type& x) = 0;
virtual queue_op_status wait_push_back(BOOST_THREAD_RV_REF(value_type) x) = 0;
- virtual queue_op_status wait_pull_front(ValueType& elem) = 0;
+ virtual queue_op_status wait_pull_front(value_type& elem) = 0;
};
}
@@ -685,11 +685,11 @@ Closed queues add the following valid expressions
namespace boost
{
template <typename Queue>
- class queue_adaptor : public queue_base<typename Queue::value_type>
+ class queue_adaptor : public queue_base<typename Queue::value_type, typename Queue::size_type>
{
public:
typedef typename Queue::value_type value_type;
- typedef std::size_t size_type;
+ typedef typename Queue::size_type size_type;
// Constructors/Assignment/Destructors
@@ -1002,13 +1002,13 @@ Closed queues add the following valid expressions
namespace boost
{
- template <typename ValueType>
+ template <typename ValueType, class Container = csbl::devector<ValueType>>
class sync_queue
{
public:
typedef ValueType value_type;
- typedef csbl::deque<ValueType> underlying_queue_type;
- typedef std::size_t size_type;
+ typedef Container underlying_queue_type;
+ typedef typename Container::size_type size_type;
sync_queue(sync_queue const&) = delete;
sync_queue& operator=(sync_queue const&) = delete;
diff --git a/libs/thread/doc/synchronized_value_ref.qbk b/libs/thread/doc/synchronized_value_ref.qbk
index 20b304c80..2826282e1 100644
--- a/libs/thread/doc/synchronized_value_ref.qbk
+++ b/libs/thread/doc/synchronized_value_ref.qbk
@@ -88,9 +88,9 @@
typedef T value_type;
typedef Lockable mutex_type;
- synchronized_value() noexept(is_nothrow_default_constructible<T>::value);
- synchronized_value(T const& other) noexept(is_nothrow_copy_constructible<T>::value);
- synchronized_value(T&& other) noexept(is_nothrow_move_constructible<T>::value);
+ synchronized_value() noexcept(is_nothrow_default_constructible<T>::value);
+ synchronized_value(T const& other) noexcept(is_nothrow_copy_constructible<T>::value);
+ synchronized_value(T&& other) noexcept(is_nothrow_move_constructible<T>::value);
synchronized_value(synchronized_value const& rhs);
synchronized_value(synchronized_value&& other);
@@ -129,7 +129,7 @@
[section:constructor `synchronized_value()`]
- synchronized_value() noexept(is_nothrow_default_constructible<T>::value);
+ synchronized_value() noexcept(is_nothrow_default_constructible<T>::value);
[variablelist
@@ -145,7 +145,7 @@
[section:constructor_vt `synchronized_value(T const&)`]
- synchronized_value(T const& other) noexept(is_nothrow_copy_constructible<T>::value);
+ synchronized_value(T const& other) noexcept(is_nothrow_copy_constructible<T>::value);
[variablelist
@@ -175,11 +175,11 @@
[section:move_vt `synchronized_value(T&&)`]
- synchronized_value(T&& other) noexept(is_nothrow_move_constructible<T>::value);
+ synchronized_value(T&& other) noexcept(is_nothrow_move_constructible<T>::value);
[variablelist
-[[Requires:] [`T` is `CopyMovable `.]]
+[[Requires:] [`T` is `MoveConstructible `.]]
[[Effects:] [Move constructs the cloaked value_type]]
[[Throws:] [Any exception thrown by `value_type(value_type&&)`.]]
@@ -194,7 +194,7 @@
[variablelist
-[[Requires:] [`T` is `CopyMovable `.]]
+[[Requires:] [`T` is `MoveConstructible `.]]
[[Effects:] [Move constructs the cloaked value_type]]
[[Throws:] [Any exception thrown by `value_type(value_type&&)` or `mtx_.lock()`.]]
@@ -209,7 +209,7 @@
[variablelist
-[[Requires:] [`T` is `Assignale`.]]
+[[Requires:] [`T` is `Assignable`.]]
[[Effects:] [Copies the underlying value on a scope protected by the two mutexes. The mutex is not copied. The locks are acquired avoiding deadlock. For example, there is no problem if one thread assigns `a = b` and the other assigns `b = a`.]]
[[Return:] [`*this`]]
@@ -224,7 +224,7 @@
[variablelist
-[[Requires:] [`T` is `Assignale`.]]
+[[Requires:] [`T` is `Assignable`.]]
[[Effects:] [Copies the value on a scope protected by the mutex.]]
[[Return:] [`*this`]]
@@ -273,7 +273,7 @@
[variablelist
-[[Requires:] [`T` is `Assignale`.]]
+[[Requires:] [`T` is `Assignable`.]]
[[Effects:] [Swaps the data on a scope protected by both mutex. Both mutex are acquired to avoid dead-lock. The mutexes are not swapped.]]
[[Throws:] [Any exception thrown by `swap(value_, rhs.value)` or `mtx_.lock()` or `rhs_.mtx_.lock()`.]]
diff --git a/libs/thread/doc/thread.qbk b/libs/thread/doc/thread.qbk
index 6d33eb5d8..9605234da 100644
--- a/libs/thread/doc/thread.qbk
+++ b/libs/thread/doc/thread.qbk
@@ -8,10 +8,10 @@
[library Thread
[quickbook 1.5]
- [version 4.4.0]
+ [version 4.5.0]
[authors [Williams, Anthony] [Botet Escriba, Vicente J.]]
[copyright 2007-11 Anthony Williams]
- [copyright 2011-14 Vicente J. Botet Escriba]
+ [copyright 2011-15 Vicente J. Botet Escriba]
[purpose C++ Library for launching threads and synchronizing data between them]
[category text]
[license
@@ -244,7 +244,6 @@
[include futures.qbk]
[endsect]
-
[include tss.qbk]
[section:sds Synchronized Data Structures]
@@ -253,6 +252,8 @@
[/include sync_streams.qbk]
[endsect]
+[include parallel.qbk]
+
[include time.qbk]
[include emulations.qbk]
diff --git a/libs/thread/doc/thread_ref.qbk b/libs/thread/doc/thread_ref.qbk
index 9709d5453..a1e86426f 100644
--- a/libs/thread/doc/thread_ref.qbk
+++ b/libs/thread/doc/thread_ref.qbk
@@ -125,7 +125,7 @@ the user to set the platform specific attributes. Boost.Thread stay in the middl
thread::attributes which allows to set at least in a portable way the stack size as follows:
boost::thread::attributes attrs;
- attrs.set_size(4096*10);
+ attrs.set_stack_size(4096*10);
boost::thread deep_thought_2(attrs, find_the_question, 42);
Even for this simple attribute there could be portable issues as some platforms could require that the stack size
@@ -147,7 +147,7 @@ Next follows how the user could set the stack size and the scheduling policy on
// ... window version
#elif defined(BOOST_THREAD_PLATFORM_PTHREAD)
// ... pthread version
- pthread_attr_setschedpolicy(attr.get_native_handle(), SCHED_RR);
+ pthread_attr_setschedpolicy(attr.native_handle(), SCHED_RR);
#else
#error "Boost threads unavailable on this platform"
#endif
@@ -432,12 +432,14 @@ This behavior is incompatible with the current Boost.Thread design, so the use o
class attributes; // EXTENSION
thread() noexcept;
+ ~thread();
+
thread(const thread&) = delete;
thread& operator=(const thread&) = delete;
+ // move support
thread(thread&&) noexcept;
thread& operator=(thread&&) noexcept;
- ~thread();
template <class F>
explicit thread(F f);
@@ -456,10 +458,6 @@ This behavior is incompatible with the current Boost.Thread design, so the use o
template <class F, class ...Args>
explicit thread(attributes& attrs, F&& f, Args&&... args);
- // move support
- thread(thread && x) noexcept;
- thread& operator=(thread && x) noexcept;
-
void swap(thread& x) noexcept;
class id;