How can I have an object-specific and thread-specific pointer?
Hi,
My class contains a member var that is a little buffer used as a cache.
Instances of this class are used concurrently (referenced through a
shared_ptr).
I would like to switch my buffer to a thread_specific_ptr, but there are
two big problems with using thread_specific_ptr as a member var:
1. There is a memory leak when my object goes away, because by design only
one of the pointers gets cleaned up (the one for the thread that happens to
delete the object).
2. There is undefined behavior when a new object gets created that happens
to be allocated at the same address as a previously freed one. get( )
could return a pointer to a buffer that goes with an old (now freed) object.
I try not to question the wisdom of the thread_specific_ptr authors, but
given these two issues, especially #2, I have to conclude that
thread_specific_ptr is only suitable for statics / globals. It is wholly
unfit to be used as a member variable (except in a singleton class I guess).
The next best thing I can think of for my class is to just have a
std::map
Anyone? Should I try boost-devel?
On Sat, Nov 12, 2011 at 4:00 PM, Phillip Hellewell
Hi,
My class contains a member var that is a little buffer used as a cache. Instances of this class are used concurrently (referenced through a shared_ptr).
I would like to switch my buffer to a thread_specific_ptr, but there are two big problems with using thread_specific_ptr as a member var: 1. There is a memory leak when my object goes away, because by design only one of the pointers gets cleaned up (the one for the thread that happens to delete the object). 2. There is undefined behavior when a new object gets created that happens to be allocated at the same address as a previously freed one. get( ) could return a pointer to a buffer that goes with an old (now freed) object.
I try not to question the wisdom of the thread_specific_ptr authors, but given these two issues, especially #2, I have to conclude that thread_specific_ptr is only suitable for statics / globals. It is wholly unfit to be used as a member variable (except in a singleton class I guess).
The next best thing I can think of for my class is to just have a std::map
, with synchronized access of course, and I clean up all the pointers in the map in the DTOR. That would work just great, except I really do need the pointers to also get cleaned up (and removed from my map) when the corresponding threads go away. Some of my objects can live a very long time, whilst threads get created and destroyed off and on, so without this functionality the map could grow indefinitely. So I'm stuck. I can't use thread_specific_ptr because it won't clean up everything when my object goes away, and I can't use a map because it won't clean up anything when a thread goes away. Is there an easy solution to this?
If there were some way for me to hook up a callback directly to the thread cleanup function, I could add a callback to remove it from my map when the thread goes away. But I'd also need the ability to remove that callback in my DTOR. Does boost provide any way to do this?
I can't be the first person to want a thread-specific, object-specific pointer, can I?
Phillip
On 11/16/2011 11:00 PM, Phillip Hellewell wrote:
Anyone? Should I try boost-devel?
On Sat, Nov 12, 2011 at 4:00 PM, Phillip Hellewell
mailto:sshock@gmail.com> wrote: Hi,
My class contains a member var that is a little buffer used as a cache. Instances of this class are used concurrently (referenced through a shared_ptr).
I would like to switch my buffer to a thread_specific_ptr, but there are two big problems with using thread_specific_ptr as a member var: 1. There is a memory leak when my object goes away, because by design only one of the pointers gets cleaned up (the one for the thread that happens to delete the object). 2. There is undefined behavior when a new object gets created that happens to be allocated at the same address as a previously freed one. get( ) could return a pointer to a buffer that goes with an old (now freed) object.
I try not to question the wisdom of the thread_specific_ptr authors, but given these two issues, especially #2, I have to conclude that thread_specific_ptr is only suitable for statics / globals. It is wholly unfit to be used as a member variable (except in a singleton class I guess).
Hi Philip, I haven't used thread_specific_ptr. But I do feel like it was designed to handle cases like dealing with "errno" values, so each thread can handle them separately. You use a shared pointer to handle the objects lifetime. If all the threads that created the thread specific pointer values get destroyed before your object, the object is in good shape. But if the object gets destroyed before all the threads, then you're expecting shared_ptr to do the cleanup for you (as in every time the ref count goes down you want the corresponding thread sp. obj to be destroyed). May be you can override one of shared_ptr's members, when ref count goes down, and reset the corresponding thread sp. obj's pointer (Not sure if this possible, but you can see if they have an extension for some cleanup during ref count downs). But the onus is up to you, not shared pointer to cleanup thread sp. stuff.
The next best thing I can think of for my class is to just have a std::map
, with synchronized access of course, and I clean up all the pointers in the map in the DTOR. That would work just great, except I really do need the pointers to also get cleaned up (and removed from my map) when the corresponding threads go away. Some of my objects can live a very long time, whilst threads get created and destroyed off and on, so without this functionality the map could grow indefinitely. So I'm stuck. I can't use thread_specific_ptr because it won't clean up everything when my object goes away, and I can't use a map because it won't clean up anything when a thread goes away. Is there an easy solution to this?
If there were some way for me to hook up a callback directly to the thread cleanup function, I could add a callback to remove it from my map when the thread goes away. But I'd also need the ability to remove that callback in my DTOR. Does boost provide any way to do this?
--- I like you last suggestion. But I can't find any way of doing it through boost threads. One thing I have to say though: if your design knows when to allocate the thread specific pointer, why doesn't it know when to deallocate it. If you're automatically allocating this cache at the beginning of a thread in each class object, expecting to deallocate at thread exit, maybe that's not the way to go. Is it possible to allocate it just when you need it and dealloc on exit of the method that needs it? best arun
I can't be the first person to want a thread-specific, object-specific pointer, can I?
Phillip
_______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
--
On Thu, Nov 17, 2011 at 5:46 PM, Arun Ramasamy
But if the object gets destroyed before all the threads, then you're expecting shared_ptr to do the cleanup for you (as in every time the ref count goes down you want the corresponding thread sp. obj to be destroyed). May be you can override one of shared_ptr's members, when ref count goes down, and reset the corresponding thread sp. obj's pointer (Not sure if this possible, but you can see if they have an extension for some cleanup during ref count downs). But the onus is up to you, not shared pointer to cleanup thread sp. stuff.
Hmm, that's a really interesting idea, but I don't know if I want to go mucking with overriding shared_ptr... That feels a little too scary.
--- I like you last suggestion. But I can't find any way of doing it through boost threads. One thing I have to say though: if your design knows when to allocate the thread specific pointer, why doesn't it know when to deallocate it. If you're automatically allocating this cache at the beginning of a thread in each class object, expecting to deallocate at thread exit, maybe that's not the way to go. Is it possible to allocate it just when you need it and dealloc on exit of the method that needs it?
I'm not allocating it at the beginning of a thread. Thousands of instances of this class can be instantiated on any thread at any time by several different places in the code, and these objects can get cached in global caches and then be used by other threads too. There's no way to know what thread might be the last one holding on to the object or removing it from the global cache. That's why I use shared pointers of course. I just want to allocate the thread_specific_ptr when my object gets allocated (this works), and I want it to get destroyed, along with all the TLS data it allocated, when my object gets destroyed (this doesn't work; it's a known bug/feature of thread_specific_ptr). Phillip
--- I like you last suggestion. But I can't find any way of doing it through boost threads. One thing I have to say though: if your design knows when to allocate the thread specific pointer, why doesn't it know when to deallocate it. If you're automatically allocating this cache at the beginning of a thread in each class object, expecting to deallocate at thread exit, maybe that's not the way to go. Is it possible to allocate it just when you need it and dealloc on exit of the method that needs it? I'm not allocating it at the beginning of a thread. Thousands of instances of this class can be instantiated on any thread at any time by several different places in the code, and these objects can get cached in global caches and then be used by other threads too. There's no way to know what thread might be the last one holding on to the object or removing it from the global cache. That's why I use shared pointers of course.
I just want to allocate the thread_specific_ptr when my object gets allocated (this works), -- When you say allocate, you just construct it right? I was talking about, when you call thread_specific_ptr->reset. This must be called within the context of a given thread inside a given function right? This is the point when you allocate "thread-specific" memory, which is not handled by shared_ptr cleanup. I'm assuming you have something like this: class MyClass{ thread_specific_ptr<MyCacheType> cacheptr;
On 11/18/2011 12:03 AM, Phillip Hellewell wrote: public: MyClass(){ } function_that_deals_with_thspptr(){ cacheptr.reset(new MyCacheType());//alloc thread specific memory; //use the memory ......... cacheptr.reset(0);//dealloc thread specific memory; } }; typedef shared_ptr<MyClass> MyClassRef; Do you not know when the need for cacheptr ends? I understand your project is much more complicated than above code. But I don't understand why you can't know when the need for the "cacheptr" ends. Your class objects can have a complicated lifespan. But you've shared_ptr to handle that, so no worries there.
and I want it to get destroyed, along with all the TLS data it allocated, when my object gets destroyed (this doesn't work; it's a known bug/feature of thread_specific_ptr). Phillip _______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
--
On Fri, Nov 18, 2011 at 6:44 AM, Arun Ramasamy
class MyClass{ thread_specific_ptr<MyCacheType> cacheptr; public: MyClass(){
} function_that_deals_with_thspptr(){ cacheptr.reset(new MyCacheType());//alloc thread specific memory; //use the memory ......... cacheptr.reset(0);//dealloc thread specific memory; } };
No, actually it's more like this: function_that_deals_with_thspptr() { if( !cacheptr.get() ) { cacheptr.reset(new MyCacheType());//alloc thread specific memory; } //use *cacheptr ......... //there is no reset here }
Do you not know when the need for cacheptr ends?
No, because it should stick around to be reused until the MyClass object goes away or the thread it goes with goes away. Phillip
Hi,
My class contains a member var that is a little buffer used as a cache. Instances of this class are used concurrently (referenced through a shared_ptr).
I would like to switch my buffer to a thread_specific_ptr, but there are two big problems with using thread_specific_ptr as a member var: The lifetime of your objects and the cache seems to be different. Have you tried to isolate the buffer (cache) in a static thread_specific_ptr? 1. There is a memory leak when my object goes away, because by design only one of the pointers gets cleaned up (the one for the thread that happens to delete the object). 2. There is undefined behavior when a new object gets created that happens to be allocated at the same address as a previously freed one. get( ) could return a pointer to a buffer that goes with an old (now freed) object. Yes, this is a know bug (feature for Anthony), that prevents from using
Le 13/11/11 00:00, Phillip Hellewell a écrit : them as member objects.
I try not to question the wisdom of the thread_specific_ptr authors, but given these two issues, especially #2, I have to conclude that thread_specific_ptr is only suitable for statics / globals. It is wholly unfit to be used as a member variable (except in a singleton class I guess).
The next best thing I can think of for my class is to just have a std::map
, with synchronized access of course, and I clean up all the pointers in the map in the DTOR. That would work just great, except I really do need the pointers to also get cleaned up (and removed from my map) when the corresponding threads go away. Some of my objects can live a very long time, whilst threads get created and destroyed off and on, so without this functionality the map could grow indefinitely. So I'm stuck. I can't use thread_specific_ptr because it won't clean up everything when my object goes away, and I can't use a map because it won't clean up anything when a thread goes away. Is there an easy solution to this?
Note that your map is equivalent to a static thread_specific_ptr, that present no evident advantages.
If there were some way for me to hook up a callback directly to the thread cleanup function, I could add a callback to remove it from my map when the thread goes away. But I'd also need the ability to remove that callback in my DTOR. Does boost provide any way to do this?
Boost.Thread provides a non member function at_thread_exit()
#include
I can't be the first person to want a thread-specific, object-specific pointer, can I?
My advice is use the static thread_specific_ptr ;-) Best, Vicente
On Wed, Nov 16, 2011 at 11:51 PM, Vicente J. Botet Escriba
Yes, this is a know bug (feature for Anthony), that prevents from using them as member objects.
Glad to know I'm not the only one who sees this "feature" as a bug :) I wonder how hard it would be to at least provide an option to clean up all the TLS data when it is destroyed?
Boost.Thread provides a non member function at_thread_exit()
Is this what you were looking for?
Thanks, yeah I think that is what I was looking for. Unfortunately, without the ability to also unhook a callback from the thread exit, it still might not do what I need though :(
My advice is use the static thread_specific_ptr ;-)
I actually do use a static thread_specific_ptr in various places in my code, but this is one scenario where I can't because it must be object-specific too. The buffer is associated with the object. Different objects must not use the same buffer, even if they are operated on by the same thread. That would result in getting the wrong data. The only way I could use a static/global thread_specific_ptr is if it pointed to a map of object (its address) to its buffer. But that puts me back in the same boat as I started in, with the same same problems as trying to use a thread_specific_ptr as a member var. Phillip
Phillip Hellewell wrote:
Glad to know I'm not the only one who sees this "feature" as a bug :) I wonder how hard it would be to at least provide an option to clean up all the TLS data when it is destroyed?
Very. By specification, TLS cleanup occurs in the same thread that allocated the data (because the data may have been allocated by a thread-specific allocator). When thread_specific_ptr is destroyed, it obviously can't do that.
The only way I could use a static/global thread_specific_ptr is if it pointed to a map of object (its address) to its buffer.
Either map< weak_ptr<Object>, buffer > or set< weak_ptr<Object> > with map< thread_id, buffer > in the object, as I see it.
But that puts me back in the same boat as I started in, with the same same problems as trying to use a thread_specific_ptr as a member var.
I'm not sure about that. Let's take the first option. What's the problem with it? No locks are needed, and in the function that creates a new map entry for the current object you can purge the expired weak_ptr entries in order to not let the map grow out of hand.
On Fri, Nov 18, 2011 at 2:04 AM, Peter Dimov
Phillip Hellewell wrote:
Glad to know I'm not the only one who sees this "feature" as a bug :) I wonder how hard it would be to at least provide an option to clean up all the TLS data when it is destroyed?
Very. By specification, TLS cleanup occurs in the same thread that allocated the data (because the data may have been allocated by a thread-specific allocator). When thread_specific_ptr is destroyed, it obviously can't do that.
I can understand an argument against doing it if it is too hard, but why is it so bad to change the spec to allow for an option as long as it is clearly documented that it should not be used if you are using a thread-specific allocator? Put another way, why do we care more about protecting those who might use a thread-specific allocator than protecting those who might use thread_specific_ptr as a member var? Ok, here's another idea: How about a new class, object_specific_thread_specific_ptr! :) Then we don't have to alter thread_specific_ptr. If nothing else, can we at least ask Anthony to add a Note to the documentation warning against using thread_specific_ptr as a non-static member var?
The only way I could use a static/global thread_specific_ptr is if it pointed to a map of object (its address) to its buffer.
Either map< weak_ptr<Object>, buffer > or set< weak_ptr<Object> > with map< thread_id, buffer > in the object, as I see it.
Hmm, say I never thought about using a weak_ptr to help me. Let me think about that for a bit...
But that puts me back in the same boat as I started in, with the same same problems as trying to use a thread_specific_ptr as a member var.
I'm not sure about that. Let's take the first option. What's the problem with it? No locks are needed, and in the function that creates a new map entry for the current object you can purge the expired weak_ptr entries in order to not let the map grow out of hand.
Ok, I think I understand how this could work. There's only one tiny problem though. Although most of my code always creates an Object inside a shared_ptr, it is allowed and there are some places that just create one on the stack and use it, e.g., within a function. Correct me if I am wrong, but this idea can't work unless I forcibly disable the ability to create an Object except via an Object::create() that returns a shared_ptr. Thanks, Phillip
Phillip Hellewell wrote:
I can understand an argument against doing it if it is too hard, but why is it so bad to change the spec to allow for an option as long as it is clearly documented that it should not be used if you are using a thread-specific allocator?
Well, the spec is already written, and not by us. http://pubs.opengroup.org/onlinepubs/009604499/functions/pthread_key_delete.... Even if we ignore the thread-specific allocation issue, I'm not sure I see how proper destruction can be implemented without imposing synchronization on the normal case. And the whole point of thread-specific storage is to avoid synchronization.
Ok, I think I understand how this could work. There's only one tiny problem though. Although most of my code always creates an Object inside a shared_ptr, it is allowed and there are some places that just create one on the stack and use it, e.g., within a function. Correct me if I am wrong, but this idea can't work unless I forcibly disable the ability to create an Object except via an Object::create() that returns a shared_ptr.
You are right. There is no way to distinguish a stack-allocated Object from another stack-allocated Object created at the same address, so a map keyed by the object address can't work. Your other option may be unique per-object identifiers for use as keys, with ~Object somehow marking the identifier as invalid. But the "invalid identifier" set will soon grow out of hand and I'm not seeing a way to purge it. You could, perhaps, store a shared_ptr to a dummy identifier object and use the corresponding weak_ptr as a key, but I'm not sure that this is worth the trouble (and allocations) if stack use is rare.
Le 19/11/11 13:45, Peter Dimov a écrit :
You are right. There is no way to distinguish a stack-allocated Object from another stack-allocated Object created at the same address, so a map keyed by the object address can't work. Your other option may be unique per-object identifiers for use as keys, with ~Object somehow marking the identifier as invalid. But the "invalid identifier" set will soon grow out of hand and I'm not seeing a way to purge it. You could, perhaps, store a shared_ptr to a dummy identifier object and use the corresponding weak_ptr as a key, but I'm not sure that this is worth the trouble (and allocations) if stack use is rare.
Hi, I don't see why the thread_specific_ptr could not store a key that will be managed by the pthread_key_ functions. AM I missing something evident; isn't it? Best, Vicente
participants (4)
-
Arun Ramasamy
-
Peter Dimov
-
Phillip Hellewell
-
Vicente J. Botet Escriba