Doc. no. N2410=07-0270
Project: Programming Language C++
Reply to: Beman Dawes <bdawes at acm.org>
Peter Dimov <pdimov at pdimov.com>
Herb Sutter <hsutter at microsoft.com>
Unless otherwise specified, standard library classes may safely be instantiated from multiple threads and standard library functions are reentrant, but objects of standard library types are not safe to share between threads if an expression evaluation conflict (N2334) occurs, unless protected by a synchronization mechanism..
With the introduction of multi-threading into the C++ standard, the contract between standard library users and implementors needs to explicitly state the conditions under which standard library components are or are not thread-safe.
The objective is to offer users of the standard library as much thread-safety as is possible without impacting performance or creating an illusion of safety where none exists.
Guaranteeing standard library functions be reentrant and guaranteeing thread-safety for standard library types has little or no impact on performance. It does actually deliver the promised safety. Thus this basic thread-safety is required of implementations.
Guaranteeing thread-safety for mutating shared objects would have a severe negative impact on performance. Furthermore, real safety often requires locking across several member function calls, so providing per function-call locking would create a illusion of safety that did in fact not exist. For these reasons, strong thread-safety for mutating shared objects is not provided, and constraints are put on programs accordingly.
The proposed wording talks in terms of data races and expression evaluation conflicts. Data races and expression evaluation conflicts will be defined in the core language portion of the standard, so do not need to be further described in the library clauses.
Consideration was given to specifying rand function and the global locale objects on a per-thread basis. That is not proposed because it does not represent existing practice. Mac OS X, for example, does not support per-thread global locale objects.
As far as is known, the proposed wording reflects existing practice in current implementations of the standard library.
References shown as ([Multi-threaded executions and data races]) refer to the new section of that name described in N2334 or it successor.
Change 17.4.4 Conforming implementations [conforming] as indicated:
This subclause describes the constraints upon, and latitude of, implementations of the C++ Standard library. The following subclauses describe an implementationís use of headers (18.104.22.168), macros (22.214.171.124), global functions (126.96.36.199), member functions (188.8.131.52), reentrancy (184.108.40.206), access specifiers (220.127.116.11), class derivation (18.104.22.168),
andexceptions (22.214.171.124), and thread safety ([res.on.thread.safety]).
At the end of [conforming] add a new subsection:
126.96.36.199 Thread safety [res.on.thread.safety]
Unless otherwise specified, calls to standard library functions from different threads shall not result in a data race unless arguments to the calls, including
*this, directly or indirectly access objects shared between the threads in such a way that an expression evaluation conflict occurs ([Multi-threaded executions and data races]) .
[Note: This means, for example, that implementations can't use a static object for internal purposes without synchronization because it could cause a data race even in programs that do not explicitly share objects between threads. --end note]
Somewhere in [constraints] add a constraint on programs:
Unless otherwise specified, direct or indirect access to the same object of a standard library type from different threads results in undefined behavior if any of the accesses are via a non-const member function of the object or non-const argument to any standard library function, including
*this, and one does not happen before the other ([Multi-threaded executions and data races]).
[Note: This lack of strong thread-safety guarantee means that modifying an object of a standard library type shared between threads without using a locking mechanism may result in a data race or other undesirable behavior. --end note]
To 18.5.1 Storage allocation and deallocation [new.delete], add:
The library versions of operator
deleteshall not introduce data races ([Multi-threaded executions and data races]) as a result of concurrent calls from different threads. Calls that allocate or deallocate a particular unit of storage shall occur in a single total order, and each such deallocation call happens before the next allocation (if any) in this order.
To 19.3 Error numbers [errno] paragraph 1, add:
errnovalue shall be provided for each thread-of-execution.
To 20.6.1 The default allocator [default.allocator], add:
Except for the destructor, member functions of the default allocator shall not introduce data races ([Multi-threaded executions and data races]) as a result of concurrent accesses to default allocator objects from different threads. Allocation and deallocation calls that allocate or return a particular unit of storage shall occur in a single total order, and each such deallocation call happens before the next allocation (if any) in this order.
To 20.7 Date and Time [date.time], add:
localtimeare not require to be thread-safe ([res.on.thread.safety]).
To 21.4 Null-terminated sequence utilities [c.strings], add:
strtokare not require to be thread-safe ([res.on.thread.safety]).
Change 26.7 C Library [c.math] paragraph 5 and 6 as indicated:
randfunction has the semantics specified in the C standard, except that the implementation may specify that particular library functions may call
randfunction is not require to be thread-safe ([res.on.thread.safety]).
Hans-J. Boehm provided helpful suggestions for improving the proposed wording.
N2410 - Revision 1:
N2298 - Initial version.
N2334, Concurrency memory model (revised again), Clark Nelson and Hans-J. Boehm
N1947, The Memory Model and the C++ Library, Non-Memory Actions etc., Nick Maclaren