Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 328
TechDragon Avatar asked Apr 20 '18 09:04

TechDragon


People also ask

How do you wake up a STD thread while it's asleep?

You can use std::promise/std::future as a simpler alternative to a bool / condition_variable / mutex in this case. A future is not susceptible to spurious wakes and doesn't require a mutex for synchronisation. Save this answer.

How does sleep work in c++?

C++ language does not provide a sleep function of its own. However, the operating system's specific files like unistd. h for Linux and Windows. h for Windows provide this functionality.

What does std :: thread do?

std::thread Threads allow multiple functions to execute concurrently. std::thread objects may also be in the state that does not represent any thread (after default construction, move from, detach, or join), and a thread of execution may not be associated with any thread objects (after detach).


2 Answers

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.

like image 121
Maxim Egorushkin Avatar answered Nov 15 '22 14:11

Maxim Egorushkin


A great article on starting threads from the constructor

https://rafalcieslak.wordpress.com/2014/05/16/c11-stdthreads-managed-by-a-designated-class/

like image 24
TechDragon Avatar answered Nov 15 '22 16:11

TechDragon