3
votes

I started with testing GoogleMock (1.8.0 release) on Windows. I wanted to show an example that it is not thread safe. After proving that successfully, I wanted to show that the same test runs fine on Linux. That failed however. That did not match my expectation. Since the GoogleMock documentation says that it is, or should be, thread safe on systems with pthreads, it should be thread safe on Linux. I did have to add -pthread to the linker command line to build the executable. That means that GoogleMock or GoogleTest does use pthreads.

This is the code I use for testing:

#include <thread>
#include <vector>

#include "gmock/gmock.h"

class Dummy
{
public:
    virtual void SomeMethod(int) {}
};

class DummyMock : public Dummy
{
public:
    MOCK_METHOD1(SomeMethod, void(int));
};

using ::testing::Exactly;

constexpr static int nrCallsPerThread = 100 * 1000;
constexpr static int nrThreads = 10;

TEST(SomeTest, Test100)
{
    DummyMock dummy;

    std::vector<std::thread> threads;
    for (int i = 0; i < nrThreads; i++)
    {
        EXPECT_CALL(dummy, SomeMethod(i)).Times(Exactly(nrCallsPerThread));
        threads.emplace_back([&dummy, i]
        {
            for (int j = 0; j < nrCallsPerThread; j++)
            {
                dummy.SomeMethod(i);
            }
        });
    }

    for (auto& t: threads)
    {
        t.join();
    }
}

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

The problem is, on Linux, not exposed every execution. But running the executable with --gtest_repeat=100 has a near 100% hit rate.

On Windows, using Visual Studio 2015, if get a Debug Assertion Failed! message with Expression: vector iterator not decrementable.

On Linux, Ubuntu 17.04, when I run the Debug build from the command line I get [ FATAL ] ../googletest-release-1.8.0/googletest/include/gtest/internal/gtest-port.h:1928:: pthread_mutex_lock(&mutex_)failed with error 22.

When running in a debugger on Linux, the program is (often) interrupted at gtest-port.h line 1100, which is line 2 in this callstack:

0 0x5555555a6633 testing::Cardinality::ConservativeUpperBound() const () (??:??)
1 0x5555555a1a13 testing::internal::ExpectationBase::CheckActionCountIfNotDone() const () (??:??)
2 0x555555563f98 testing::internal::TypedExpectation<void (int)>::ShouldHandleArguments(std::tuple<int> const&) const(this=0x5555557f58a0, args=std::tuple containing = {...}) (../googletest-release-1.8.0/googlemock/include/gmock/gmock-spec-builders.h:1100)
3 0x55555556397d testing::internal::FunctionMockerBase<void (int)>::FindMatchingExpectationLocked(std::tuple<int> const&) const(this=0x7fffffffde38, args=std::tuple containing = {...}) (../googletest-release-1.8.0/googlemock/include/gmock/gmock-spec-builders.h:1723)
4 0x555555563578 testing::internal::FunctionMockerBase<void (int)>::UntypedFindMatchingExpectation(void const*, void const**, bool*, std::ostream*, std::ostream*)(this=0x7fffffffde38, untyped_args=0x7fffde7fbe14, untyped_action=0x7fffde7fb7d0, is_excessive=0x7fffde7fb7c7, what=0x7fffde7fb900, why=0x7fffde7fba90) (../googletest-release-1.8.0/googlemock/include/gmock/gmock-spec-builders.h:1687)
5 0x5555555a265e testing::internal::UntypedFunctionMockerBase::UntypedInvokeWith(void const*) () (??:??)
6 0x55555555fcba testing::internal::FunctionMockerBase<void (int)>::InvokeWith(std::tuple<int> const&)(this=0x7fffffffde38, args=std::tuple containing = {...}) (../googletest-release-1.8.0/googlemock/include/gmock/gmock-spec-builders.h:1585)
7 0x55555555f16c testing::internal::FunctionMocker<void (int)>::Invoke(int)(this=0x7fffffffde38, a1=1) (../googletest-release-1.8.0/googlemock/include/gmock/gmock-generated-function-mockers.h:101)
8 0x55555555ecb6 DummyMock::SomeMethod(this=0x7fffffffde30, gmock_a1=1) (/home/jos/Programming/ThreadSafeGMock/main.cpp:16)
9 0x55555555d31e SomeTest_Test100_Test::<lambda()>::operator()(void) const(__closure=0x5555557f5478) (/home/jos/Programming/ThreadSafeGMock/main.cpp:36)
10 0x55555555de98 std::_Bind_simple<SomeTest_Test100_Test::TestBody()::<lambda()>()>::_M_invoke<>(std::_Index_tuple<>)(this=0x5555557f5478) (/usr/include/c++/6/functional:1391)
11 0x55555555de22 std::_Bind_simple<SomeTest_Test100_Test::TestBody()::<lambda()>()>::operator()(void)(this=0x5555557f5478) (/usr/include/c++/6/functional:1380)
12 0x55555555ddf2 std::thread::_State_impl<std::_Bind_simple<SomeTest_Test100_Test::TestBody()::<lambda()>()> >::_M_run(void)(this=0x5555557f5470) (/usr/include/c++/6/thread:197)
13 0x7ffff7b0a83f ??() (/usr/lib/x86_64-linux-gnu/libstdc++.so.6:??)
14 0x7ffff76216da start_thread(arg=0x7fffde7fc700) (pthread_create.c:456)
15 0x7ffff735b17f clone() (../sysdeps/unix/sysv/linux/x86_64/clone.S:105)

Since it should be thread safe, I suspect that I am doing something wrong. But I do not see what. Or did I hit a bug in GoogleTest or GoogleMock?

1
You have to add -pthread to the compiler command line as well.n. 1.8e9-where's-my-share m.
@n.m. Why? The compiler, or more specific the pre-processor, gets to include the header for pthreads based on conditional compilation in the googlemock and googletest headers.jokr

1 Answers

5
votes

From the fine manual:

Important note: Google Mock requires expectations to be set before the mock functions are called, otherwise the behavior is undefined. In particular, you mustn't interleave EXPECT_CALL()s and calls to the mock functions.

Your original code fails on my system (cygwin) intermittently with error 22 or sometimes no message/error code whatsoever. This modification works flawlessly:

for (int i = 0; i < nrThreads; i++)
{
    EXPECT_CALL(dummy, SomeMethod(i)).Times(Exactly(nrCallsPerThread));
}

std::vector<std::thread> threads;
for (int i = 0; i < nrThreads; i++)
{
    threads.emplace_back([&dummy, i] ...