Boost logo

Boost :

From: Christophe Meessen (christophe_at_[hidden])
Date: 2002-05-07 04:37:48


Hello,

Chris Eliott said:

> The potential race condition you describe cannot occur
> with the smart_ptr design. They are thread-safe with
> a mutex or atomic operations.
>
> The race condition described is a temporary drop of
> the reference count to 0 while a smart pointer is
> waiting to increment the reference count. Here is why
> that can't happen.
>
> The incrementing occurs when a smart pointer is
> copied, through operator= or copy constructor, etc.
> Throughout this call there must exist another smart
> pointer to the object (specifically the one we are
> copying). In C++ there is no way the source smart
> pointer could go out of scope during the operator= or
> copy constructor for the destination smart pointer.
> Therefore before we get a chance to increment the
> count, it could be decremented, but it could not go
> below 1.

Now I am confused. This is really not obvious. Lets try with a concrete
example:

// declare these two variables as globals.
shared_ptr<MyData> dataPtr1 = new MyData(), dataPtr2;

// Now whe have the following instruction excuted by two threads T1 and
T2.

if( rand()%1 )
        dataPtr2 = dataPtr1;
else
        dataPtr1.reset();

If thread T1 reaches reset() first, but is prehempted just after taking
the mutex protecting it.
Then T2 arrives and excute the assignement that should increment the
counter, but it is of course blocked by the mutex. Thread T1 gets the
cpu again and execute the reset operation. This should decrement the
counter that reaches 0 and has the side effect to destroy the object.
What happens to the counter and mutex ?

In the example you give, the shared_ptr is a local variable so each
thread has its local copy. In my example the shared_ptr is it self the
shared resource.

And the example given is a possible scenario where the counter reaches 0
when a thread tries to access it. Destroying hte object is Ok. But
destroying the counter and the associated mutex would be harmful since
there is at least one thread in the waiting queue.

What could save us in this situation is if the counter and mutex are not
destroyed event thought the counter reaches 0. When T1 leaves the mutex
after destroying the object, T2 can enter and increment the mutex again.
Of course it will get a NULL pointer but at least the shared_ptr is safe
to use as shared resource without a need of protection.

The counter should then be destroyed when the shared_ptr it self is
destroyed.

But if the shared_ptr is a dynamically allocated variable (new) and if
the thread T1 see the counter reaching 0 and destroy the referenced
object which as side effect calls the shared_ptr destructor, we are in
trouble.

What I conclude is that it is impossible to make shared_ptr thread safe
when it comes to delete the object and if the threads access the same
shared_ptr instance. This is the same when accessing the same integer
variable with two threads.

If two threads have their private shared_ptr instance pointing on the
same object it is true that the counter can never reach 0 and things are
thread safe.

User should be warned that shared_ptr is thread safe except the
shared_ptr it self.

-- 
Bien cordialement,
Ch. Meessen



Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk