Sleep affecting which virtual member function is called by std::thread?

  • A+
Category:Languages

I am unsure if this is expected behavior in c++11. Here is an example of what I found.

#include <iostream> #include <thread> using namespace std;  class A { public:     virtual void a() = 0;     thread t;     A() : t(&A::a, this) {}     virtual ~A() {         t.join();     } };  class B : public A { public:     virtual void a() {         cout << "B::a" << endl;     } };  int main() {     B b;     this_thread::sleep_for(chrono::seconds(1)); } 

When compiled and run

$ g++ -std=c++11 -pthread test.cpp -o test $ ./test B::a $ 

But when the sleep is removed...

int main() {     B b;     //this_thread::sleep_for(chrono::seconds(1)); } 

something strange happens

$ g++ -std=c++11 -pthread test.cpp -o test $ ./test pure virtual method called terminate called without an active exception Aborted (core dumped) $ 

Could this be a bug?


When in constructor and destructor virtual functions behave differently. In As constructor B has not initialised yet, this is why Bs virtual function cannot possibly be called yet. See virtual:

When a virtual function is called directly or indirectly from a constructor or from a destructor (including during the construction or destruction of the class’s non-static data members, e.g. in a member initializer list), and the object to which the call applies is the object under construction or destruction, the function called is the final overrider in the constructor’s or destructor’s class and not one overriding it in a more-derived class. In other words, during construction or destruction, the more-derived classes do not exist.

So, that member function pointer &A::a gets resolved in that other thread when it calls it. This call resolution uses the virtual table because &A::a is a pointer to a virtual member function. The first thing that a constructor does is it sets the virtual table pointer to the virtual table of the class. If Bs constructor has been entered by the time (this->&A::a)() is called, then it calls B::a. There is a race condition between that new thread invoking (this->&A::a)() and the current thread executing A and B constructors.

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: