ssb
ssb

Reputation: 7502

C++ Inheritance : Calling virtual method when it has been overridden

I am trying to build a service object which can run (i.e. execute it's run() function) in a separate thread. This is the service object

#include <boost/noncopyable.hpp>
#include <atomic>
#include <thread>
#include <iostream>

class service : public boost::noncopyable {
 public:
  service() : stop_(false), started_(false) { }

  virtual ~service() {
    stop();
    if (thread_.joinable()) {
      thread_.join();
    }
  }

  virtual void stop() { stop_ = true; }

  virtual void start() {
    if (started_.load() == false) {
      started_ = true;
      thread_ = std::thread([&] () {
        run();
      });
    }
  }

 protected:
  virtual void run() = 0;

  std::atomic<bool> stop_;

  std::atomic<bool> started_;

  std::thread thread_;
};

I am the creating a test class which inherits from this abstract class and is called in the main() function

class test : public service {
 public:
  test() : service() {
    std::cout<< "CTOR" << std::endl;
    start();
  }

  ~test() {
    std::cout<< "DTOR" << std::endl;
  }

 protected:
  void run() override {
    std::cout << "HELLO WORLD" <<std::endl;
  }
};


int main() {
  test test1;
  return 0;
}

Now when I execute this, why do I get an error saying pure virtual function called? The run() function is clearly overridden in the test class. Whats worse is that it runs correctly sometimes?

$ ./a.out
CTOR
DTOR
pure virtual method called
terminate called without an active exception

$ ./a.out
CTOR
DTOR
pure virtual method called
terminate called without an active exception

$ ./a.out
CTOR
DTOR
pure virtual method called
terminate called without an active exception

$ ./a.out
CTOR
DTOR
HELLO WORLD

$ ./a.out
CTOR
DTOR
pure virtual method called
terminate called without an active exception

What could be going wrong here?

Upvotes: 7

Views: 1046

Answers (1)

Sam Varshavchik
Sam Varshavchik

Reputation: 118340

Follow along, step by step:

1) You construct the object.

2) You execute the following piece of code:

if (started_.load() == false) {
  started_ = true;
  thread_ = std::thread([&] () {
    run();
  });
}

The parent thread immediately returns to main() where it immediately exits and destroys your object.

Here's your bug:

  • You are not guaranteed that the thread started in start() is going to reach the call to run(), above, before the parent thread terminates the process. Both the child thread, and the parent thread runs concurrently.

So, every once in a while, the parent thread will destroy the object before the child thread gets in gear, and calls run().

At this point, the object whose run() method gets invoked, is already destroyed.

Undefined behavior.

The assertion you're hitting, every once in a while, is one possible result of this undefined behavior.

Upvotes: 10

Related Questions