Reputation: 7502
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
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:
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