Is GoogleMock not thread safe on Linux?
Asked Answered
H

1

5

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?

Harney answered 25/6, 2017 at 19:51 Comment(3)
You have to add -pthread to the compiler command line as well.Pigeon
@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.Harney
Because that's how it works.Pigeon
P
6

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] ...
Pigeon answered 26/6, 2017 at 8:3 Comment(3)
Thank you that fixed it. What I was trying to achieve is to create some examples that show that GoogleMock is not thread safe on Windows, but is on Linux. Do you @n.m. know of an example that shows this?Harney
The CookBook link says: Using Google Mock and Threads IMPORTANT NOTE: What we describe in this recipe is ONLY true on platforms where Google Mock is thread-safe. Currently these are only platforms that support the pthreads library (this includes Linux and Mac). To make it thread-safe on other platforms we only need to implement some synchronization operations in "gtest/internal/gtest-port.h".Harney
In response to my own comment. That is what the google mock cook book currently (still) says. However, when I look at the internals of gtest-port.cc, which is also used by gmock, I see that class Mutex is implemented using ::XxxCriticalSection. So the cook book might be outdated on this topic and still refer to what was valid for version 1.7.0 and earlier.Harney

© 2022 - 2024 — McMap. All rights reserved.