Question on C++ Thread Class and inheritance/polymorphism with POSIXpthread

A

alexroat

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
 
I

Ian Collins

alexroat said:
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.

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

Ian Collins

Chris said:
Ian Collins said:
alexroat wrote:

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?
 
C

Chris M. Thomasson

Ian Collins said:
Have a look at boost threads, the template approach is more flexible.


Style comment: drop the superfluous comments!


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_!

:^/
 
C

Chris M. Thomasson

Ian Collins said:
Chris said:
Ian Collins said:
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?

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

JoelKatz

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
 
J

JoelKatz

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
 
J

James Kanze

alexroat wrote:
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.

[...]
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.
 
C

Chris M. Thomasson

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)::eek:n_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?
 
S

Szabolcs Ferenczi

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)::eek:n_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?

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

alexroat

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

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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,776
Messages
2,569,603
Members
45,189
Latest member
CryptoTaxSoftware

Latest Threads

Top