Virtual function and amplification bind strange behavior

I saw strange behavior in a piece of code that I wrote on Linux, and would like to share it to see if anyone knows about this. I had a base class and a derived class. In the base class, I defined a virtual method, and in the derived class I redefined this method with the same signature. Then I used boost bind to start the stream. Here is an example code:

Class Base { public: virtual void DoSomething(); virtual void Init() = 0; ... } Class Derived : public Base { public: void DoSomething(); void Init(); ... } 

In the Derived method's Init method, I did the following:

  boost::thread *t = new boost::thread(boost::bind(&Base::DoSomething, this)); 

The DoSomething method of the base class did what it was supposed to do, while the same method of the derived class was an empty method, left there by mistake. Now, during the execution of the above code, most of the time when DoSomething from the base class was executed in a thread, the application worked fine, and sometimes it did not work. After some debugging, I noticed the error above, and removing DoSomething from the derived class solved the problem. Using Eclipse in debug mode it seems that the DoSomething method of the derived class has always been called, while the application from the console has been working most of the time, but not always. Is there a reason for this behavior? I mean, why sometimes the binding function used the base class method, and sometimes the same derived class method?

Thank you in advance

Edit in response to @pmr

It would be difficult to show a complete working example, I will try to show a little how the classes are used.

First I create a Derived object, then in the init function I start the thread with the initialization code shown above. DoSomething has a while loop that iterates over a vector, but that is not what I think.

 void Derived::Init() { ... boost::thread *t = new boost::thread(boost::bind(&Base::DoSomething, this)); } void Base::DoSomething() { while(true) { ... } } void Derived::DoSomething() { } 

As you can see in this code, the Derived DoSomething method was empty, so sometimes I didn’t see any processing that was performed in the Base DoSomething function instead.

+4
source share
3 answers

I suppose I figured out the reason for this behavior: first I called the stream constructor inside the base class constructor. I think this is a problem because, because the base constructor was called before the derivative, sometimes a vtable was created that pointed to an empty derived function, sometimes the thread started before the vtable was created, so the binding function used the base method, which did what it was intended for . I assume that some delays were introduced using debugging, so using a debugger, the thread was always bound to the derived class method, causing incorrect behavior. In addition, I tried to move the creation of the stream inside the init function, and thus the derived function is always called.

+2
source

Here's a wild guess: the object you used to start the stream was actually destroyed! Since the binding of virtual functions changes during destruction (when an object is destroyed, all virtual functions decide as if the object used is the type of class that is being destroyed). For this, the "vtable pointer" usually reset points to a suitable "virtual function table". After the destruction of the base there is no need to further destroy the object.

This fits nicely with your explanation of randomness: sometimes the parent thread runs fast enough to reach the base class constructor, sometimes it doesn't. When compiling with debug mode, the parent thread apparently sequentially occupied long before the destruction of the object. Your expression that everything works very well in many cases does not actually destroy this image: often the error code looks as if it works, although in fact it exhibits eradistic behavior upon closer inspection.

+4
source

We faced the same problem in our internal OS, we bind some virtual function to another workflow when building the object. If the OS switches to the workflow before we get the function to build the class, the worker will call "this" with the base type of the class. So, I can describe this: the "this" pointer is not thread safe in the constructor and de-constructor.

0
source

Source: https://habr.com/ru/post/1392237/


All Articles