5

duplicate of: "pure virtual method called" when implementing a boost::thread wrapper interface

I am trying to create a more object oriented version of the threads using boost threads.

So I created a Thread class:

class Thread {
public:
    Thread() {}
    virtual ~Thread() { thisThread->join(); }

    void start() { thisThread = new boost::thread(&Thread::run, this); }

    virtual void run() {};

private:
    boost::thread *thisThread;
};

this class creates the thread in start() like this:

thisThread = new boost::thread(&Thread::run, this);

The problem is that when I create a class that overwrites the run() method, the run() method from Thread is call by the thread instead of the new run() method

for example I have a class that extends Thread:

class CmdWorker: public Thread {
public:
    CmdWorker() : Thread() {}
    virtual ~CmdWorker() {}

    void run() { /* deosn't get called by the thread */ }
};

when I do

Thread *thread = new CmdWorker();
thread.start(); //---> calls run() from Thread instead of run() from CmdWorker

but just to be more clear:

thread.run();  calls the correct run from CmdWorker, (run() is virtual from Runnable)

Any idea why this happens or how it can be fixed ?

NOTE: I created a function (that has nothing to do with the Thread class)

void callRun(Thread* thread) {
    thread->run();
}

and changed the thread creation to:

thisThread = new boost::thread(callRun, this);

when debugging I noticed that the thread pointer is pointing to a object of type Thread instead of CmdWorker

EDIT:

testcase code at: http://ideone.com/fqMLF and http://ideone.com/Tmva1

Object seems to be sliced (but this is strange since pointers are used)

didn't manage to add boost to it

Community
  • 1
  • 1
  • The question is unclear to me! – Nawaz Sep 12 '11 at 18:02
  • 1
    Not enough code. I fail to see how you would use such construct. – K-ballo Sep 12 '11 at 18:04
  • 2
    FYI `std::thread` is arriving. What exactly is wrong with `boost::thread` anyway? – spraff Sep 12 '11 at 18:05
  • Should `run()` be virtual in Thread? – Peter Lawrey Sep 12 '11 at 18:06
  • I added more code to make it clear what happens –  Sep 12 '11 at 18:09
  • try to remove the declaration of `run` from `Thread` and see where you have a "cannot instantiate abstract class blah blah". – Alexandre C. Sep 12 '11 at 18:09
  • 3
    Do you happen to call `start` in the constructor ? – Alexandre C. Sep 12 '11 at 18:10
  • @Alexandre C: no I do not call start() in the constructor, and when I remove the run declaration It just crashes pure virtual call error –  Sep 12 '11 at 18:17
  • @Hallowed: Okay. Can you post a minimal complete compilable example of the behavior you observe, for instance at http://www.ideone.com (they have boost::thread IIRC) ? – Alexandre C. Sep 12 '11 at 18:20
  • @Alexandre C: can I compile it there since I use boost ? –  Sep 12 '11 at 18:22
  • @Hallowed: yes, there is some boost support at ideone. The problem is likely to be not in the code you shown. Either the `Thread` object got sliced somewhere, or the constructor is (indirectly) calling `start`. The fact that the program crashes (with something in the output like "pure virtual function called") when you remove `run` from `Thread` indicates this. – Alexandre C. Sep 12 '11 at 18:24
  • @Alexandre C: I've added some test code, and it seems that object is sliced –  Sep 12 '11 at 18:36
  • 1
    @Hallowed: Ok, I managed to get it compile on my machine, and surprisingly, I observe your behavior. Let me get to it. – Alexandre C. Sep 12 '11 at 18:42

4 Answers4

3

The answer is in that question:

"pure virtual method called" when implementing a boost::thread wrapper interface

Basically, when the boost::thread object begins running, the object it was run against had the time to be deleted.

You have to implement a join method that you call manually before destroying the object.

Community
  • 1
  • 1
Alexandre C.
  • 55,948
  • 11
  • 128
  • 197
2

when debugging I noticed that the thread pointer is pointing to a object of type Thread instead of CmdWorker

Maybe the CmdWorker object is sliced (i.e. copied by value) into a Thread object somewhere in your code?

Do you get the same behaviour with a minimal test case?

Macke
  • 24,812
  • 7
  • 82
  • 118
1

By doing &Thread::Run on a non-virtual function, you are forcing any class that derives from Thread to use the function specified in the Thread base class. Try making Thread::Run a virtual void and see if that fixes your issue.

Michael
  • 701
  • 4
  • 15
  • 3
    @Michael : In C++, `virtual` is inherited from the base class as long as the member function signatures match (which they do in this case); declaring `Thread::run` as `virtual` would be redundant. – ildjarn Sep 12 '11 at 18:44
1

From reading your updates, you're calling delete in the main thread, while the thread is starting in the other. Depending on the race between the destructor and the invocation of run, it will either:

  1. Crash before it starts, because the vtable is completely destroyed
  2. Call the Thread::run (which is pure virtual, and crashes with a pure virtual thunk)
  3. Call the correct function, which is the derived class run()

If you add a call to sleep(1) after you call start, but before you call delete, you'll find that it works as you expect.

Dave S
  • 20,507
  • 3
  • 48
  • 68
  • I simplified the example and removed any locks + checks. also in the destructor I do a join() so it waits for the thread to finish. as I mentioned before the run() from thread is called. –  Sep 12 '11 at 19:00
  • 3
    @Ha11owed If you add a join in the destructor of `Thread`, then by the time join is called, the most derived class has already been destroyed, and the vtable entries for it no longer exist. – Dave S Sep 12 '11 at 19:02
  • I understand now didn't notice that –  Sep 12 '11 at 19:04