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:
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)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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With