I found this problem in my job, to reproduce that problem, the simplified model is as follows:
int echo()
{
return 0;
}
TEST(ProcStubTest, test1)
{
MOCKER(echo)
.stubs()
.will(invoke(+[]{
return 1;
}));
std::vector<std::thread> thds;
for ( int i = 0; i < 4; i++ )
{
thds.emplace_back([]{
for ( int i = 0; i < 100; i++ )
{
EXPECT_EQ(1, echo());
}
});
}
for ( auto&& t : thds )
{
t.join();
}
GlobalMockObject::verify();
}
problem:
when invoke the function be mocked concurrently,the operator= of result (member of ProcStub object) will be invoked concurrently, however, it's not thread safe, may cause double free/accessing freed memory etc..
In order to test my conclusion, I add a mutex for ProcStub as follows:
MOCKCPP_NS_START
template <typename F>
struct ThreadSafeProcStub;
#define THREAD_SAFE_PROC_STUB(n) \
template <typename R DECL_TEMPLATE_ARGS(n)> \
struct ThreadSafeProcStub<R(DECL_ARGS(n))> : public ProcStubBase \
{ \
public: \
typedef R (*Func)(DECL_ARGS(n)); \
ThreadSafeProcStub(Func f, std::string name) : ProcStubBase(name, (void*)f), func(f){}\
Any& invoke(const Invocation& inv) \
{ \
SIMPLE_REPEAT(n, MOCKCPP_CHECK_AND_ASSIGN_PARAMETER); \
Any tmp = func(DECL_PARAMS(n)); \
std::lock_guard<std::mutex> lck{mtx}; \
results.emplace_back(tmp); \
return results.back(); \
} \
private: \
Func func; \
std::mutex mtx; \
std::list<Any> results; \
}; \
template <typename R DECL_TEMPLATE_ARGS(n)> \
Stub* invoke_thread_safe(R(*f)(DECL_ARGS(n)), const char* name = 0) \
{ \
return new ThreadSafeProcStub<R(DECL_ARGS(n))>(f, name ? name : ""); \
}
THREAD_SAFE_PROC_STUB(0);
THREAD_SAFE_PROC_STUB(1);
THREAD_SAFE_PROC_STUB(2);
THREAD_SAFE_PROC_STUB(3);
THREAD_SAFE_PROC_STUB(4);
THREAD_SAFE_PROC_STUB(5);
THREAD_SAFE_PROC_STUB(6);
THREAD_SAFE_PROC_STUB(7);
THREAD_SAFE_PROC_STUB(8);
THREAD_SAFE_PROC_STUB(9);
THREAD_SAFE_PROC_STUB(10);
THREAD_SAFE_PROC_STUB(11);
THREAD_SAFE_PROC_STUB(12);
MOCKCPP_NS_END
int echo()
{
return 0;
}
TEST(ProcStubTest, test1)
{
MOCKER(echo)
.stubs()
.will(invoke_thread_safe(+[]{
return 1;
}));
std::vector<std::thread> thds;
for ( int i = 0; i < 4; i++ )
{
thds.emplace_back([]{
for ( int i = 0; i < 100; i++ )
{
EXPECT_EQ(1, echo());
}
});
}
for ( auto&& t : thds )
{
t.join();
}
GlobalMockObject::verify();
}
It seems works and no longer cause coredump(although the implementation may not be so graceful).
I found this problem in my job, to reproduce that problem, the simplified model is as follows:
problem: when invoke the function be mocked concurrently,the operator= of result (member of ProcStub object) will be invoked concurrently, however, it's not thread safe, may cause double free/accessing freed memory etc..
In order to test my conclusion, I add a mutex for ProcStub as follows:
It seems works and no longer cause coredump(although the implementation may not be so graceful).