Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Question on C++ Thread Class and inheritance/polymorphism with POSIX pthread

102 views
Skip to first unread message

alexroat

unread,
Oct 27, 2008, 1:55:58 PM10/27/08
to
Hello to everybody,
I cannot understand a strange behaviour of the following C++ code.
I'm on debian linux with gcc 4.1.2 and I'm using POSIX pthread.
I want to create a class which encapsulates the logic of threads in
order to have a simple Thread virtual class which must be INHERITABLE,
that means, I can create a thread with custom code simply inheriting
Thread class and overloading void run() method.

This is the code that I written:

class Thread
{
public:
Thread();//constructor
virtual ~Thread();//distructor
int start();//method which allow user to launch the thread
virtual void run();virtual empty method run, the user will put
the
thread's code here
private:
static void * gate(void *);//access point for pthread_create
pthread_t _tid;//thread ID
};

Thread::Thread() : _tid(0)
{

}

int Thread::start()
{
//create the thread
return pthread_create(&_tid, NULL,gate,this);
}

Thread::~Thread()
{
//wait for thread reconjunction
if (_tid)
pthread_join(_tid,NULL);
}

void Thread::run()
{
cerr << "hello, I'm the base thread" << endl;//debug print
}

void * Thread::gate(void * p)
{
//cast the void pointer
((Thread *)p)->run();
return NULL;
}

My intention is to inherit Thread and redefine Thread::run(), that is
for example :

class MyThread : public Thread
{
public:
void run();
};

MyThread::run()
{
cerr << "hello, I'm the derived thread" << endl;//debug print
}


In the main funztion I put


MyThread mt;
mt.start();
...
...
...

Well,
I excpect to see "hello, I'm the derived thread" on the output,
instead I obtain is :

"hello, I'm the base thread"

-------------
WHY ????
-------------

I defined run as a virtual function so, even after a cast to void *
and then a cast to Thread * of the address of mt ( that is a MyThread
* ), I would excpect that the called method is decided not on the base
of the type of the pointer (Thread *) but on the type of the original
object (MyThread *) recorded in the v-table. This is the basics of the
polymorphism ....

The strange thing is that's happen only using threads. With the
following code instead everithing works correctly :

MyThread mt;
Thread *p=&mt;
p->run();

OUTPUT :
"hello, I'm the derived thread".

Please,
let me know what is the motivation of this strange behaviour.

Many thanks.

Alessandro

Ian Collins

unread,
Oct 27, 2008, 2:43:45 PM10/27/08
to
alexroat wrote:
> Hello to everybody,
> I cannot understand a strange behaviour of the following C++ code.
> I'm on debian linux with gcc 4.1.2 and I'm using POSIX pthread.
> I want to create a class which encapsulates the logic of threads in
> order to have a simple Thread virtual class which must be INHERITABLE,
> that means, I can create a thread with custom code simply inheriting
> Thread class and overloading void run() method.
>
Have a look at boost threads, the template approach is more flexible.

> This is the code that I written:
>
> class Thread
> {
> public:
> Thread();//constructor
> virtual ~Thread();//distructor

Style comment: drop the superfluous comments!

> int start();//method which allow user to launch the thread
> virtual void run();virtual empty method run, the user will put
> the
> thread's code here
> private:
> static void * gate(void *);//access point for pthread_create

This should be an extern "C" linkage function, not a static member.

<snip>

>
> The strange thing is that's happen only using threads. With the
> following code instead everithing works correctly :
>
> MyThread mt;
> Thread *p=&mt;
> p->run();
>
> OUTPUT :
> "hello, I'm the derived thread".
>
> Please,
> let me know what is the motivation of this strange behaviour.
>

main() has returned and mt has been at least partly destroyed. If you
add something like a getchar() after the call to run to prevent is, you
will get a different result.

--
Ian Collins

Chris M. Thomasson

unread,
Oct 27, 2008, 5:44:32 PM10/27/08
to

"Ian Collins" <ian-...@hotmail.com> wrote in message
news:6mmgf1F...@mid.individual.net...

> alexroat wrote:
>> Hello to everybody,
>> I cannot understand a strange behaviour of the following C++ code.
>> I'm on debian linux with gcc 4.1.2 and I'm using POSIX pthread.
>> I want to create a class which encapsulates the logic of threads in
>> order to have a simple Thread virtual class which must be INHERITABLE,
>> that means, I can create a thread with custom code simply inheriting
>> Thread class and overloading void run() method.
>>
> Have a look at boost threads, the template approach is more flexible.
>
>> This is the code that I written:
>>
>> class Thread
>> {
>> public:
>> Thread();//constructor
>> virtual ~Thread();//distructor
>
> Style comment: drop the superfluous comments!
>
>> int start();//method which allow user to launch the thread
>> virtual void run();virtual empty method run, the user will put
>> the
>> thread's code here
>> private:
>> static void * gate(void *);//access point for pthread_create
>
> This should be an extern "C" linkage function, not a static member.

[...]

Yes. Humm... Well, FWIW, I guess one can take advantage of compiler specific
extensions here. It's a dirty thing, but I think it would work. Something
like:


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


class Thread
{
public:
Thread();
virtual ~Thread();
int start();
virtual void run();

private:
static void* DECLSPEC_CDECL gate(void*);
pthread_t _tid;
};


This should be okay as long as the OP does not mind his code being tied to
specific compilers for its entire _lifetime_!

:^/

Ian Collins

unread,
Oct 27, 2008, 5:41:08 PM10/27/08
to
Chris M. Thomasson wrote:
>
> "Ian Collins" <ian-...@hotmail.com> wrote in message
> news:6mmgf1F...@mid.individual.net...
>> alexroat wrote:

>>> private:
>>> static void * gate(void *);//access point for pthread_create
>>
>> This should be an extern "C" linkage function, not a static member.
>
> [...]
>
> Yes. Humm... Well, FWIW, I guess one can take advantage of compiler
> specific extensions here. It's a dirty thing, but I think it would work.
> Something like:
>
> #if defined(_MSC_VER)
> # define DECLSPEC_CDECL __cdecl
> #elif defined(__GNUC__)
> # define DECLSPEC_CDECL __attribute__((cdecl))
> #else
> # error MSVC or GCC REQUIRED!!!!! ;^(...
> #endif
>

Why bother with all that nonsense when there is a standard solution?

--
Ian Collins

Chris M. Thomasson

unread,
Oct 27, 2008, 11:20:16 PM10/27/08
to

"Ian Collins" <ian-...@hotmail.com> wrote in message
news:6mmqrkF...@mid.individual.net...

Agreed. However, using the non-standard compiler specific hack can eliminate
the "need" to use a base-class for the thread class. The extern "C"
free-function has no idea what type what will be passed to it. This is not
the case for the hack version...

JoelKatz

unread,
Oct 28, 2008, 10:30:26 AM10/28/08
to
On Oct 27, 11:43 am, Ian Collins <ian-n...@hotmail.com> wrote:

> main() has returned and mt has been at least partly destroyed.  If you
> add something like a getchar() after the call to run to prevent is, you
> will get a different result.

So close, but not quite. Actually, 'main' can't return because the
destructor calls 'pthread_join'.

In fact, his problem is that he calls 'pthread_join' in the
destructor. You can't do that. In fact, he can replicate his problem
without using threads at all. Consider:

class Thread
{
public:
Thread(void);//constructor
virtual ~Thread();//distructor
virtual void run(void);
};

Thread::Thread()
{
}

Thread::~Thread()
{
run();
}

void Thread::run()
{
printf("Base\n");
}

class MyThread : public Thread
{
public:

virtual void run(void);
};

void MyThread::run(void)
{
printf("Derived\n");
}

int main(void)
{
MyThread mt;
}

In simplest terms, he has a race condition. His 'main' function
destroys an object (his thread object) while another thread (the newly-
created thread) is, or might be, using it. Calling 'pthread_join' in
the destructor is nonsense.

DS

JoelKatz

unread,
Oct 28, 2008, 10:58:49 AM10/28/08
to
On Oct 27, 10:55 am, alexroat <alexr...@gmail.com> wrote:

> The strange thing is that's happen only using threads.

Really? What do you get with this code:

#include <stdio.h>
#include <pthread.h>

class Thread
{
public:
Thread(void) { ; }
virtual ~Thread() { run(); }
virtual void run(void);
};

void Thread::run(void)
{
printf("Base\n");
}

class MyThread : public Thread
{
public:

virtual void run(void);
};

void MyThread::run(void)
{
printf("Derived\n");
}

int main(void)
{
MyThread mt;
}

You need to join the thread *BEFORE* you destroy the object. You
cannot destroy even some of the object while the thread is still
running.

DS

James Kanze

unread,
Oct 28, 2008, 12:50:30 PM10/28/08
to
On Oct 27, 7:43 pm, Ian Collins <ian-n...@hotmail.com> wrote:
> alexroat wrote:

> > I cannot understand a strange behaviour of the following C++
> > code. I'm on debian linux with gcc 4.1.2 and I'm using
> > POSIX pthread. I want to create a class which encapsulates
> > the logic of threads in order to have a simple Thread
> > virtual class which must be INHERITABLE, that means, I can
> > create a thread with custom code simply inheriting Thread
> > class and overloading void run() method.

> Have a look at boost threads, the template approach is more flexible.

It depends. It doesn't work that well with joinable threads,
where you want to recover some values after the join; you need
to introduce an extra level of indirection to work around the
copy. On the other hand, it's perfect for detached threads.

[...]


> > The strange thing is that's happen only using threads. With the
> > following code instead everithing works correctly :
>
> > MyThread mt;
> > Thread *p=&mt;
> > p->run();

> > OUTPUT :
> > "hello, I'm the derived thread".

> > Please,
> > let me know what is the motivation of this strange behaviour.

> main() has returned and mt has been at least partly destroyed.
> If you add something like a getchar() after the call to run to
> prevent is, you will get a different result.

Or join before returning from main.

--
James Kanze (GABI Software) email:james...@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Chris M. Thomasson

unread,
Oct 28, 2008, 7:07:05 PM10/28/08
to
FWIW, here is a quick example (in the form of a fully compliable program
with error checking omitted) of how I use POSIX threads within a C++
environment:
_________________________________________________________________
/* Simple Thread Object
______________________________________________________________*/
#include <pthread.h>


extern "C" void* thread_entry(void*);

class thread_base {
pthread_t m_tid;
friend void* thread_entry(void*);
virtual void on_thread_entry() = 0;

public:
virtual ~thread_base() = 0;

void thread_run() {
pthread_create(&m_tid, NULL, thread_entry, this);
}

void thread_join() {
pthread_join(m_tid, NULL);
}
};

thread_base::~thread_base() {}

void* thread_entry(void* state) {
reinterpret_cast<thread_base*>(state)->on_thread_entry();
return 0;
}

template<typename T>
struct thread : public T {
thread() : T() {
this->thread_run();
}

~thread() {
this->thread_join();
}

template<typename T_p1>
thread(T_p1 p1) : T(p1) {
this->thread_run();
}

template<typename T_p1, typename T_p2>
thread(T_p1 p1, T_p2 p2) : T(p1, p2) {
this->thread_run();
}

// [and on and on for for params...]
};


/* Simple Usage Example
______________________________________________________________*/
#include <string>
#include <cstdio>


class worker : public thread_base {
std::string const m_name;

void on_thread_entry() {
std::printf("(%p)->worker(%s)::on_thread_entry()\n",
(void*)this, m_name.c_str());
}

public:
worker(std::string const& name)
: m_name(name) {
std::printf("(%p)->worker(%s)::my_thread()\n",
(void*)this, m_name.c_str());
}

~worker() {
std::printf("(%p)->worker(%s)::~my_thread()\n",
(void*)this, m_name.c_str());
}
};


int main(void) {
{
thread<worker> workers[] = {
"Chris",
"John",
"Jane",
"Steve",
"Richard",
"Lisa"
};

worker another_worker("Jeff");
another_worker.thread_run();
another_worker.thread_join();
}

std::puts("\n\n\n__________________\nhit <ENTER> to exit...");
std::fflush(stdout);
std::getchar();
return 0;
}
_________________________________________________________________


IMVHO, this is very straight forward and works well. In fact, I think I like
it better than the Boost method... Humm... Well, what do you all think about
the design? Is it crap?

Szabolcs Ferenczi

unread,
Oct 29, 2008, 6:06:47 AM10/29/08
to

The active object concept is much better than the Boost thread and I
have suggested it earlier that the C++0x committee should consider
this pattern, i.e. active object, as a high level wrapper construction
next to their high level wrapper wait/2 on the condition variable:

<quote from="
http://groups.google.com/group/comp.lang.c++/msg/8e80468d988c4feb
">
I do think it has advantages over Boost's method. One such advantage
is the RAII nature of it.

Furthermore, I think it should be taken in into the C++0x standard on
the similar grounds as they provide higher level condition variable
wait API as well:

<quote>
template <class Predicate>
void wait(unique_lock<mutex>& lock, Predicate pred);
Effects:
As if:
while (!pred())
wait(lock);
</quote>
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2497.html
</quote>

Another remark: Why do you not keep to the terminology we have come up
with earlier? It is for an active object and not for any plain thread.
See:

"What's the connection between objects and threads?"
http://groups.google.com/group/comp.lang.c++/msg/84d6d69cae17fee7

What is the difference between:

worker another_worker("XY");
another_worker.thread_run();
another_worker.thread_join();

and

ActiveObject<worker> another_worker("XY");

Well, the difference is that the wrapper takes care of the low level
details of starting and stopping the thread. As a bonus, it is
impossible to miss to join the thread. In other frameworks they tend
to call it the fork-join style.

So, you might consider this patch for clarity:

34,35c34,35
< struct ActiveObject : public T {
< ActiveObject() : T() {
---


> struct thread : public T {
> thread() : T() {

39c39
< ~ActiveObject() {
---
> ~thread() {
44c44
< ActiveObject(T_p1 p1) : T(p1) {
---
> thread(T_p1 p1) : T(p1) {
49c49
< ActiveObject(T_p1 p1, T_p2 p2) : T(p1, p2) {
---


> thread(T_p1 p1, T_p2 p2) : T(p1, p2) {

86c86
< ActiveObject<worker> workers[] = {
---
> thread<worker> workers[] = {

I believe that the program will be clearer and more readable if you
keep to the terminology that reflects the active object. It is not a
good idea to overload the term `thread' since it will just cause some
more confusion.

Best Regards,
Szabolcs

alexroat

unread,
Nov 25, 2008, 9:03:07 AM11/25/08
to

I'm sorry for the late answer but I've not activated the automatic
notification of the group answers.

Infact,
destructor is a statement that is called BEFORE the deallocation of
the objects on the stack and I put a pthread_join there in order to
block the termination of the main thread and maintain the program life
with the secondary thread.
I want to keep the main thread alive till the created thread has not
finished.
In this manner, the destructor is blocked on pthread_join an no
variables are erased from the stack till destructor is ended.
This design is oriented to obtain something similar to Java Threads on
C++.
The JVM is keept alive from every single thread and it does not exit
before all threads all completed.

However, this WORKS correctly and I'm not discussing about the
pthread_join, but about the behaviour of polymorphism in gcc-4.1.2.
I tried to compile the same code on 3.3.2 on ARM and it sucessfully
works as I expected showing "DERIVED" and no "PARENT" and also on the
default gcc version on an ubuntu intrepid and it works correctly too
(showing "DERIVED"). Does it means that gcc-4.1.2 of debian is
bugged ?


Many thanks.

Alessandro

0 new messages