Polymorphism, pthreads, multi-threading, and libstdc++

S

Savagesmc

Problem Background:
I am working on a thin wrapper for the pthreads interface on linux, and have
encountered frustrating behavior. The idea is to have a "PosixThread" class
that any class can inherit from. A child then only needs to define a
virtual "executive" method that has the thread code for the child object,
and the threading setup and management are automatic then. The thread
creation takes place in the PosixThread base class with a pthread_create
call that gets the address of a static member function that calls the
'executive' function, i.e. the polymorphic executive. The parameter passed
to the static member function is the 'this' pointer which points to the
object being created.

Problem Statement:
The problem I am experiencing is that in a fairly new distro of linux
(mandrake 9.1) calling pthread_create from the base class's constructor
(with this as a parameter) results in a 'pure virtual method called'
run-time error. However, in an older linux distro (redhat 7.3) everything
works as expected.

I suspect that using the 'this' pointer in the Base class's constructor is
probably not something that should be done, with the standard saying if you
do this, you get some kind of undefined behavior. However, I am not a
super-guru yet, therefore the posting here asking for either confirmation or
an explanation.

I've enclosed a sample program that shows both correct and incorrect
behavior. In the distro that gives me errors, if I wait until after the
object has been constructed and call pthread_create from some other method
(like 'thread.start()') then everything works ok, as the this pointer now
has a good virtual method table or something.

The command line command I use to compile this program is:
g++ -o junk junk.cpp -lpthread

It builds in linux or in cygwin. Does not work right in my version of
cygwin either, except no 'pure virtual method called' run-time error.

Thanks in advance for all the help.

Steve


#include <iostream>
#include <iomanip>
#include <pthread.h>
#include <unistd.h>

using namespace std;

class BaseThread {
public:
BaseThread() : started(false) { start(); }
virtual ~BaseThread() { }
void start() {
if (!started) {
started=true;
pthread_create(&id, NULL, BaseThread::hook, this);
}
}
private:
BaseThread& operator=(const BaseThread& val) { }
bool started;
pthread_t id;
virtual void* execute() = 0;
static void* hook(void* b) {
BaseThread* base = (BaseThread*)b;
return base->execute();
}
};

class Thread : virtual public BaseThread {
public:
Thread(bool start=false) : BaseThread() {
// if(start) this->start();
}
virtual ~Thread() { }
private:
Thread& operator=(const Thread& val) { }
virtual void* execute() {
cout << "Child::run" << endl;
return (void*)0;
}
};

class OtherBaseThread {
public:
OtherBaseThread() : started(false) { }
virtual ~OtherBaseThread() { }
void start() {
if (!started) {
started=true;
pthread_create(&id, NULL, OtherBaseThread::hook, this);
}
}
private:
OtherBaseThread& operator=(const OtherBaseThread& val) { }
bool started;
pthread_t id;
virtual void* execute() = 0;
static void* hook(void* b) {
OtherBaseThread* base = (OtherBaseThread*)b;
return base->execute();
}
};

class OtherThread : public OtherBaseThread {
public:
OtherThread(bool start=true) : OtherBaseThread() {
if(start) this->start();
}
virtual ~OtherThread() { }
private:
OtherThread& operator=(const OtherThread& val) { }
virtual void* execute() {
cout << "Child::run" << endl;
return (void*)0;
}
};

int main() {
OtherThread otherThread;
sleep(1);
Thread thread;
sleep(1);
return -1;
}
 
N

NFish

Savagesmc wrote:

[...]
Problem Statement:
The problem I am experiencing is that in a fairly new distro of linux
(mandrake 9.1) calling pthread_create from the base class's constructor
(with this as a parameter) results in a 'pure virtual method called'
run-time error. However, in an older linux distro (redhat 7.3) everything
works as expected.
[...]

Virtual methods lose their virtual-ness property in a constructor (and a
destructor). The rationale is that the derived part of the object is
not fully constructed yet.

You'll need to call the "executive" function after the constructor is
completed.
 
S

SavageSMC

NFish said:
Virtual methods lose their virtual-ness property in a constructor (and a
destructor). The rationale is that the derived part of the object is
not fully constructed yet.

You'll need to call the "executive" function after the constructor is
completed.
First of all, thank you for taking the time to take a look at my problem. I
know
it probably isn't the easiest code to look at.

However, I do not beleive your statement above is correct, at least for the
destructor
part. Virtual methods must work in a destructor, as all good programming
(Meyers, etc...) books say to always make the destructor itself virtual.

That said, I don't think the issue here is a problem with virtual methods,
it's a problem
with the 'this' pointer. It seems to be a pointer to an object of the base
class's type during
the base class's constructor (which is called from the derived class's
constructor.) This
is also confirmed by the fact that calling pthread create in the derived
class's
CONSTRUCTOR (rather than the base class's constructor) does indeed work.
This doesn't seem like the correct behavior. The 'this' pointer should
always be to the
correct type, shouldn't it?

One of the best c++ programmers I know uses this same exact technique on the
g++,
MS C++, and Borland compilers. Until this Mandrake 9.1 build I am using, it
has worked on all of them - including g++.

My theory, the more I think about it, is that something is broken in the g++
I am using. -
Unless some smart person on here can convince me otherwise.
 
S

Savagesmc

I consulted with my expert here, and it turns out that the 'this' pointer
points to the base class's VMT during the base constructor part of the
derived constructor. The VMT is not reliably set to the correct table until
after the derived constructor has finished. Therefore the pthread_create
must defer use of the 'this' pointer until after construction. There are a
number of ways this can be accomplished, particularly with the use of
semaphores.

Thank you for your help.
 

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

Forum statistics

Threads
473,995
Messages
2,570,225
Members
46,815
Latest member
treekmostly22

Latest Threads

Top