Search code examples
c++c++11shared-ptrlanguage-lawyerc++14

What is the meaning of this piece of Standardese about shared_ptr's use_count()?


While trying to wrap my head around the problem shown in this question I found myself stuck on the following sentence from [util.smartptr.shared]/4:

[...] Changes in use_count() do not reflect modifications that can introduce data races.

I don't understand how I should read that, and what conclusions shall I draw. Here are a few interpretations:

  • Invoking use_count() does not introduce data races (but this should be guaranteed by the const-ness of that function alone, together with the corresponding library-wide guarantees)
  • The value returned by use_count() is not influenced by ("does not reflect"?) the outcome of operations that require atomicity or synchronization (but what are these relevant operations?)
  • use_count() is executed atomically, but without preventing reordering by the CPU or the compiler (i.e. without sequential consistency, but then why not mentioning the particular model?)

To me, none of the above seems to follow from that sentence, and I am at loss trying to interpret it.


Solution

  • The current wording derives from library issue 896, which also addressed the question of whether shared_ptr should be thread safe in the sense that distinct shared_ptrs owning the same object can be accessed (in particular, copied and destructed) from distinct threads simultaneously. The conclusion of that discussion was that shared_ptr should be thread-safe; the mechanism to guarantee this was to pretend that shared_ptr member functions only access the shared_ptr object itself and not its on-heap control block:

    For purposes of determining the presence of a data race, member functions access and modify only the shared_ptr and weak_ptr objects themselves and not objects they refer to.

    Here "objects they refer to" means the control block.

    However this raises an issue; if we pretend that distinct shared_ptrs owning the same object don't access the control block, then surely use_count() cannot change? This is patched by making use_count() a magic function that produces a result out of thin air:

    Changes in use_count() do not reflect modifications that can introduce data races.

    That is, use_count() can change from one call to the next, but that does not mean that a data race (or potential data race) has occurred. This is perhaps clearer from the previous wording of that sentence:

    [Note: This is true in spite of that fact that such functions often modify use_count() --end note]