This is the mail archive of the
libstdc++@gcc.gnu.org
mailing list for the libstdc++ project.
Re: [patch] Make std::tr1::shared_ptr thread-safe.
- From: Alexander Terekhov <alexander dot terekhov at gmail dot com>
- To: Peter Dimov <pdimov at mmltd dot net>
- Cc: libstdc++ at gcc dot gnu dot org
- Date: Wed, 30 Mar 2005 17:28:49 +0200
- Subject: Re: [patch] Make std::tr1::shared_ptr thread-safe.
- References: <OF7BD94D70.2C214BBE-ONC1256FD4.0051B276-C1256FD4.0051AA26@de.ibm.com>
- Reply-to: Alexander Terekhov <alexander dot terekhov at gmail dot com>
"Peter Dimov" <pdimov@mmltd.net> on 03/30/2005 04:36:54 PM:
>
> Alexander Terekhov wrote:
> > [... __release/acquire_memory_barrier() ...]
> >
> >> The only reliable implementation of these barriers that I see
> >> is an empty pthread_mutex_lock/pthread_mutex_unlock pair.
> >
> > Nope. That won't work. [...]
>
> Update:
>
> I think that we've reached the conclusion that the following
> implementation:
>
> void release() // nothrow
> {
> if (__gnu_cxx::__exchange_and_add(&_M_use_count, -1) == 1)
> {
> pthread_mutex_lock( &_M_mutex );
> dispose();
> pthread_mutex_unlock( &_M_mutex );
> weak_release();
> }
> }
void release() // nothrow
{
if (__gnu_cxx::__exchange_and_add(&_M_use_count, -1) == 1)
{
dispose();
pthread_mutex_lock( &_M_mutex );
pthread_mutex_unlock( &_M_mutex );
weak_release();
}
}
would also work. The key here is...
>
> void weak_release() // nothrow
> {
> if (__gnu_cxx::__exchange_and_add(&_M_weak_count, -1) == 1)
> {
> pthread_mutex_lock( &_M_mutex );
> pthread_mutex_unlock( &_M_mutex );
> destroy();
> }
> }
to have the same lock on both sides. Some "thread-specific" locks to
"emulate" bidirectional fences with minimum contention won't work.
>
> will work *provided that __exchange_and_add imposes at least the ordering
> that is required for reference-counted immutable objects to work*.
That's msync::slb (relaxed msync::rel not affecting sinking of preceding
stores) when result > 1 and msync::cchsb (relaxed msync::acq [cc stands
control condition hint] not affecting hoisting of subsequent loads) when
result == 0.
No _M_mutex above is needed with more constrained version of
__exchange_and_add() providing msync::rel when result > 1 and msync::ccacq
when result == 0. Such semantics will also support basic thread-safety of
shared_ptr<> for mutable managed objects without unpleasant requirement
for client provided synchronizing deleters.
>
> The last point is important, because it affects the other uses of
> __exchange_and_add in libstdc++.
>
> Alexander has identified the following problematic case:
>
> // thread A
>
> read *p1
> p1.drop_reference()
>
> // thread B
>
> p2.drop_reference(); // destroys *p1
>
> If the __exchange_and_adds hidden in drop_reference do not establish
> ordering, it is possible for *p1 to be destroyed (and the storage
> invalidated, zeroed or reused) by thread B before the read in thread A.
Yep.
regards,
alexander.