Search code examples
c++multithreadingvirtualgoogletest

googletest: coredump when testing a derived class whose base class has pure virtual methods


I have an abstract base class that has the logic that handles the lifecycle of a thread (start, stop, join). The work executed in the thread depends on the derived class that is instantiated, and there are several derived classes.

The base class looks like this:

class Base {
public:
    Base(int i, bool b) : i{i}, b{b}
    {
        start();
    }

    virtual bool getB() { return b; };
    void stop() { stopWorking = true; workerTh.join(); };

protected:
    virtual void start()
    {
        std::cout << "Base start method" << std::endl;
        workerTh = std::thread{ std::bind(&Derived::work, this) };
    };
    virtual void work() = 0;

    std::thread workerTh;
    int i;
    bool b;

    bool stopWorking = false;
};

A derived class looks like this:

class Derived : public Base {
public:
    Derived(int i, bool b) : Base(i,b) {};

protected:
    void work()
    {
        std::cout << "Derived Work started!" << std::endl;
        while (not stopWorking)
        {
            std::this_thread::sleep_for(std::chrono::milliseconds(1000));
        }
        std::cout << "Derived Work ended!" << std::endl;
    }
};

It runs as expected in a regular program like:

int main()
{ 
    std::cout << "Starting" << std::endl;
    Derived d { 10, false};
    std::this_thread::sleep_for(std::chrono::milliseconds(2000));
    std::cout << "Finishing" << std::endl;
    d.stop();
    std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}

But if I execute it in a google test then I get a coredump and the cause is that I'm invoking a pure virtual method in this line:

workerTh = std::thread{ std::bind(&Base::work, this) };

So, why does that line work as expected in a normal program, but crashes in a google test? Am I missing something when launching GTest?

This is the test:

#include "BaseDerived.cc"
#include "gtest/gtest.h"

TEST(BaseDerivedWithThreadTest, FailsCoredumpCallingPureVirtual) {
    Derived d { 10, false };
    ASSERT_FALSE (d.getB());
}

int main(int argc, char **argv) {
    ::testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}

[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from BaseDerivedWithThreadTest
[ RUN      ] BaseDerivedWithThreadTest.FailsCoredumpCallingPureVirtual
Base start method
terminate called without an active exception
pure virtual method called
terminate called recursively
Aborted (core dumped)

I can overcome this issue by putting the thread lifecycle code down to the derived classes, but I find it very ugly just to make googletest happy.

Any ideas?

Thanks!


Solution

  • Your code is unfortunately broken: In the baseclass constructor, you call start(), which is virtual. This will never invoke a derived class' start() method, because the dynamic type at the time of call in the constructor is not of the derived class. The same applies to the virtual work() function. Worse, depending on when the thread is scheduled, it may or may not invoke the derived class' function, because its type may have changed to the derived class' type by that time.

    The test failures are just a red herring perhaps, your code is broken. Fix that first and then see if the tests still fail.