|
Boost-Commit : |
Subject: [Boost-commit] svn:boost r81282 - trunk/libs/thread/doc
From: vicente.botet_at_[hidden]
Date: 2012-11-10 05:32:07
Author: viboes
Date: 2012-11-10 05:32:06 EST (Sat, 10 Nov 2012)
New Revision: 81282
URL: http://svn.boost.org/trac/boost/changeset/81282
Log:
Thread: update doc with lockable traits, yuturial, compliance.
Text files modified:
trunk/libs/thread/doc/compliance.qbk | 17 +-
trunk/libs/thread/doc/future_ref.qbk | 14 +-
trunk/libs/thread/doc/futures.qbk | 184 +++++++++++++++++++++++++++++++++++++++
trunk/libs/thread/doc/mutex_concepts.qbk | 65 +++++++++++++
4 files changed, 261 insertions(+), 19 deletions(-)
Modified: trunk/libs/thread/doc/compliance.qbk
==============================================================================
--- trunk/libs/thread/doc/compliance.qbk (original)
+++ trunk/libs/thread/doc/compliance.qbk 2012-11-10 05:32:06 EST (Sat, 10 Nov 2012)
@@ -25,10 +25,10 @@
[[30.2.5.3] [Lockable requirements] [yes] [-] [-]]
[[30.2.5.4] [TimedLockable requirements] [Yes] [-] [-]]
[[30.2.6] [decay_copy] [-] [-] [-]]
- [[30.3] [Threads] [Partial] [-] [-]]
- [[30.3.1] [Class thread] [Partial] [move,variadic,terminate] [#6270]]
+ [[30.3] [Threads] [Yes] [-] [-]]
+ [[30.3.1] [Class thread] [Yes] [-] [-]]
[[30.3.1.1] [Class thread::id] [Yes] [-] [-]]
- [[30.3.1.2] [thread constructors] [Partial] [move,variadic] [#6270]]
+ [[30.3.1.2] [thread constructors] [Partial] [-] [-]]
[[30.3.1.3] [thread destructor] [Yes] [-] [-]]
[[30.3.1.4] [thread assignment] [Yes] [-] [-]]
[[30.3.1.5] [thread members] [Yes] [-] [-]]
@@ -56,20 +56,19 @@
[[30.4.4] [Call once] [Partial] [call_once] [#7285]]
[[30.4.4.1] [Struct once_flag] [Yes] [-] [-]]
[[30.4.4.2] [Function call_once] [Partial] [interface] [#7285]]
- [[30.5] [Condition variables] [Partial] [notify_all_at_thread_exit] [#7283]]
- [[30.5 6-10] [Function notify_all_at_thread_exit] [No] [-] [#7283]]
+ [[30.5] [Condition variables] [Yes] [-] [-]]
[[30.5.1] [Class condition_variable] [Yes] [-] [-]]
[[30.5.2] [Class condition_variable_any] [Yes] [-] [-]]
- [[30.6] [Futures] [Partial] [async,at_thread_exit] [#4710,#7280]]
+ [[30.6] [Futures] [Partial] [noexcept] [#7279]]
[[30.6.1] [Overview] [Partial] [-] [-]]
[[30.6.2] [Error handling] [Yes] [-] [-]]
[[30.6.3] [Class future_error] [Partial] [noexcept] [#7279]]
[[30.6.4] [Shared state] [-] [-] [-]]
- [[30.6.5] [Class template promise] [Partial] [at_thread_exit] [#7280]]
+ [[30.6.5] [Class template promise] [Yes] [-] [-]]
[[30.6.6] [Class template future] [Yes] [-] [-]]
[[30.6.7] [Class template shared_future] [Yes] [-] [-]]
- [[30.6.8] [Function template async] [Partial] [deferred not implemented and only a copyable functor is allowed yet] [#4710]]
- [[30.6.9] [Class template packaged_task] [Partial] [args,make_ready_at_thread_exit] [#7281,#7282]]
+ [[30.6.8] [Function template async] [Yes] [-] [-]]
+ [[30.6.9] [Class template packaged_task] [Yes] [-] [-]]
]
[/
Modified: trunk/libs/thread/doc/future_ref.qbk
==============================================================================
--- trunk/libs/thread/doc/future_ref.qbk (original)
+++ trunk/libs/thread/doc/future_ref.qbk 2012-11-10 05:32:06 EST (Sat, 10 Nov 2012)
@@ -117,7 +117,7 @@
[endsect]
-[section:future_errc Enumeration `future_errc `]
+[section:future_errc Enumeration `future_errc`]
enum class future_errc
{
@@ -131,7 +131,7 @@
The enum values of future_errc are distinct and not zero.
[endsect]
-[section:launch Enumeration `launch `]
+[section:launch Enumeration `launch`]
enum class launch
{
@@ -277,7 +277,7 @@
R&& get();
// functions to check state
- bool valid() const;
+ bool valid() const noexcept;
bool is_ready() const; // EXTENSION
bool has_exception() const; // EXTENSION
bool has_value() const; // EXTENSION
@@ -424,7 +424,7 @@
[section:wait Member function `wait()`]
- void wait();
+ void wait() const;
[variablelist
@@ -605,7 +605,7 @@
[section:valid Member function `valid()`]
- bool valid() const;
+ bool valid() const noexcept;
[variablelist
@@ -827,7 +827,7 @@
[section:wait Member function `wait()`]
- void wait();
+ void wait() const;
[variablelist
@@ -989,7 +989,7 @@
[section:valid Member function `valid()`]
- bool valid() const;
+ bool valid() const noexcept;
[variablelist
Modified: trunk/libs/thread/doc/futures.qbk
==============================================================================
--- trunk/libs/thread/doc/futures.qbk (original)
+++ trunk/libs/thread/doc/futures.qbk 2012-11-10 05:32:06 EST (Sat, 10 Nov 2012)
@@ -93,10 +93,13 @@
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 `const` reference so that multiple calls to __shared_future_get__ are safe. You can move an
+and __shared_future_get__ returns a non `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.
+`boost::async` is a simple way of running asynchronous tasks. A call to `boost::async` returns a __unique_future__ that will contain the result of the task.
+
+
You can wait for futures either individually or with one of the __wait_for_any__ and __wait_for_all__ functions.
[endsect]
@@ -184,7 +187,186 @@
[endsect]
+[section:at_thread_exit Handling Detached Threads and Thread Specific Variables]
+
+Detached threads pose a problem for objects with thread storage duration.
+If we use a mechanism other than `thread::__join` to wait for a __thread to complete its work - such as waiting for a future to be ready -
+then the destructors of thread specific variables will still be running after the waiting thread has resumed.
+This section explain how the standard mechanism can be used to make such synchronization safe by ensuring that the
+objects with thread storage duration are destroyed prior to the future being made ready. e.g.
+
+ int find_the_answer(); // uses thread specific objects
+ void thread_func(boost::promise<int>&& p)
+ {
+ p.set_value_at_thread_exit(find_the_answer());
+ }
+
+ int main()
+ {
+ boost::promise<int> p;
+ boost::thread t(thread_func,boost::move(p));
+ t.detach(); // we're going to wait on the future
+ std::cout<<p.get_future().get()<<std::endl;
+ }
+
+When the call to `get()` returns, we know that not only is the future value ready, but the thread specific variables
+on the other thread have also been destroyed.
+
+Such mechanisms are provided for `boost::condition_variable`, `boost::promise` and `boost::packaged_task`. e.g.
+
+ void task_executor(boost::packaged_task<void(int)> task,int param)
+ {
+ task.make_ready_at_thread_exit(param); // execute stored task
+ } // destroy thread specific and wake threads waiting on futures from task
+
+Other threads can wait on a future obtained from the task without having to worry about races due to the execution of
+destructors of the thread specific objects from the task's thread.
+
+ boost::condition_variable cv;
+ boost::mutex m;
+ complex_type the_data;
+ bool data_ready;
+
+ void thread_func()
+ {
+ boost::unique_lock<std::mutex> lk(m);
+ the_data=find_the_answer();
+ data_ready=true;
+ boost::notify_all_at_thread_exit(cv,boost::move(lk));
+ } // destroy thread specific objects, notify cv, unlock mutex
+
+ void waiting_thread()
+ {
+ boost::unique_lock<std::mutex> lk(m);
+ while(!data_ready)
+ {
+ cv.wait(lk);
+ }
+ process(the_data);
+ }
+
+The waiting thread is guaranteed that the thread specific objects used by `thread_func()` have been destroyed by the time
+`process(the_data)` is called. If the lock on `m` is released and re-acquired after setting `data_ready` and before calling
+`boost::notify_all_at_thread_exit()` then this does NOT hold, since the thread may return from the wait due to a
+spurious wake-up.
+
+[endsect]
+
[section:async Executing asynchronously]
+
+`boost::async` is a simple way of running asynchronous tasks to make use of the available hardware concurrency.
+A call to `boost::async` returns a `boost::future` that will contain the result of the task. Depending on
+the launch policy, the task is either run asynchronously on its own thread or synchronously on whichever thread
+calls the `wait()` or `get()` member functions on that `future`.
+
+A launch policy of either boost::launch::async, which asks the runtime to create an asynchronous thread,
+or boost::launch::deferred, which indicates you simply want to defer the function call until a later time (lazy evaluation).
+This argument is optional - if you omit it your function will use the default policy.
+
+For example, consider computing the sum of a very large array. The first task is to not compute asynchronously when
+the overhead would be significant. The second task is to split the work into two pieces, one executed by the host
+thread and one executed asynchronously.
+
+
+ int parallel_sum(int* data, int size)
+ {
+ int sum = 0;
+ if ( size < 1000 )
+ for ( int i = 0; i < size; ++i )
+ sum += data[i];
+ else {
+ auto handle = boost::async(parallel_sum, data+size/2, size-size/2);
+ sum += parallel_sum(data, size/2);
+ sum += handle.get();
+ }
+ return sum;
+ }
+
+
+
+[endsect]
+
+[section:shared Shared Futures]
+
+`shared_future` is designed to be shared between threads,
+that is to allow multiple concurrent get operations.
+
+[heading Multiple get]
+
+The second `get()` call in the following example future
+
+ void bad_second_use( type arg ) {
+
+ auto ftr = async( [=]{ return work( arg ); } );
+ if ( cond1 )
+ {
+ use1( ftr.get() );
+ } else
+ {
+ use2( ftr.get() );
+ }
+ use3( ftr.get() ); // second use is undefined
+ }
+
+Using a `shared_mutex` solves the issue
+
+ void good_second_use( type arg ) {
+
+ shared_future<type> ftr = async( [=]{ return work( arg ); } );
+ if ( cond1 )
+ {
+ use1( ftr.get() );
+ } else
+ {
+ use2( ftr.get() );
+ }
+ use3( ftr.get() ); // second use is defined
+ }
+
+[heading share()]
+
+Namming the return type when declaring the `shared_future` is needed; auto is not available within template argument lists.
+Here `share()` could be used to simplify the code
+
+ void better_second_use( type arg ) {
+
+ auto ftr = async( [=]{ return work( arg ); } ).share();
+ if ( cond1 )
+ {
+ use1( ftr.get() );
+ } else
+ {
+ use2( ftr.get() );
+ }
+ use3( ftr.get() ); // second use is defined
+ }
+
+[heading Writting on get()]
+
+The user can either read or write the future avariable.
+
+ void write_to_get( type arg ) {
+
+ auto ftr = async( [=]{ return work( arg ); } ).share();
+ if ( cond1 )
+ {
+ use1( ftr.get() );
+ } else
+ {
+ if ( cond2 )
+ use2( ftr.get() );
+ else
+ ftr.get() = something(); // assign to non-const reference.
+ }
+ use3( ftr.get() ); // second use is defined
+ }
+
+This works because the `shared_future<>::get()` function returns a non-const reference to the appropriate storage.
+Of course the access to this storage must be ensured by the user. The library doesn't ensure the access to the internal storage is thread safe.
+
+There has been some work by the C++ standard committe on an `atomic_future` that behaves as an `atomic` variable, that is is thread_safe,
+and a `shared_future` that can be shared between several threads, but there were not enough consensus and time to get it ready for C++11.
+
[endsect]
[section:make_future Making immediate futures easier]
Modified: trunk/libs/thread/doc/mutex_concepts.qbk
==============================================================================
--- trunk/libs/thread/doc/mutex_concepts.qbk (original)
+++ trunk/libs/thread/doc/mutex_concepts.qbk 2012-11-10 05:32:06 EST (Sat, 10 Nov 2012)
@@ -80,18 +80,33 @@
[endsect]
+[section:is_basic_lockable `is_basic_lockable` trait]
+
+ // #include <boost/thread/lockable_traits.hpp>
+
+ namespace boost
+ {
+ namespace sync
+ {
+ template<typename L>
+ class is_basic_lockable;
+ }
+ }
+
+Some of the algorithms on mutexes use this trait via SFINAE. If BOOST_THREAD_NO_AUTO_DETECT_MUTEX_TYPES is defined you will need to specialize this traits for the models of BasicLockable you could build.
+
[endsect]
+[endsect]
+
[section:lockable `Lockable` Concept]
// #include <boost/thread/lockable_concepts.hpp>
-
namespace boost
{
template<typename L>
class Lockable;
}
-
A type `L` meets the __Lockable requirements if it meets the __BasicLockable requirements and the following expressions are well-formed and have the specified semantics (`m` denotes a value of type `L`):
* `m.__try_lock()`
@@ -115,7 +130,53 @@
]
[endsect]
+[section:is_lockable `is_lockable` trait]
+
+ // #include <boost/thread/lockable_traits.hpp>
+ namespace boost
+ {
+ namespace sync
+ {
+ template<typename L>
+ class is_lockable;
+ }
+ }
+
+Some of the algorithms on mutexes use this trait via SFINAE. If BOOST_THREAD_NO_AUTO_DETECT_MUTEX_TYPES is defined you will need to specialize this traits for the models of Lockable you could build.
+
[endsect]
+[endsect]
+
+[section:recursive Recursive Lockable Concept]
+
+The user could require that the mutex passed to an algorithm is a recursive one. Whether a lockable is recursive or not can not be checked using template meta-programming. This is the motivation for the following trait.
+
+
+[section:is_recursive_mutex_sur_parolle `is_recursive_mutex_sur_parolle` trait]
+
+ // #include <boost/thread/lockable_traits.hpp>
+
+ namespace boost
+ {
+ namespace sync
+ {
+ template<typename L>
+ class is_recursive_mutex_sur_parolle: false_type;
+ template<>
+ class is_recursive_mutex_sur_parolle<recursive_mutex>: true_type;
+ template<>
+ class is_recursive_mutex_sur_parolle<timed_recursive_mutex>: true_type;
+ }
+ }
+
+The trait `is_recursive_mutex_sur_parolle` is `false_type` by default and is specialized for the provide `recursive_mutex` and `timed_recursive_mutex`.
+
+It should be specialized by the user providing other model of recursive lockable.
+
+[endsect]
+
+[endsect]
+
[section:timed_lockable `TimedLockable` Concept]
Boost-Commit list run by bdawes at acm.org, david.abrahams at rcn.com, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk