[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.c++

looking for elegant C++ abstraction around pthread_key_t...

Chris M. Thomasson

10/30/2008 11:48:00 PM

Here is what I am playing around with now:
_________________________________________________________________
/* Simple TSD Object
______________________________________________________________*/
#include <pthread.h>
#include <cstdio>


#if defined(_MSC_VER)
# define DECLSPEC_CDECL __cdecl
#elif defined(__GNUC__)
# define DECLSPEC_CDECL __attribute__((cdecl))
#else
# error MSVC or GCC REQUIRED!!!!! ;^(...
#endif


template<typename T>
class tsd {
pthread_key_t m_key;

static void DECLSPEC_CDECL tsd_dtor(void* state) {
delete reinterpret_cast<T*>(state);
}

public:
struct main_guard {
tsd& m_tsd;

main_guard(tsd& tsd_) : m_tsd(tsd_) {

}

~main_guard() {
m_tsd.clear();
}
};

tsd() {
pthread_key_create(&m_key, tsd_dtor);
}

~tsd() {
pthread_key_delete(m_key);
std::printf("(%p)->tsd<T>::~tsd()\nhit <ENTER> to continue...",
(void*)this);
std::fflush(stdout);
std::getchar();
}

T& instance() const {
T* obj = reinterpret_cast<T*>(pthread_getspecific(m_key));
if (! obj) {
obj = new T();
pthread_setspecific(m_key, obj);
}
return *obj;
}

void clear() {
delete reinterpret_cast<T*>(pthread_getspecific(m_key));
pthread_setspecific(m_key, NULL);
}
};




/* Simple Usage Example
______________________________________________________________*/
#include <cassert>


static tsd<struct foo> g_foo_tsd;
static tsd<struct foo2> g_foo2_tsd;
static tsd<struct foo3> g_foo3_tsd;


struct foo {
foo() {
std::printf("(%p)->foo::foo()\n", (void*)this);
}

~foo() {
std::printf("(%p)->foo::~foo()\n", (void*)this);
}
};


struct foo2 {
foo2() {
std::printf("(%p)->foo2::foo2()\n", (void*)this);
}

~foo2() {
std::printf("(%p)->foo2::~foo2()\n", (void*)this);
}
};


struct foo3 {
foo3() {
std::printf("(%p)->foo3::foo3()\n", (void*)this);
}

~foo3() {
std::printf("(%p)->foo3::~foo3()\n", (void*)this);
}
};


extern "C" void* thread_entry(void* state) {
{
foo& f1 = g_foo_tsd.instance();
foo& f2 = g_foo_tsd.instance();
foo& f3 = g_foo_tsd.instance();
assert(&f1 == &f2 && &f2 == &f3);
}

{
foo2& f1 = g_foo2_tsd.instance();
foo2& f2 = g_foo2_tsd.instance();
foo2& f3 = g_foo2_tsd.instance();
assert(&f1 == &f2 && &f2 == &f3);
}

{
foo3& f1 = g_foo3_tsd.instance();
foo3& f2 = g_foo3_tsd.instance();
foo3& f3 = g_foo3_tsd.instance();
assert(&f1 == &f2 && &f2 == &f3);
}

return 0;
}


int main(void) {
{
tsd<foo>::main_guard tsd_main_guard(g_foo_tsd);
tsd<foo2>::main_guard tsd_main_guard2(g_foo2_tsd);
tsd<foo3>::main_guard tsd_main_guard3(g_foo3_tsd);

pthread_t tid[2];
pthread_create(&tid[0], NULL, thread_entry, NULL);
pthread_create(&tid[1], NULL, thread_entry, NULL);

{
foo& f1 = g_foo_tsd.instance();
foo& f2 = g_foo_tsd.instance();
foo& f3 = g_foo_tsd.instance();
assert(&f1 == &f2 && &f2 == &f3);
}

{
foo2& f1 = g_foo2_tsd.instance();
foo2& f2 = g_foo2_tsd.instance();
foo2& f3 = g_foo2_tsd.instance();
assert(&f1 == &f2 && &f2 == &f3);
}

{
foo3& f1 = g_foo3_tsd.instance();
foo3& f2 = g_foo3_tsd.instance();
foo3& f3 = g_foo3_tsd.instance();
assert(&f1 == &f2 && &f2 == &f3);
}

pthread_join(tid[1], NULL);
pthread_join(tid[0], NULL);
}
std::puts("\n\n\n__________________\nhit <ENTER> to exit...");
std::fflush(stdout);
std::getchar();
return 0;
}
_________________________________________________________________




As you can see it uses compiler specific extensions in order to ensure the
procedure `tsd<T>::tsd_dtor()' has C linkage. I am doing this in order to
get around having to dynamically create a base-class, helper object and a
free extern "C" function. I can't really see any way around having to go
through that mess without resorting to compiler extensions. Humm, if I were
to use a free function, I think I would have to do something ugly like:
_________________________________________________________________
/* Simple TSD Object
______________________________________________________________*/
#include <pthread.h>
#include <cstdio>


struct tsd_object_base {
virtual ~tsd_object_base() = 0;
};

tsd_object_base::~tsd_object_base() {}


extern "C" void tsd_object_dtor(void* state) {
delete reinterpret_cast<tsd_object_base*>(state);
}


template<typename T>
class tsd {
struct tsd_object : public tsd_object_base {
T m_object;
tsd_object() : m_object() {}
};


pthread_key_t m_key;


public:
struct main_guard {
tsd& m_tsd;

main_guard(tsd& tsd_) : m_tsd(tsd_) {

}

~main_guard() {
m_tsd.clear();
}
};

tsd() {
pthread_key_create(&m_key, tsd_object_dtor);
}

~tsd() {
pthread_key_delete(m_key);
std::printf("(%p)->tsd<T>::~tsd()\nhit <ENTER> to continue...",
(void*)this);
std::fflush(stdout);
std::getchar();
}

T& instance() const {
tsd_object* obj =
reinterpret_cast<tsd_object*>(pthread_getspecific(m_key));
if (! obj) {
obj = new tsd_object();
pthread_setspecific(m_key, obj);
}
return obj->m_object;
}

void clear() {
delete reinterpret_cast<tsd_object*>(pthread_getspecific(m_key));
pthread_setspecific(m_key, NULL);
}
};


// [...]
_________________________________________________________________




This works fine, but IMVHO, its kind of messy. However, it is standard wrt
POSIX rules, and a heck of a lot more portable. Is there any way to keep
maximum portability, yet remove the need for helper classes? I suppose I
could do two versions and #ifdef them if the compiler does not support the
extensions I am looking for... Humm... Need advise!


Thanks.

7 Answers

Chris M. Thomasson

10/31/2008 12:03:00 AM

0


"Chris M. Thomasson" <no@spam.invalid> wrote in message
news:rBrOk.33$kd5.23@newsfe01.iad...
> Here is what I am playing around with now:
> _________________________________________________________________
[...]
> _________________________________________________________________
>
> As you can see it uses compiler specific extensions in order to ensure the
> procedure `tsd<T>::tsd_dtor()' has C linkage. I am doing this in order to
> get around having to dynamically create a base-class, helper object and a
> free extern "C" function. I can't really see any way around having to go
> through that mess without resorting to compiler extensions. Humm, if I
> were to use a free function, I think I would have to do something ugly
> like:
> _________________________________________________________________
[...]
> _________________________________________________________________
>
> This works fine, but IMVHO, its kind of messy. However, it is standard wrt
> POSIX rules, and a heck of a lot more portable. Is there any way to keep
> maximum portability, yet remove the need for helper classes? I suppose I
> could do two versions and #ifdef them if the compiler does not support the
> extensions I am looking for... Humm... Need advise!
[...]

Well, I suppose I could allow the user to pass in a free function that must
have C linkage for use as the actual TSD dtor; something like:
_________________________________________________________________
/* Simple TSD Object
______________________________________________________________*/
#include <pthread.h>
#include <cstdio>


template<typename T>
class tsd {
pthread_key_t m_key;
typedef void (func_dtor_t) (void*);
func_dtor_t* const m_fp_dtor;


public:
struct main_guard {
tsd& m_tsd;

main_guard(tsd& tsd_) : m_tsd(tsd_) {

}

~main_guard() {
m_tsd.clear();
}
};

tsd(func_dtor_t* const fp_dtor) : m_fp_dtor(fp_dtor) {
pthread_key_create(&m_key, fp_dtor);
}

~tsd() {
pthread_key_delete(m_key);
std::printf("(%p)->tsd<T>::~tsd()\nhit <ENTER> to continue...",
(void*)this);
std::fflush(stdout);
std::getchar();
}

T& instance() const {
T* obj = reinterpret_cast<T*>(pthread_getspecific(m_key));
if (! obj) {
obj = new T();
pthread_setspecific(m_key, obj);
}
return *obj;
}

void clear() {
T* obj = reinterpret_cast<T*>(pthread_getspecific(m_key));
pthread_setspecific(m_key, NULL);
if (obj) {
m_fp_dtor(obj);
}
}
};




/* Simple Usage Example
______________________________________________________________*/
#include <cassert>


extern "C" void foo_dtor(void* state);
extern "C" void foo2_dtor(void* state);
extern "C" void foo3_dtor(void* state);


static tsd<struct foo> g_foo_tsd(foo_dtor);
static tsd<struct foo2> g_foo2_tsd(foo2_dtor);
static tsd<struct foo3> g_foo3_tsd(foo3_dtor);


struct foo {
foo() {
std::printf("(%p)->foo::foo()\n", (void*)this);
}

~foo() {
std::printf("(%p)->foo::~foo()\n", (void*)this);
}
};

void foo_dtor(void* state) {
delete reinterpret_cast<foo*>(state);
}


struct foo2 {
foo2() {
std::printf("(%p)->foo2::foo2()\n", (void*)this);
}

~foo2() {
std::printf("(%p)->foo2::~foo2()\n", (void*)this);
}
};

void foo2_dtor(void* state) {
delete reinterpret_cast<foo2*>(state);
}


struct foo3 {
foo3() {
std::printf("(%p)->foo3::foo3()\n", (void*)this);
}

~foo3() {
std::printf("(%p)->foo3::~foo3()\n", (void*)this);
}
};

void foo3_dtor(void* state) {
delete reinterpret_cast<foo3*>(state);
}





extern "C" void* thread_entry(void* state) {
{
foo& f1 = g_foo_tsd.instance();
foo& f2 = g_foo_tsd.instance();
foo& f3 = g_foo_tsd.instance();
assert(&f1 == &f2 && &f2 == &f3);
}

{
foo2& f1 = g_foo2_tsd.instance();
foo2& f2 = g_foo2_tsd.instance();
foo2& f3 = g_foo2_tsd.instance();
assert(&f1 == &f2 && &f2 == &f3);
}

{
foo3& f1 = g_foo3_tsd.instance();
foo3& f2 = g_foo3_tsd.instance();
foo3& f3 = g_foo3_tsd.instance();
assert(&f1 == &f2 && &f2 == &f3);
}

return 0;
}


int main(void) {
{
tsd<foo>::main_guard tsd_main_guard(g_foo_tsd);
tsd<foo2>::main_guard tsd_main_guard2(g_foo2_tsd);
tsd<foo3>::main_guard tsd_main_guard3(g_foo3_tsd);

pthread_t tid[2];
pthread_create(&tid[0], NULL, thread_entry, NULL);
pthread_create(&tid[1], NULL, thread_entry, NULL);

{
foo& f1 = g_foo_tsd.instance();
foo& f2 = g_foo_tsd.instance();
foo& f3 = g_foo_tsd.instance();
assert(&f1 == &f2 && &f2 == &f3);
}

{
foo2& f1 = g_foo2_tsd.instance();
foo2& f2 = g_foo2_tsd.instance();
foo2& f3 = g_foo2_tsd.instance();
assert(&f1 == &f2 && &f2 == &f3);
}

{
foo3& f1 = g_foo3_tsd.instance();
foo3& f2 = g_foo3_tsd.instance();
foo3& f3 = g_foo3_tsd.instance();
assert(&f1 == &f2 && &f2 == &f3);
}

pthread_join(tid[1], NULL);
pthread_join(tid[0], NULL);
}
std::puts("\n\n\n__________________\nhit <ENTER> to exit...");
std::fflush(stdout);
std::getchar();
return 0;
}
_________________________________________________________________




That would solve my problem, but might not be all that user friendly. For
one, the poor users would
__always_need__ to ensure that the free function they pass to the `tsd<T>'
ctor has proper C linkage... Not to sure about this... Humm...


Any advise? I am NOT a C++ guy! I need help here...

:^(...

Maxim Yegorushkin

10/31/2008 9:26:00 AM

0

On Oct 30, 11:48 pm, "Chris M. Thomasson" <n...@spam.invalid> wrote:
> Here is what I am playing around with now:

[]

Have you looked at http://www.boost.org/doc/libs/1_36_0/doc/html/thread/thread_local_st...
?

--
Max

Chris M. Thomasson

10/31/2008 6:28:00 PM

0


"Maxim Yegorushkin" <maxim.yegorushkin@gmail.com> wrote in message
news:9a891bd5-77e1-4d92-9097-4e40438dade1@i18g2000prf.googlegroups.com...
On Oct 30, 11:48 pm, "Chris M. Thomasson" <n...@spam.invalid> wrote:
> > Here is what I am playing around with now:

> []

> Have you looked at
> http://www.boost.org/doc/libs/1_36_0/doc/html/thread/thread_local_st... ?

Yeah. I need to look at the actual source-code and see how they deal with
handling the TSD dtor callback. It needs to have C-linkage, and I want to
see how they get it to understand what type its dealing with. Do they use a
base-class, or other trickery that I am not familiar with.

Chris M. Thomasson

10/31/2008 6:51:00 PM

0


"Chris M. Thomasson" <no@spam.invalid> wrote in message
news:o%HOk.1694$cx5.507@newsfe01.iad...
>
> "Maxim Yegorushkin" <maxim.yegorushkin@gmail.com> wrote in message
> news:9a891bd5-77e1-4d92-9097-4e40438dade1@i18g2000prf.googlegroups.com...
> On Oct 30, 11:48 pm, "Chris M. Thomasson" <n...@spam.invalid> wrote:
>> > Here is what I am playing around with now:
>
>> []
>
>> Have you looked at
>> http://www.boost.org/doc/libs/1_36_0/doc/html/thread/thread_local_st... ?
>
> Yeah. I need to look at the actual source-code and see how they deal with
> handling the TSD dtor callback. It needs to have C-linkage, and I want to
> see how they get it to understand what type its dealing with. Do they use
> a base-class, or other trickery that I am not familiar with.


Well, it seems like they are using an abstract base-class. I may be wrong;
the Boost source-code is pretty hardcore.

Maxim Yegorushkin

10/31/2008 9:34:00 PM

0

On Oct 31, 6:51 pm, "Chris M. Thomasson" <n...@spam.invalid> wrote:
> "Chris M. Thomasson" <n...@spam.invalid> wrote in messagenews:o%HOk.1694$cx5.507@newsfe01.iad...
>
>
>
> > "Maxim Yegorushkin" <maxim.yegorush...@gmail.com> wrote in message
> >news:9a891bd5-77e1-4d92-9097-4e40438dade1@i18g2000prf.googlegroups.com...
> > On Oct 30, 11:48 pm, "Chris M. Thomasson" <n...@spam.invalid> wrote:
> >> > Here is what I am playing around with now:
>
> >> []
>
> >> Have you looked at
> >>http://www.boost.org/doc/libs/1_36_0/doc/html/thread/thread_loca...
>
> > Yeah. I need to look at the actual source-code and see how they deal with
> > handling the TSD dtor callback.

The source code which deals with pthread specifics of TLS is here
http://boost.cvs.sourceforge.net/viewvc/boost/boost/libs/thread/src/tss.cpp?revision=HEAD&v...

However, boost does not map thread_specific_ptr to pthread_key_t 1:1.
Instead, they boost threads library uses only one pthread_key_t and
implements its own clean up handler mechanism.

> > It needs to have C-linkage, and I want to
> > see how they get it to understand what type its dealing with. Do they use
> > a base-class, or other trickery that I am not familiar with.

Although all POSIX APIs require pointers to functions with C linkage,
on practice all C++ functions and static member functions have C
linkage, it is name mangling what is different. I don't know of any
platform/compiler where this is not true (my knowledge is limited
though).

This is what I did with my own thread specific pointer (intended to be
used for global variables mostly):

http://ccull.svn.sourceforge.net/viewvc/ccull/trunk/include/threads/tls_ptr..h?revision=1&v...

--
Max

Chris M. Thomasson

10/31/2008 10:05:00 PM

0


"Maxim Yegorushkin" <maxim.yegorushkin@gmail.com> wrote in message
news:c96cebad-b090-4f34-9751-a37844f067bc@b38g2000prf.googlegroups.com...
On Oct 31, 6:51 pm, "Chris M. Thomasson" <n...@spam.invalid> wrote:
> > "Chris M. Thomasson" <n...@spam.invalid> wrote in
> > messagenews:o%HOk.1694$cx5.507@newsfe01.iad...
> >
> >
> >
> > > "Maxim Yegorushkin" <maxim.yegorush...@gmail.com> wrote in message
> > >news:9a891bd5-77e1-4d92-9097-4e40438dade1@i18g2000prf.googlegroups.com...
> > > On Oct 30, 11:48 pm, "Chris M. Thomasson" <n...@spam.invalid> wrote:
> > >> > Here is what I am playing around with now:
> >
> > >> []
> >
> > >> Have you looked at
> > >>http://www.boost.org/doc/libs/1_36_0/doc/html/thread/thread_loc...
> >
> > > Yeah. I need to look at the actual source-code and see how they deal
> > > with
> > > handling the TSD dtor callback.

> The source code which deals with pthread specifics of TLS is here
> http://boost.cvs.sourceforge.net/viewvc/boost/boost/libs/thread/src/tss.cpp?revision=HEAD&v...

> However, boost does not map thread_specific_ptr to pthread_key_t 1:1.
> Instead, they boost threads library uses only one pthread_key_t and
> implements its own clean up handler mechanism.

Ahhh... Okay. Humm, now I wonder why they did it that way. This kind of
seems odd to me. I know that you have to do that on Windows, but under a
POSIX system; why? I suppose it would be good in the sense of you won't run
out of TSD keys. Interesting.




> > > It needs to have C-linkage, and I want to
> > > see how they get it to understand what type its dealing with. Do they
> > > use
> > > a base-class, or other trickery that I am not familiar with.

> Although all POSIX APIs require pointers to functions with C linkage,
> on practice all C++ functions and static member functions have C
> linkage, it is name mangling what is different. I don't know of any
> platform/compiler where this is not true (my knowledge is limited
> though).

> This is what I did with my own thread specific pointer (intended to be
> used for global variables mostly):

> http://ccull.svn.sourceforge.net/viewvc/ccull/trunk/include/threads/tls_ptr.h?revision=1&v...

Humm... Well, I was going for something that would be as portable as
possible. Murphy's Law comes to mind:


The first use of your TSD will of course be on a platform that does not use
C-linkage for static member functions.


Yikes! ;^)

Anthony Williams

10/31/2008 10:17:00 PM

0

"Chris M. Thomasson" <no@spam.invalid> writes:

> "Chris M. Thomasson" <no@spam.invalid> wrote in message
> news:o%HOk.1694$cx5.507@newsfe01.iad...
>>
>> "Maxim Yegorushkin" <maxim.yegorushkin@gmail.com> wrote in message
>> news:9a891bd5-77e1-4d92-9097-4e40438dade1@i18g2000prf.googlegroups.com...
>> On Oct 30, 11:48 pm, "Chris M. Thomasson" <n...@spam.invalid> wrote:
>>> > Here is what I am playing around with now:
>>
>>> []
>>
>>> Have you looked at
>>> http://www.boost.org/doc/libs/1_36_0/doc/html/thread/thread_local_st...
>>> ?
>>
>> Yeah. I need to look at the actual source-code and see how they deal
>> with handling the TSD dtor callback. It needs to have C-linkage, and
>> I want to see how they get it to understand what type its dealing
>> with. Do they use a base-class, or other trickery that I am not
>> familiar with.
>
>
> Well, it seems like they are using an abstract base-class.

Yes, I use an abstract base class.

In boost, the actual TSD value is a pointer to a data structure which
contains *all* the thread-specific data for a given thread (it's
currently a linked list). Each entry has its own cleanup function,
which is called through a pointer to an abstract base class.

The TSD dtor callback is an extern "C" function that takes a pointer
to the data structure and calls the cleanup function for each
thread_specific_ptr value used by the current thread.

> I may be wrong; the Boost source-code is pretty hardcore.

:-)

Anthony
--
Anthony Williams
Author of C++ Concurrency in Action | http://www.manning.co...

Custom Software Development | http://www.justsoftwaresolut...
Just Software Solutions Ltd, Registered in England, Company Number 5478976.
Registered Office: 15 Carrallack Mews, St Just, Cornwall, TR19 7UL, UK