Boost shared pointers, PThreads and void* casts
I haven't been able to find any clear explanation about this, so here it goes: I'm coding a multithreaded program using pthreads. The general behaviour is this: -A function starts. -The function creates a shared pointer "OBJ". -The function creates a Thread, passing "OBJ" as the parameter (having to cast it as void*). -The thread starts, getting a (void*) param which is casted back to "OBJ" (shared pointer type). -The function ends, and therefore its "OBJ" goes out of scope. -The thread" ends, so its "OBJ" goes out of scope too. (of course, the thread could at times end before the function instead) I'm afraid that the shared pointer does not behave well due to those nasty void* casts... maybe trying to delete twice the object. What's the proper way of coding this? Weak pointers maybe? Thanks a lot for any help. -- Saludos, Bruno González _______________________________________________ Msn/Jabber: stenyak AT gmail.com ICQ: 153709484 http://www.stenyak.com
boost-users-bounces@lists.boost.org <> wrote:
I haven't been able to find any clear explanation about this, so here it goes:
I'm coding a multithreaded program using pthreads. The general behaviour is this:
-A function starts. -The function creates a shared pointer "OBJ". -The function creates a Thread, passing "OBJ" as the parameter (having to cast it as void*). -The thread starts, getting a (void*) param which is casted back to "OBJ" (shared pointer type). -The function ends, and therefore its "OBJ" goes out of scope. -The thread" ends, so its "OBJ" goes out of scope too. (of course, the thread could at times end before the function instead)
I'm afraid that the shared pointer does not behave well due to those nasty void* casts... maybe trying to delete twice the object. What's the proper way of coding this? Weak pointers maybe?
Thanks a lot for any help.
The first problem I see is that a boost::shared_ptr is not the same size
as a void*. The believe the cast would slice off the pointer to the
reference count. I would try something like the following (untested):
//Notice that we are passing the shared pointer by value. I
//consider this important because we are about to abuse it.
void Spawn_thread (boost::shared_ptr <whatever> value)
{
pthread_t thread;
pthread_attr_t attr;
//Notice that I'm passing a raw pointer to the shared pointer.
if (!pthread_create (&thread, &attr, &Thread_main, &value))
{
//Wait for the new thread to copy its parameter into a
//local variable
Wait_for_signal();
}
else
{
//Error handling
}
}
void *Thread_main (void *arg)
{
//Dereference the argument (to get back the shared pointer),
copy
//it into a local shared pointer and allow the main thread to
proceed
boost::shared_ptr <whatever> parameter = *reinterpret_cast
On Tue, Dec 18, 2007 at 10:28:37AM -0500, Andrew Holden wrote:
boost-users-bounces@lists.boost.org <> wrote:
I haven't been able to find any clear explanation about this, so here it goes:
I'm coding a multithreaded program using pthreads. The general behaviour is this:
-A function starts. -The function creates a shared pointer "OBJ". -The function creates a Thread, passing "OBJ" as the parameter (having to cast it as void*). -The thread starts, getting a (void*) param which is casted back to "OBJ" (shared pointer type). -The function ends, and therefore its "OBJ" goes out of scope. -The thread" ends, so its "OBJ" goes out of scope too. (of course, the thread could at times end before the function instead)
I'm afraid that the shared pointer does not behave well due to those nasty void* casts... maybe trying to delete twice the object. What's the proper way of coding this? Weak pointers maybe?
Thanks a lot for any help.
The first problem I see is that a boost::shared_ptr is not the same size as a void*. The believe the cast would slice off the pointer to the reference count. I would try something like the following (untested):
He might just allocate the shared_ptr on the heap (the dynamic memory allocation
will be anyway cheaper than thread creation):
void *thread(void *ptr)
{
shared_ptr<X> *_p = static_cast
Zeljko Vrba [zvrba@ifi.uio.no] wrote:
He might just allocate the shared_ptr on the heap (the dynamic memory allocation will be anyway cheaper than thread creation):
void *thread(void *ptr) { shared_ptr<X> *_p = static_cast
(ptr); shared_ptr<X> p = *_p; delete _p; ... } void creator() { ... pthread_create(&thrid, 0, thread, new shared_ptr<X>(orig_ptr)); ... }
No need for any additional synchronization.
Good point. And certainly much simpler than my solution.
Shared pointer question:
func(...)
{
shared_pointer<foo> p;
p = list
Elli Barasch wrote:
Shared pointer question: [snip odd example]
Will this cause a memory leak? Do I need to do a delete in between, or does the act of reassignment cause the prior object to be dereferenced?
A second assignment to a given shared_ptr should not cause a leak. You should avoid an explicit delete. If p is a shared_ptr pointing to a heap object, then assigning a new value to p will decrement the previously-referenced object's refcount, which may cause the heap object to be deleted. This document: http://boost.org/libs/smart_ptr/shared_ptr.htm#Members says this under 'assignment': shared_ptr & operator=(shared_ptr const & r); // never throws template<class Y> shared_ptr & operator=(shared_ptr<Y> const & r); // never throws template<class Y> shared_ptr & operator=(std::auto_ptr<Y> & r); Effects: Equivalent to shared_ptr(r).swap(*this). Returns: *this. Notes: The use count updates caused by the temporary object construction and destruction are not considered observable side effects, and the implementation is free to meet the effects (and the implied guarantees) via different means, without creating a temporary. But the important point is that the assignment must behave as though a shared_ptr holding p's prior value has been deleted.
That's the answer I was looking for. Sorry for my mangled example, but you had the idea... Nat Goodspeed wrote:
Elli Barasch wrote:
Shared pointer question:
[snip odd example]
Will this cause a memory leak? Do I need to do a delete in between, or does the act of reassignment cause the prior object to be dereferenced?
A second assignment to a given shared_ptr should not cause a leak. You should avoid an explicit delete.
If p is a shared_ptr pointing to a heap object, then assigning a new value to p will decrement the previously-referenced object's refcount, which may cause the heap object to be deleted.
This document: http://boost.org/libs/smart_ptr/shared_ptr.htm#Members says this under 'assignment':
shared_ptr & operator=(shared_ptr const & r); // never throws template<class Y> shared_ptr & operator=(shared_ptr<Y> const & r); // never throws template<class Y> shared_ptr & operator=(std::auto_ptr<Y> & r);
Effects: Equivalent to shared_ptr(r).swap(*this).
Returns: *this.
Notes: The use count updates caused by the temporary object construction and destruction are not considered observable side effects, and the implementation is free to meet the effects (and the implied guarantees) via different means, without creating a temporary.
But the important point is that the assignment must behave as though a shared_ptr holding p's prior value has been deleted. _______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
Thank you two for the help.
On 12/18/07, Zeljko Vrba
On Tue, Dec 18, 2007 at 10:28:37AM -0500, Andrew Holden wrote: He might just allocate the shared_ptr on the heap (the dynamic memory allocation will be anyway cheaper than thread creation):
void *thread(void *ptr) { shared_ptr<X> *_p = static_cast
(ptr); shared_ptr<X> p = *_p; delete _p; ... } void creator() { ... pthread_create(&thrid, 0, thread, new shared_ptr<X>(orig_ptr)); ... }
Ok. This is how my code structure looks:
----------------------------
typedef shared_ptr<MyClass> pMyClass;
void* MyClass::actionThread(void *objectPointer)
{
pMyClass * _obj = static_cast
On Wed, Dec 19, 2007 at 01:21:10PM +0100, STenyaK (Bruno Gonzalez) wrote:
Ok. This is how my code structure looks: ---------------------------- typedef shared_ptr<MyClass> pMyClass; void* MyClass::actionThread(void *objectPointer) { pMyClass * _obj = static_cast
(objectPointer); pMyClass obj = *_obj; obj->action(false); delete _obj; }
It is _crucial_ to call delete BEFORE any other action in the code, or else you may suffer memory leak (consider e.g. that action throws an exception). Otherwise, your interpretation seems correct.
Do i need to use enable_shared_from_this? Or do i have to avoid using "this" (orig_ptr in your code) somehow maybe?
Uf, I don't know. Read this: http://www.boost.org/libs/smart_ptr/sp_techniques.html it mentiones several instances of the "shared_ptr from this" problem.
STenyaK (Bruno Gonzalez) [stenyak@gmail.com] wrote:
Ok. This is how my code structure looks: ---------------------------- typedef shared_ptr<MyClass> pMyClass; void* MyClass::actionThread(void *objectPointer) { pMyClass * _obj = static_cast
(objectPointer); pMyClass obj = *_obj; obj->action(false); delete _obj; } void MyClass::action(bool threaded) { if (threaded) { pthread_t tid; pthread_create(&tid, 0, actionThread, new pMyClass(this)) } else { //TODO: actually perform the action } } ---------------------------- When calling an instance->action(false) it works correctly. When calling instance->action(true) it sometimes segfaults. So i'm not sure i interpreted your code sample correctly. Do i need to use enable_shared_from_this? Or do i have to avoid using "this" (orig_ptr in your code) somehow maybe?
Thanks in advance!
I would stongly recommend shared_from_this. You code, as written, will
guaranteee that actionThread will destroy the MyClass as soon
asactionThread finishes, even if the main thread still expects it to
exist. This would result in a double-free if the main thread has its
own shared pointer to the object.
Actually, the fact that this is a member function opens another
possibility. First, derive MyClass from enable_shared_from_this
<MyClass>, then you can implement the functions like this:
----------------------------
typedef shared_ptr<MyClass> pMyClass;
void* MyClass::actionThread(void *objectPointer)
{
MyClass * _obj = static_cast
On 12/19/07, Andrew Holden
STenyaK (Bruno Gonzalez) [stenyak@gmail.com] wrote: typedef shared_ptr<MyClass> pMyClass; void* MyClass::actionThread(void *objectPointer) { MyClass * _obj = static_cast
(objectPointer); pMyClass obj = _obj->shared_from_this(); obj->action(false); } void MyClass::action(bool threaded) { if (threaded) { pthread_t tid; pthread_create(&tid, 0, actionThread, this) } else { //TODO: actually perform the action } } ----------------------------
This works because shared_from_this is specifically designed to allow member functions of a class to recover the shared pointer to the object.
Just in case... actionThread is a static method of the class, since otherwise C++ won't allow me to get a pointer of it (and i dislike global functions). Does this fact have any implications in the last proposed code sample? Thanks again to everybody. -- Saludos, Bruno González _______________________________________________ Msn/Jabber: stenyak AT gmail.com ICQ: 153709484 http://www.stenyak.com
STenyaK (Bruno Gonzalez) [stenyak@gmail.com] wrote:
On 12/19/07, Andrew Holden
wrote: typedef shared_ptr<MyClass> pMyClass; void* MyClass::actionThread(void *objectPointer) { MyClass * _obj = static_cast
(objectPointer); pMyClass obj = _obj->shared_from_this(); obj->action(false); } void MyClass::action(bool threaded) { if (threaded) { pthread_t tid; pthread_create(&tid, 0, actionThread, this) } else { //TODO: actually perform the action } } ----------------------------
This works because shared_from_this is specifically designed to allow member functions of a class to recover the shared pointer to the object.
Just in case... actionThread is a static method of the class, since otherwise C++ won't allow me to get a pointer of it (and i dislike global functions). Does this fact have any implications in the last proposed code sample?
Not really. actionThread is still a member function of MyClass, which means it has full access to MyClass's members (as long as it has access to a MyClass object somehow), including private and protected members, and protected members from base classes. _obj is a valid pointer to a MyClass, so there is nothing wrong with actionThread calling _obj's shared_from_this function.
On 12/19/07, Andrew Holden
STenyaK (Bruno Gonzalez) [stenyak@gmail.com] wrote:
On 12/19/07, Andrew Holden
wrote: typedef shared_ptr<MyClass> pMyClass; void* MyClass::actionThread(void *objectPointer) { MyClass * _obj = static_cast
(objectPointer); pMyClass obj = _obj->shared_from_this(); obj->action(false); } void MyClass::action(bool threaded) { if (threaded) { pthread_t tid; pthread_create(&tid, 0, actionThread, this) sleep(1); } else { //TODO: actually perform the action } } ----------------------------
I'm still getting crashes. By adding that sleep sentence i seem to prevent them (or most of them). So i guess i have to add a wait right there, in order to make sure that _obj still exists when trying to get its shared_from_this? -- Saludos, Bruno González _______________________________________________ Msn/Jabber: stenyak AT gmail.com ICQ: 153709484 http://www.stenyak.com
Andrew Holden [aholden@charteroaksystems.com] wrote:
STenyaK (Bruno Gonzalez) [stenyak@gmail.com] wrote:
Ok. This is how my code structure looks: ---------------------------- typedef shared_ptr<MyClass> pMyClass; void* MyClass::actionThread(void *objectPointer) { pMyClass * _obj = static_cast
(objectPointer); pMyClass obj = *_obj; obj->action(false); delete _obj; } void MyClass::action(bool threaded) { if (threaded) { pthread_t tid; pthread_create(&tid, 0, actionThread, new pMyClass(this)) } else { //TODO: actually perform the action } } ---------------------------- ---------------------------- typedef shared_ptr<MyClass> pMyClass; void* MyClass::actionThread(void *objectPointer) { MyClass * _obj = static_cast
(objectPointer); pMyClass obj = _obj->shared_from_this(); obj->action(false); } void MyClass::action(bool threaded) { if (threaded) { pthread_t tid; pthread_create(&tid, 0, actionThread, this) } else { //TODO: actually perform the action } } ----------------------------
I just realized you should ignore this implementation because it
contains a race condition (main thread may delete the object before
actionThread can call shared_from_this. Here is a tweak for your
version that uses shared_from_this:
----------------------------
typedef shared_ptr<MyClass> pMyClass;
void* MyClass::actionThread(void *objectPointer) {
pMyClass * _obj = static_cast
participants (5)
-
Andrew Holden
-
Elli Barasch
-
Nat Goodspeed
-
STenyaK (Bruno Gonzalez)
-
Zeljko Vrba