Problem with threads and virtual functions

I am coding a class that uses SDL to create and control multiple threads which
call a virtual member in this class.

Each thread is created via a static member of mtclass, with ‘this’ passed as
the object’s pointer. When the thread is allowed to run it calls the
pointer’s virtual member. No matter what I do it calls the BASE member, not
the overloaded one!

I wish I could give a shorter example, but whenever I try and make one the
problem does not occur… very fruistrating. Here is my attached code:
-------------- next part --------------
A non-text attachment was scrubbed…
Name: lump.cpp
Type: text/x-c++src
Size: 3198 bytes
Desc: not available
URL: http://lists.libsdl.org/pipermail/sdl-libsdl.org/attachments/20031117/fde379ce/attachment.cpp

Each thread is created via a static member of mtclass, with ‘this’ passed as
the object’s pointer. When the thread is allowed to run it calls the
pointer’s virtual member. No matter what I do it calls the BASE member, not
the overloaded one!

mtclass::mtclass(int threads_in)
: l_mutex(NULL),l_cond(NULL),t_done(NULL),threads(0),running(0)
{

for(n=0; n<threads_in; n++)
{
SDL_Thread *t=SDL_CreateThread(threadroot,this);
if(t!=NULL) threads++;
}
}

You’re creating the thread during the base class ctor. In there, the object
is still the base class. The base class ctor can not access the derived
class virtuals–they aren’t there yet.

Try this:

struct parent {
parent() { printf(“parent ctor():\n”); func(); }
virtual ~parent() {}
virtual void func() { printf(“A\n”); }
};

struct child: public parent {
child() { printf(“child ctor():\n”); func(); }
virtual void func() { printf(“B\n”); }
};

main() { child x; }

./a.out

parent ctor():
A
child ctor():
B

For more information, I’d suggest:

http://www.parashift.com/c++-faq-lite/

which explains this behavior and rationale. (It’s off-topic here; this
behavior isn’t SDL’s fault. :)On Mon, Nov 17, 2003 at 02:29:26PM -0600, Tyler Montbriand wrote:


Glenn Maynard

You’re creating the thread during the base class ctor. In there, the
object is still the base class. The base class ctor can not access the
derived class virtuals–they aren’t there yet.
Thanks for the reply! :slight_smile:

I googled for a couple hours on this and tried that too - no luck. :frowning: I
should have left that code in, but I trimmed it out to make my code smaller…
it’s pretty big for posting as is.

I could understand that problem happening if I was trying to call derived
class virtuals IN THE CONSTRUCTOR - but I’m not. I’m just creating
threads… the pointer is not dereferenced by the thread until long after
the constructor returns. Indeed, the semaphore is there to guarantee that.

Hm. Are virtual tables for stack variables created in stack space? That
could explain it - when the thread gets created, it would get a copy of the
stack with no virtual table in it yet. Except that still wouldn’t explain
why it doesn’t work when I create the threads after the constructor returns…
perhaps it’s a bug in g++. Anyway, I’ve changed it from a virtual function
to a function pointer, that’s working much better.

(It’s off-topic here; this behavior isn’t SDL’s fault. :slight_smile:
I thought I might have overlooked something obvious about how threads work, or
some peculiarity about SDL threads. Oh well.On Monday 17 November 2003 03:16 pm, Glenn Maynard wrote:

I’m posting this one on the list since the answer to his question is
somewhat more obscure than it looked. I’ll take any further replies
off-list.

(Oops, posted with the wrong address …)On Mon, Nov 17, 2003 at 05:56:21PM -0600, Tyler Montbriand wrote:

I’m just creating
threads… the pointer is not dereferenced by the thread until long after
the constructor returns. Indeed, the semaphore is there to guarantee that.

I looked at this a bit more. The real problem is the other part of
the same issue: when you get to the dtor of a parent class, the derived
class no longer exists–it’s reverted to the underived state it was in
when the ctor was called.

So, you have a race condition: any ThreadFuncs called after mtclass::Go
calls Launch and before main() returns (and calls testthread::~testthread)
will call the derived class. Otherwise, testthread no longer exists; your
"t" object is only an mtclass, and the rest of the ThreadFunc calls that
are being flushed while mtclass::~mtclass blocks on the mutex will call
mtclass::ThreadFunc.

I was seeing two Woos and a Narf, myself; probably because I’m running
Linux 2.6, which has a much better tuned scheduler.


Glenn Maynard