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
> 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
[...]
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_!
:^/
>>> 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
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...
> 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
> 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
> > 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
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?
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
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