[patch] Make std::tr1::shared_ptr thread-safe.
Peter Dimov
pdimov@mmltd.net
Wed Mar 30 21:33:00 GMT 2005
Paolo Carlini wrote:
> Gabriel Dos Reis wrote:
>
>>> Why are you using the term "broken"?!? I don't think Alexander and
>>> Peter used the term "broken" anywhere. It's just a matter of
>>> efficiency and, as far as I'm concerned, not checking in invasive
>>> changes so close to the release. Do you disagree with that?!?
>>
>> if it is appropriate for 4.0.1, it ought to be appropriate for 4.0.0.
>>
>>
> Ok, if you think so, and the other maintainers agree, I volunteer to
> help Jonathan preparing a complete patch in time for 4.0.0, which
> would include (to summarize): configury bits for old platforms (386
> and Sparc), resurrection of compare_and_swap, fall-back to Alexander
> and Peter proposal.
I think I need to clarify things a bit.
Whether add_ref_lock uses a CAS (Alexander Terekhov's algorithm) or a mutex
lock (Tyson Whitehead's modification) is one issue. Both are equally
correct. The mutex lock is taken only when a shared_ptr is constructed from
a weak_ptr and does not affect the rest of the implementation.
A separate issue is that there needs to exist proper memory ordering between
some of the operations, for example, destroy() needs to happen after
dispose() (but there are other visibility issues, some of them more subtle.)
This can be fixed in one of the following ways:
1. Empty lock/unlock regions on the same mutex as outlined in my and
Alexander's posts.
2. Full bidirectional memory barriers at the same two places.
3. Making __exchange_and_add offer the following memory synchronization
guarantees:
- acquire memory semantics when the result is not zero;
- release memory semantics when the result is zero
or introducing a separate __atomic_decrement primitive with these
properties.
I recommend (1), if Tyson Whitehead's algorithm is used and there is already
a mutex handy, or (2), which would require the addition of a
_GLIBCXX_MEMORY_BARRIER primitive.
Alexander would recommend (3), which also fixes two other problems. I think
that this is not feasible for 4.0.0, but the decision is up to you. The main
problem with msync guarantees is that they aren't testable.
The next issue on our list is that reference-counted mutable synchronized
objects would need an empty lock/unlock region at the start of the
destructor if (3) is not chosen. I consider this unfortunate but acceptable.
The final issue is that the reference counted objects in libstdc++
(std::string, std::locale::facet) may also be broken *unless*
__exchange_and_add has some memory visibility guarantees that I don't
understand, but Alexander does. (It needs to establish ordering of prior
loads WRT following stores IIUC). This will be fixed by (3) and may or may
not be a problem in practice; I'm not an expert in this area and can't
review the current __exchange_and_add implementations. In any event,
shared_ptr with the above fixes applied would be no more broken than the
rest of the library.
If that's going to be of any help, I'm planning to use Whitehead/(1) in
Boost on libstdc++ pre-4, and whatever you decide to use for 4 and up.
Thank you for reading this far. ;-)
More information about the Libstdc++
mailing list