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:
[
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_ptr
s 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
andweak_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_ptr
s 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]
It means that the code in use_count()
is either lock-free or uses mutexs to lock critical sections. In other words you can call it from threads without worring about race conditions.
I think when we add the previous sentence the intent becomes a little clearer:
For purposes of determining the presence of a data race, member functions shall access and modify only the shared_ptr and weak_ptr objects themselves and not objects they refer to. Changes in use_count() do not reflect modifications that can introduce data races.
So, the last sentence is just emphasizing the same point as the first sentence. For example, if I copy a shared_ptr
, its use-count will be incremented to reflect the fact that the shared_ptr
has been copied--therefore the result from use_count()
will be changed--but this is not allowed to access (and, especially, not allowed to modify) the pointee object, so it can never introduce a data race in use of that pointee object.