简体   繁体   中英

Virtual function and boost bind strange behavior

I saw a strange behavior in a piece of code I wrote under Linux, and I would like to share it to see if someone knows the cause of that. 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 that method, with the same signature. Then I used boost bind to start a thread. Here's a sample code:

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

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

In the Init method of the Derived class I did this:

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

The DoSomething method of the base class did what it was meant to do, while the same method of the derived class was an empty method, left there by mistake. Now, while running the code above, most of the times the DoSomething of the Base class was executed in the thread, so the application worked fine, while sometimes it did not work. After some debug I noted the mistake above, and removing the DoSomething of the derived class solved the issue. Using Eclipse in debug mode it seems that the DoSomething method of the derived class was always called, while running the application from the console worked most of the times, but not always. Is there a reason for this behavior? I mean, why sometimes the bind function used the base class method, and sometimes the same method of the derived class?

Thanks in advance

Edit in response to @pmr

It would be hard to show a full working example, I will try to show a bit how the classes are used.

First I instantiate a Derived object, then in the init function I start the thread with the initialization code shown above. The DoSomething has a while loop that iterates on a vector, but that's not the point 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 did not see any processing, which instead took place in the Base DoSomething function.

Here is a wild guess: the object you used to start the thread actually was destroyed! Since the binding of virtual functions changes during destruction (when object is destroyed all virtual functions resolve as if the object used be a type of the class currently being destroyed). To do this, the "vtable pointer" is typically reset to point to a suitable "virtual function table". Once the base is destroyed there isn't any need to further destroy the object.

This would fit in nicely with your explanation of the behavior being random: sometimes the parent thread executed fast enough to have reached the base class constructor, sometimes it didn't. When compiling with debug mode the parent thread apparently consistently took to long before destroying the object. Your statement that everything worked just fine in many cases doesn't really destroy that image either: often buggy code looks as if it works although it actually shows erradic behavior when being inspected more closely.

I guess I've found out the reason of this behavior: at first I called the thread constructor inside the Base class constructor. I think this is the problem, because since the base constructor was called before the derived one, sometimes the vtable was created pointing to the empty derived function, sometimes the thread was started before the vtable was created, thus the bind function used the base method, which did what it was meant for. I guess that using debug some delays were introduced, so using the debugger the thread was always bound to the derived class method, causing a wrong behavior. Also, I tried moving the thread creation inside the init function, and in that way the derived function is always called.

We faced same problem in our internal OS, we bind some virtual function to another worker thread when object constructed. If OS switch to worker thread before we have done derive class's construct function, worker will call "this" with base class type. So, I think we can describe it : "this" pointer is not thread safe in constructor and De-constructor.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM