Home > front end >  When is the vtable created/populated?
When is the vtable created/populated?

Time:08-29

I had a hard time figuring out why my code didn't work. I tracked the problem down to the vtable generation/population.

Here is a simplified version of my code:

#include <iostream>
#include <functional>
#include <thread>

using namespace std;

typedef std::function<void (void)> MyCallback;

MyCallback clb = NULL;

void callCallbackFromThread(void){
    while(clb == NULL);
    clb();
}

int someLongTask(void){
    volatile unsigned int i = 0;
    cout << "Start someLongTask" << endl;
    while(i < 100000000)
        i  ;
    cout << "End someLongTask" << endl;
    return i;
}

class Base{
public:
    Base(){
        clb = std::bind(&Base::_methodToCall, this);
        cout << "Base-Constructor" << endl;
    }
protected:
    virtual void methodToCall(void){
        cout << "Base methodToCall got called!" << endl;
    };
private:
    void _methodToCall(void){
        methodToCall();
    }
};

class Child: public Base{
public:
    Child()
     : dummy(someLongTask()){
        cout << "Child Constructor" << endl;
    }
protected:
    void methodToCall(void){
        cout << "Child methodToCall got called!" << endl;
    }
private:
    int dummy;
};

int main()
{
    thread t(callCallbackFromThread);
    Child child;
    t.join();
    clb();

    return 0;
}

When I run this, I sometimes get the result:

Base-Constructor
Start someLongTask
Child methodToCall got called!
End someLongTask
Child Constructor
Child methodToCall got called!

And sometimes this:

Base-ConstructorBase methodToCall got called!
Start someLongTask

End someLongTask
Child Constructor
Child methodToCall got called!

Can someone explain to me why the base-method is called in one case and the child-method in the other? It surely has something to do with the exact time the thread will be executed. To me, it seems, that the vtable isn't set up properly when the thread calls the callback, but I don't understand why.

CodePudding user response:

The vtable is an implementation detail of the C language. You never have to care about it if you are following the language rules and not digging into undefined behavior territory to hack around within the specific language implementation.

Your code fails because you are not using any appropriate synchronization that is required for a multi-threaded program.

clb is not an atomic variable and as a consequence you are not allowed to store to it in one thread and load from it in another without using some synchronization primitive like e.g. a mutex guarding the read and write access.

Accessing a non-atomic object in multiple threads without synchronization with at least one of them being a read is known as a data race and is unconditionally undefined behavior in C .


As pointed out by @Mat there is another race on the lifetime of the class object that the call on clb would use. A virtual function call during construction is only allowed directly or indirectly from the path through the constructor. This doesn't apply to the call in the thread and there is also no synchronization to assure that all constructors of the class object have finished before clb is called. Therefore again undefined behavior. (This is colloquially known as a data race on the vtable pointer, since that is why it fails in practice on implementations.)

  • Related