Doc. no.   N2410=07-0270
Date:        2007-09-07
Project:     Programming Language C++
Reply to:   Beman Dawes <bdawes at acm.org>
                Peter Dimov <pdimov at pdimov.com>
                Herb Sutter <hsutter at microsoft.com>

Thread-Safety in the Standard Library (Rev 1)

Executive summary
Introduction
Rationale
Existing practice
Proposed wording
Acknowledgements
Revision history
References

Executive summary

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..

Introduction

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.

Rationale

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.

Basic thread-safety guarantee

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.

Strong thread-safety guarantee

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.

Meaning of thread-safety

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.

Standard library components not given strong guarantee

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.

Existing practice

As far as is known, the proposed wording reflects existing practice in current implementations of the standard library.

Proposed Wording

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 (17.4.4.1), macros (17.4.4.2), global functions (17.4.4.3), member functions (17.4.4.4), reentrancy (17.4.4.5), access specifiers (17.4.4.6), class derivation (17.4.4.7), and exceptions (17.4.4.8), and thread safety ([res.on.thread.safety]).

At the end of [conforming] add a new subsection:

17.4.4.9 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 new and delete shall 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:

A separate errno value 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:

Functions asctime, ctime, gmtime, and localtime are not require to be thread-safe ([res.on.thread.safety]).

To 21.4 Null-terminated sequence utilities [c.strings], add:

Functions strerror and strtok are not require to be thread-safe ([res.on.thread.safety]).

Change 26.7 C Library [c.math] paragraph 5 and 6 as indicated:

The rand function has the semantics specified in the C standard, except that the implementation may specify that particular library functions may call rand. The rand function is not require to be thread-safe ([res.on.thread.safety]).

Acknowledgements

Hans-J. Boehm provided helpful suggestions for improving the proposed wording.

Revision history

N2410 - Revision 1:

N2298 - Initial version.

References

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