Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C++ > looking for elegant C++ abstraction around pthread_key_t...

Reply
Thread Tools

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

 
 
Chris M. Thomasson
Guest
Posts: n/a
 
      10-30-2008
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:rintf("(%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:rintf("(%p)->foo::foo()\n", (void*)this);
}

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


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

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


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

~foo3() {
std:rintf("(%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:uts("\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:rintf("(%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.

 
Reply With Quote
 
 
 
 
Chris M. Thomasson
Guest
Posts: n/a
 
      10-31-2008

"Chris M. Thomasson" <> wrote in message
news:rBrOk.33$...
> 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:rintf("(%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:rintf("(%p)->foo::foo()\n", (void*)this);
}

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

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


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

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

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


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

~foo3() {
std:rintf("(%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:uts("\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...

:^(...

 
Reply With Quote
 
 
 
 
Maxim Yegorushkin
Guest
Posts: n/a
 
      10-31-2008
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...l_storage.html
?

--
Max
 
Reply With Quote
 
Chris M. Thomasson
Guest
Posts: n/a
 
      10-31-2008

"Maxim Yegorushkin" <> wrote in message
news:9a891bd5-77e1-4d92-9097-...
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...l_storage.html ?


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.

 
Reply With Quote
 
Chris M. Thomasson
Guest
Posts: n/a
 
      10-31-2008

"Chris M. Thomasson" <> wrote in message
news%HOk.1694$...
>
> "Maxim Yegorushkin" <> wrote in message
> news:9a891bd5-77e1-4d92-9097-...
> 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...l_storage.html ?

>
> 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.

 
Reply With Quote
 
Maxim Yegorushkin
Guest
Posts: n/a
 
      10-31-2008
On Oct 31, 6:51*pm, "Chris M. Thomasson" <n...@spam.invalid> wrote:
> "Chris M. Thomasson" <n...@spam.invalid> wrote in messagenews%HOk.1694$...
>
>
>
> > "Maxim Yegorushkin" <maxim.yegorush...@gmail.com> wrote in message
> >news:9a891bd5-77e1-4d92-9097-....
> > 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...local_sto....?

>
> > 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/vie...AD&view=markup

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/vie...=1&view=markup

--
Max
 
Reply With Quote
 
Chris M. Thomasson
Guest
Posts: n/a
 
      10-31-2008

"Maxim Yegorushkin" <> wrote in message
news:c96cebad-b090-4f34-9751-...
On Oct 31, 6:51 pm, "Chris M. Thomasson" <n...@spam.invalid> wrote:
> > "Chris M. Thomasson" <n...@spam.invalid> wrote in
> > messagenews%HOk.1694$...
> >
> >
> >
> > > "Maxim Yegorushkin" <maxim.yegorush...@gmail.com> wrote in message
> > >news:9a891bd5-77e1-4d92-9097-...
> > > 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..._local_sto...?

> >
> > > 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/vie...AD&view=markup


> 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/vie...=1&view=markup


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! ;^)

 
Reply With Quote
 
Anthony Williams
Guest
Posts: n/a
 
      10-31-2008
"Chris M. Thomasson" <> writes:

> "Chris M. Thomasson" <> wrote in message
> news%HOk.1694$...
>>
>> "Maxim Yegorushkin" <> wrote in message
>> news:9a891bd5-77e1-4d92-9097-...
>> 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...l_storage.html
>>> ?

>>
>> 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.com/williams

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

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
a really simple C++ abstraction around pthread_t... Chris M. Thomasson C++ 19 11-09-2008 12:12 PM
Microsoft & patents: what goes around comes around... Lawrence D'Oliveiro NZ Computing 104 12-16-2006 07:11 AM
Read all of this to understand how it works. then check around on otherRead all of this to understand how it works. then check around on other thelisa martin Computer Support 2 08-18-2005 06:40 AM
Make wxListCtrl fit around contents and parent frame fit around listctrl Piet Python 0 07-18-2004 08:27 AM
Looking for a more elegant way to do memory offsets mikegw C Programming 5 05-24-2004 11:39 AM



Advertisments
 



1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57