shared_ptr and thread safety

Dear all, it is already posted a dozen times, but still I do not get it. I understand that if you have a class, the shared_ptr will not make this thread safe. That's fair. Non const operations accessed by different threads should still be guarded (and probably their const versions then also). But what about the shared_ptr itself. From the doc's (http://www.boost.org/libs/smart_ptr/shared_ptr.htm#ThreadSafety) I read that reading and writing to the shared_ptr object (not the pointee) is safe: 'A shared_ptr instance can be "read" (accessed using only const operations) simultaneously by multiple threads. Different shared_ptr instances can be "written to" (accessed using mutable operations such as operator= or reset) simultaneosly by multiple threads (even when these instances are copies, and share the same reference count underneath.)' However then a couple of examples are listed where both operator= and a dtor call are marked as undefined, and this is of course the most use (share a shared pointer over multiple threads and let them decide who finishes last, but this is then illegal?)
From the code I see that the ref counting is implemented through atomic inc's and dec's, and I would therefore expect that it is thread safe after all, since only the thread which makes the ref count 0 will be assigned the job of 'dispose', and the ref counting is thread safe.
who can help this poor programmer... wkr, me

Ok, just looking at the example will bring more light into your questions. First non-safe scenario: // thread A p = p3; // reads p3, writes p // thread B p3.reset(); // writes p3; undefined, simultaneous read/write Either thread B is executed before thread A, then you assign empty shared pointer in thread A (to prior pointer deletion in thread B) OR thread A is executed before thread B, then a valid pointer is assigned in B. Possible scenario here as well: Value of p3 is read, scheduler switches to thread B; deletes pointer owned by p3; switches back to thread A and assignes invalid pointer value to p (deletion of pointee can happen probably twice and it is also undefined what is in memory at the point of assignment and what will be written later on...) Other scenarios are pretty similar. With Kind Regards, Ovanes Markarian On Mon, August 6, 2007 14:51, gast128 wrote:
Dear all,
it is already posted a dozen times, but still I do not get it. I understand that if you have a class, the shared_ptr will not make this thread safe. That's fair. Non const operations accessed by different threads should still be guarded (and probably their const versions then also).
But what about the shared_ptr itself. From the doc's (http://www.boost.org/libs/smart_ptr/shared_ptr.htm#ThreadSafety) I read that reading and writing to the shared_ptr object (not the pointee) is safe:
'A shared_ptr instance can be "read" (accessed using only const operations) simultaneously by multiple threads. Different shared_ptr instances can be "written to" (accessed using mutable operations such as operator= or reset) simultaneosly by multiple threads (even when these instances are copies, and share the same reference count underneath.)'
However then a couple of examples are listed where both operator= and a dtor call are marked as undefined, and this is of course the most use (share a shared pointer over multiple threads and let them decide who finishes last, but this is then illegal?)
From the code I see that the ref counting is implemented through atomic inc's and dec's, and I would therefore expect that it is thread safe after all, since only the thread which makes the ref count 0 will be assigned the job of 'dispose', and the ref counting is thread safe.
who can help this poor programmer...
wkr, me
_______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users

Ovanes Markarian
Ok, just looking at the example will bring more light into your questions.
First non-safe scenario: // thread A p = p3; // reads p3, writes p
// thread B p3.reset(); // writes p3; undefined, simultaneous read/write
Either thread B is executed before thread A, then you assign empty shared
prior pointer deletion in thread B) OR thread A is executed before thread B,
pointer in thread A (to then a valid pointer
is assigned in B. Possible scenario here as well: Value of p3 is read, scheduler switches to thread B; deletes pointer owned by p3; switches back to thread A and assignes invalid pointer value to p (deletion of pointee can happen probably twice and it is also undefined what is in memory at the point of assignment and what will be written later on...) ... Ovanes Markarian
This should mean that shared_ptr is not at all thread safe, as in above race condition. I am not sure if this contradicts the quote 'shared_ptr objects offer the same level of thread safety as built-in types', it at least does not address the thread safety of the unique reference counting feature (which has no analog for 'normal' pointers). The atomic increment and decrement ref counting then only work for a shared const shared pointer: void g(boost::shared_ptr<const B> ptr) { //... } void f() { boost::shared_ptr<const B> ptr(new B); //give ptr to threads boost::thread thrd1(boost::bind(&g, ptr)); boost::thread thrd2(boost::bind(&g, ptr)); //fire and forget ptr.reset(); } However they finally end up in a dtor call which according to the documentation is an legal or illegal write operation.

On Mon, August 6, 2007 16:56, gast128 wrote:
This should mean that shared_ptr is not at all thread safe, as in above race condition. I am not sure if this contradicts the quote 'shared_ptr objects offer the same level of thread safety as built-in types', it at least does not address the thread safety of the unique reference counting feature (which has no analog for 'normal' pointers).
The atomic increment and decrement ref counting then only work for a shared const shared pointer:
void g(boost::shared_ptr<const B> ptr) { //... }
void f() { boost::shared_ptr<const B> ptr(new B);
//give ptr to threads boost::thread thrd1(boost::bind(&g, ptr)); boost::thread thrd2(boost::bind(&g, ptr));
//fire and forget ptr.reset(); }
However they finally end up in a dtor call which according to the documentation is an legal or illegal write operation.
Well, since the standard requires all parameters to function be evaluated before the function call, your code is safe. So before the thread ctor is called, all binds are evaluated and have already a copied instance of shared_ptr instance, is not it so? And for sure you can afterwards safely call reset, or just discard the shared pointer which will decrement the shared counter. With Kind Regards, Ovanes Markarian

gast128 wrote:
The atomic increment and decrement ref counting then only work for a shared const shared pointer:
No, the presence or absence of 'const' do not affect the thread safety of a piece of code.
void g(boost::shared_ptr<const B> ptr) { //... }
void f() { boost::shared_ptr<const B> ptr(new B);
//give ptr to threads boost::thread thrd1(boost::bind(&g, ptr)); boost::thread thrd2(boost::bind(&g, ptr));
//fire and forget ptr.reset(); }
This example is OK. You have three distinct shared_ptr variables, all named 'ptr' (one per thread). Despite them having the same name, they are still different objects, not shared among threads, so you can assign/reset/destroy them at will.

On Mon, August 6, 2007 17:11, Peter Dimov wrote:
gast128 wrote:
The atomic increment and decrement ref counting then only work for a shared const shared pointer:
No, the presence or absence of 'const' do not affect the thread safety of a piece of code.
void g(boost::shared_ptr<const B> ptr) { //... }
void f() { boost::shared_ptr<const B> ptr(new B);
//give ptr to threads boost::thread thrd1(boost::bind(&g, ptr)); boost::thread thrd2(boost::bind(&g, ptr));
//fire and forget ptr.reset(); }
This example is OK. You have three distinct shared_ptr variables, all named 'ptr' (one per thread). Despite them having the same name, they are still different objects, not shared among threads, so you can assign/reset/destroy them at will.
Yes, just in addition, your code would be broken, if g would expect a shared_ptr by reference, i.e. void g(boost::shared_ptr<const B>& ptr). Then all your threads would own the same shared_ptr instance and the thread in which runs the function f would reset the pointer which might be used by thrd1 or thrd2 and that might cause the scenario from the docs example. With Kind Regards, Ovanes Markarian

Ovanes Markarian wrote:
void g(boost::shared_ptr<const B> ptr) { //... }
void f() { boost::shared_ptr<const B> ptr(new B);
//give ptr to threads boost::thread thrd1(boost::bind(&g, ptr)); boost::thread thrd2(boost::bind(&g, ptr));
//fire and forget ptr.reset(); } This example is OK. You have three distinct shared_ptr variables, all named 'ptr' (one per thread). Despite them having the same name, they are still different objects, not shared among threads, so you can assign/reset/destroy them at will.
Yes, just in addition, your code would be broken, if g would expect a shared_ptr by reference, i.e. void g(boost::shared_ptr<const B>& ptr). Then all your threads would own the same shared_ptr instance and the thread in which runs the function f would reset the pointer which might be used by thrd1 or thrd2 and that might cause the scenario from the docs example.
I wouldn't think so in this case, since boost::bind makes a copy of the shared_ptr before it gets passed to g(). If the code used boost::bind(&g, boost::ref(ptr)), then there would be a race condition. -- Jon Biggar Floorboard Software jon@floorboard.com jon@biggar.org ra

On Mon, August 6, 2007 18:08, Jonathan Biggar wrote:
Ovanes Markarian wrote:
void g(boost::shared_ptr<const B> ptr) { //... }
void f() { boost::shared_ptr<const B> ptr(new B);
//give ptr to threads boost::thread thrd1(boost::bind(&g, ptr)); boost::thread thrd2(boost::bind(&g, ptr));
//fire and forget ptr.reset(); } This example is OK. You have three distinct shared_ptr variables, all named 'ptr' (one per thread). Despite them having the same name, they are still different objects, not shared among threads, so you can assign/reset/destroy them at will.
Yes, just in addition, your code would be broken, if g would expect a shared_ptr by reference, i.e. void g(boost::shared_ptr<const B>& ptr). Then all your threads would own the same shared_ptr instance and the thread in which runs the function f would reset the pointer which might be used by thrd1 or thrd2 and that might cause the scenario from the docs example.
I wouldn't think so in this case, since boost::bind makes a copy of the shared_ptr before it gets passed to g(). If the code used boost::bind(&g, boost::ref(ptr)), then there would be a race condition.
Yes, sorry! You are right... With Kind Regards, Ovanes Markarian

Ok thx all. Maybe it's after all in the documentation. One shared_ptr can be shared if there is read access only. Multiple instances of a shared_ptr pointing to the same pointee can be written to simultanesly. Something similar is to be found in the documentation. Wkr.

on Mon Aug 06 2007, gast128
Ovanes Markarian
writes: Ok, just looking at the example will bring more light into your questions.
First non-safe scenario: // thread A p = p3; // reads p3, writes p
// thread B p3.reset(); // writes p3; undefined, simultaneous read/write
Either thread B is executed before thread A, then you assign empty shared pointer in thread A (to prior pointer deletion in thread B) OR thread A is executed before thread B, then a valid pointer is assigned in B. Possible scenario here as well: Value of p3 is read, scheduler switches to thread B; deletes pointer owned by p3; switches back to thread A and assignes invalid pointer value to p (deletion of pointee can happen probably twice and it is also undefined what is in memory at the point of assignment and what will be written later on...) ... Ovanes Markarian
This should mean that shared_ptr is not at all thread safe, as in above race condition.
No, it's exactly "as threadsafe as int." Believe it or not, it's possible for things to be even less threadsafe when shared data (e.g. a reference count) is involved. For example, with p1 and p2 both shared_ptr<T>, pre-thread: p1 = p2 thread A: p1.reset() thread B: p2.reset() is safe. That sequence would not be safe with a naive reference-counted pointer that was totally unaware of thread safety. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com The Astoria Seminar ==> http://www.astoriaseminar.com

David Abrahams
No, it's exactly "as threadsafe as int."
snip because of 'There's much more quoted text in your article than new. Prune quoted stuff.'
That sequence would not be safe with a naive reference-counted pointer that was totally unaware of thread safety.
'Inside COM' from Dale Rogerson suggests to make ref counting atomic (i.e. through InterLockedIncrement).

On Tue, August 7, 2007 19:09, gast128 wrote:
David Abrahams
writes: No, it's exactly "as threadsafe as int."
snip because of 'There's much more quoted text in your article than new. Prune quoted stuff.'
That sequence would not be safe with a naive reference-counted pointer that was totally unaware of thread safety.
'Inside COM' from Dale Rogerson suggests to make ref counting atomic (i.e. through InterLockedIncrement).
_______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
shared_ptr ref counting is implemented the same way under windows (at least the version I have looked at a year ago). With Kind Regards, Ovanes Markarian

Ovanes Markarian wrote:
Ok, just looking at the example will bring more light into your questions.
First non-safe scenario: // thread A p = p3; // reads p3, writes p
// thread B p3.reset(); // writes p3; undefined, simultaneous read/write
Either thread B is executed before thread A, then you assign empty shared pointer in thread A (to prior pointer deletion in thread B) OR thread A is executed before thread B, then a valid pointer is assigned in B. Possible scenario here as well: Value of p3 is read, scheduler switches to thread B; deletes pointer owned by p3; switches back to thread A and assignes invalid pointer value to p (deletion of pointee can happen probably twice and it is also undefined what is in memory at the point of assignment and what will be written later on...)
Hi, all! I understand, that thread A could get an invalid pointer. But what if thread B did not reset p3, but rather reassign it? Assuming that I don't care if the reader gets the new pointee or the old one. I just wan't to assure that all dynamic objects would be deleted properly. // pre-thread boost::shared_ptr<const A> ptr(new A); // reader thread A boost::shared_ptr<const A> p1=ptr; // get the latest value p1->getSomeValue(); p1->getSomeOtherValue(); p1.reset(); // don't need that anymore // reader thread B boost::shared_ptr<const A> p2=ptr; // get the latest value p2->getSomeValue(); p2->getSomeOtherValue(); p2.reset(); // don't need that anymore // writer thread ptr.reset(new A); // look Ma! I've brought you something new! So is the substitution thread-safe? I think it could be, if the steps necessary to do that were taken in the following order: - setting new object's counter to 1 (atomicity irrelevant at this point) - switching internal pointer to point to the new object (atomic) - from now on, reader threads would get the new object - decrementing old object's counter (atomic) - the old object gets released and eventually deleted If it's not that way, would this template class help in the abovementioned use-scenario: template <class T> class SharedObject { public: typedef boost::shared_ptr<T> SharedPtr; SharedObject() : sharedPtr(new const SharedPtr(new T)) {} ~SharedObject() { delete sharedPtr; } SharedPtr get() { return *sharedPtr; } void set(T *httpLogFilter) { const SharedPtr *newSharedPtr=new SharedPtr(sharedObject); const SharedPtr *oldSharedPtr=sharedPtr; // there sould be a write membar here probably sharedPtr=newSharedPtr; // atomic substitution delete oldSharedPtr; } private: const SharedPtr *sharedPtr; }; PS Would any valid compiler rearrange isntructions is set() ? Thanks in advance for your help! -- View this message in context: http://www.nabble.com/shared_ptr-and-thread-safety-tf4223939.html#a12224610 Sent from the Boost - Users mailing list archive at Nabble.com.

Emil Wojak wrote:
// pre-thread boost::shared_ptr<const A> ptr(new A);
// reader thread A boost::shared_ptr<const A> p1=ptr; // get the latest value p1->getSomeValue(); p1->getSomeOtherValue(); p1.reset(); // don't need that anymore
// reader thread B boost::shared_ptr<const A> p2=ptr; // get the latest value p2->getSomeValue(); p2->getSomeOtherValue(); p2.reset(); // don't need that anymore
// writer thread ptr.reset(new A); // look Ma! I've brought you something new!
So is the substitution thread-safe? I think it could be, if the steps necessary to do that were taken in the following order: - setting new object's counter to 1 (atomicity irrelevant at this point) - switching internal pointer to point to the new object (atomic) - from now on, reader threads would get the new object - decrementing old object's counter (atomic) - the old object gets released and eventually deleted
No, this is not safe. 1. reader A reads the pointer value 2. writer thread sets new pointer value 3. writer thread deletes old object and count 4. reader A tries to increment the deleted count, bad things happen The proposed additions in http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2007/n2297.html#atomic aim to support this use case, but I haven't implemented them yet.
participants (6)
-
David Abrahams
-
Emil Wojak
-
gast128
-
Jonathan Biggar
-
Ovanes Markarian
-
Peter Dimov