Index: doc/xml/manual/using.xml =================================================================== --- doc/xml/manual/using.xml (revision 180334) +++ doc/xml/manual/using.xml (working copy) @@ -1281,9 +1281,16 @@ A quick read of the relevant part of the
Thread Safety - -We currently use the SGI STL 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 SGI STL definition of thread safety, which the library used +prior to the 2011 standard. @@ -1329,17 +1336,25 @@ gcc version 4.1.2 20070925 (Red Hat 4.1. - 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 + + 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: 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. // Multiple copies of thread_main() are started in independent threads. 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: - 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 (); } - 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. + + 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 + <atomic> + and lock/unlock operations on the standard mutex types in + <mutex>. These + atomic operations allow concurrent accesses to the same object + without introducing data races. + The following member functions of standard containers can be + considered to be const for the purposes of avoiding data races: + begin, end, rbegin, rend, + front, back, data, + find, lower_bound, upper_bound, + equal_range, at + and, except in associative or unordered associative containers, + operator[]. 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). + + + 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 + shared_ptr 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: + + + std::shared_ptr<int> 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(); + } + + + For further details of the C++11 memory model see Hans-J. Boehm's + Threads + and memory model for C++ pages, particularly the introduction + and FAQ. +
Atomics