re PR libstdc++/50834 (Documentation about STL thread safety is ambiguous)

PR libstdc++/50834
	* doc/xml/manual/using.xml: Update thread safety docs w.r.t. C++11.

From-SVN: r180359
This commit is contained in:
Jonathan Wakely 2011-10-24 00:19:56 +00:00 committed by Jonathan Wakely
parent a0d90b97f0
commit ec8ab7c4e4
2 changed files with 95 additions and 16 deletions

View file

@ -1,3 +1,8 @@
2011-10-22 Jonathan Wakely <jwakely.gcc@gmail.com>
PR libstdc++/50834
* doc/xml/manual/using.xml: Update thread safety docs w.r.t. C++11.
2011-10-22 Jonathan Wakely <jwakely.gcc@gmail.com>
PR libstdc++/50196

View file

@ -1281,9 +1281,16 @@ A quick read of the relevant part of the GCC
<section xml:id="manual.intro.using.concurrency.thread_safety" xreflabel="Thread Safety"><info><title>Thread Safety</title></info>
<para>
We currently use the <link xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://www.sgi.com/tech/stl/thread_safety.html">SGI STL</link> definition of thread safety.
In the terms of the 2011 C++ standard a thread-safe program is one which
does not perform any conflicting non-atomic operations on memory locations
and so does not contain any data races.
The standard places requirements on the library to ensure that no data
races are caused by the library itself or by programs which use the
library correctly (as described below).
The C++11 memory model and library requirements are a more formal version
of the <link xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://www.sgi.com/tech/stl/thread_safety.html">SGI STL</link> definition of thread safety, which the library used
prior to the 2011 standard.
</para>
@ -1329,17 +1336,25 @@ gcc version 4.1.2 20070925 (Red Hat 4.1.2-33)
</listitem>
</itemizedlist>
<para>The user-code must guard against concurrent method calls which may
access any particular library object's state. Typically, the
application programmer may infer what object locks must be held
based on the objects referenced in a method call. Without getting
<para>The user code must guard against concurrent function calls which
access any particular library object's state when one or more of
those accesses modifies the state. An object will be modified by
invoking a non-const member function on it or passing it as a
non-const argument to a library function. An object will not be
modified by invoking a const member function on it or passing it to
a function as a pointer- or reference-to-const.
Typically, the application
programmer may infer what object locks must be held based on the
objects referenced in a function call and whether the objects are
accessed as const or non-const. Without getting
into great detail, here is an example which requires user-level
locks:
</para>
<programlisting>
library_class_a shared_object_a;
thread_main () {
void thread_main () {
library_class_b *object_b = new library_class_b;
shared_object_a.add_b (object_b); // must hold lock for shared_object_a
shared_object_a.mutate (); // must hold lock for shared_object_a
@ -1347,25 +1362,84 @@ gcc version 4.1.2 20070925 (Red Hat 4.1.2-33)
// Multiple copies of thread_main() are started in independent threads.</programlisting>
<para>Under the assumption that object_a and object_b are never exposed to
another thread, here is an example that should not require any
another thread, here is an example that does not require any
user-level locks:
</para>
<programlisting>
thread_main () {
void thread_main () {
library_class_a object_a;
library_class_b *object_b = new library_class_b;
object_a.add_b (object_b);
object_a.mutate ();
} </programlisting>
<para>All library objects are safe to use in a multithreaded program as
long as each thread carefully locks out access by any other
thread while it uses any object visible to another thread, i.e.,
treat library objects like any other shared resource. In general,
this requirement includes both read and write access to objects;
unless otherwise documented as safe, do not assume that two threads
may access a shared standard library object at the same time.
<para>All library types are safe to use in a multithreaded program
if objects are not shared between threads or as
long each thread carefully locks out access by any other
thread while it modifies any object visible to another thread.
Unless otherwise documented, the only exceptions to these rules
are atomic operations on the types in
<filename class="headerfile">&lt;atomic&gt;</filename>
and lock/unlock operations on the standard mutex types in
<filename class="headerfile">&lt;mutex&gt;</filename>. These
atomic operations allow concurrent accesses to the same object
without introducing data races.
</para>
<para>The following member functions of standard containers can be
considered to be const for the purposes of avoiding data races:
<code>begin</code>, <code>end</code>, <code>rbegin</code>, <code>rend</code>,
<code>front</code>, <code>back</code>, <code>data</code>,
<code>find</code>, <code>lower_bound</code>, <code>upper_bound</code>,
<code>equal_range</code>, <code>at</code>
and, except in associative or unordered associative containers,
<code>operator[]</code>. In other words, although they are non-const
so that they can return mutable iterators, those member functions
will not modify the container.
Accessing an iterator might cause a non-modifying access to
the container the iterator refers to (for example incrementing a
list iterator must access the pointers between nodes, which are part
of the container and so conflict with other accesses to the container).
</para>
<para>Programs which follow the rules above will not encounter data
races in library code, even when using library types which share
state between distinct objects. In the example below the
<code>shared_ptr</code> objects share a reference count, but
because the code does not perform any non-const operations on the
globally-visible object, the library ensures that the reference
count updates are atomic and do not introduce data races:
</para>
<programlisting>
std::shared_ptr&lt;int&gt; global_sp;
void thread_main() {
auto local_sp = global_sp; // OK, copy constructor's parameter is reference-to-const
int i = *global_sp; // OK, operator* is const
int j = *local_sp; // OK, does not operate on global_sp
// *global_sp = 2; // NOT OK, modifies int visible to other threads
// *local_sp = 2; // NOT OK, modifies int visible to other threads
// global_sp.reset(); // NOT OK, reset is non-const
local_sp.reset(); // OK, does not operate on global_sp
}
int main() {
global_sp.reset(new int(1));
std::thread t1(thread_main);
std::thread t2(thread_main);
t1.join();
t2.join();
}
</programlisting>
<para>For further details of the C++11 memory model see Hans-J. Boehm's
<link xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://www.hpl.hp.com/personal/Hans_Boehm/c++mm/user-faq.html">Threads
and memory model for C++</link> pages, particularly the <link xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://www.hpl.hp.com/personal/Hans_Boehm/c++mm/threadsintro.html">introduction</link>
and <link xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://www.hpl.hp.com/personal/Hans_Boehm/c++mm/user-faq.html">FAQ</link>.
</para>
</section>
<section xml:id="manual.intro.using.concurrency.atomics" xreflabel="Atomics"><info><title>Atomics</title></info>