Doc. No.: WG21/N2775
J16/08-0285
Date: 2008-9-18
Authors: Hans-J. Boehm, Beman Dawes
Phone: +1-650-857-3406
Email: Hans.Boehm@hp.com

N2775: Small library thread-safety revisions

When N2669 (Thread-safety in the standard library) was voted into the working paper, there was a widespread sentiment that it was both a marked improvement and an incomplete solution. This is an attempt to address the most blatant remaining issues, and to thus move us closer to a complete solution.

Here we specifically address two issues:

  1. 17.4.3.9 [res.on.objects] is clearly much too restrictive on the programmer. As it stands, if a non-const vector is read by two threads that results in undefined behavior. If container elements happen to point to a global data structure G, simultaneous accesses to two containers pointing to G cause a data race, even though container operations have no business accessing G. My intuition is that this technically affects almost any interesting program. 17.6.5.6 [res.on.data.races] also effectively specifies part of this, but doesn't remove the problem.
  2. The current working paper specifies in 17.6.5.6 [res.on.data.races] (paragraph 15) that "C++ standard library functions shall be reentrant subroutines." This replaced earlier text, making reentrancy implementation defined. The earlier text seemed to refer primarily to recursive reentrancy (and possibly in some vague sense interrupts), where the new text is in a thread-related section. If we look solely at the single-threaded situation, this may represent a substantive change in an area that we have not explored sufficiently. Presumably container operations may not be safely recursively reentered if the operations affect the same container. It is unclear to us whether any parts of the library may potentially invoke user oeprations while a library internal lock is held.

Proposed wording changes:

Rewrite 17.6.4.10 [res.on.objects]:

The behavior of a program is undefined if calls to standard library functions from different threads may introduce a data race. The conditions under which this may occur are specified in [res.on.data.races].

The behavior of a program is undefined if calls to standard library functions from different threads:

  1. share access to an object directly or indirectly via their arguments, including this, and
  2. at least one of the arguments accessing a shared object is non-const, and
  3. one call does not happen before the other (1.10).

[ Note: This prohibition against concurrent non-const access means that modifying an object of a standard library type shared between threads without using a locking mechanism may result in a data race. --end note. ]

Insert slight variant of original text before 17.6.5.6 [reentrancy], making it clear that this has nothing to do with threads:

Except where explicitly specified in this standard, it is implementation defined which functions in the C++ standard library may be recursively reentered.

In 17.6.5.6 [res.on.data.races], delete p15 on reentrancy:

C++ standard library functions shall be reentrant subroutines.

In 17.6.5.6 [res.on.data.races], insert after p18:

A C++ standard library function f shall not access objects indirectly accessable via its arguments or via elements of its container arguments, except by invoking functions, required by f's specification, on those container elements.