Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[bug] deadlock in the setup of expectations for the mocked GetCurrentThreadId function #8

Conversation

smalti
Copy link
Owner

@smalti smalti commented Oct 1, 2023

Sample where the fix was tested:

#include <gmock/gmock.h>
#include <gmock-win32.h>

MOCK_STDCALL_FUNC(DWORD, GetCurrentThreadId);

TEST(GetCurrentThreadIdTest, BaseTest)
{
    ON_MODULE_FUNC_CALL(GetCurrentThreadId).WillByDefault(testing::Return(42U));
    EXPECT_MODULE_FUNC_CALL(GetCurrentThreadId).Times(2);

    const auto tid1 = ::GetCurrentThreadId();
    const auto tid2 = ::GetCurrentThreadId();

    BYPASS_MOCKS(EXPECT_EQ(tid1, 42U));
    BYPASS_MOCKS(EXPECT_EQ(tid2, 42U));

    RESTORE_MODULE_FUNC(GetCurrentThreadId);
    VERIFY_AND_CLEAR_MODULE_FUNC_EXPECTATIONS(GetCurrentThreadId);
}

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

Env:

  • GTest: googletest-1.13.0
  • Toolchains: MSVC 2015 v140; MSVC 2022 v143 /std:c++17
  • Platform: x86

Not solved problems:

  • We need to avoid optimizations for lock, see avoid_opt test code
  • The RESTORE_MODULE_FUNC call is required before expectations checks
  • Current implementation made only for MOCK_MODULE_FUNC0_

To close the issue, the merging of this PR is also required:
#7

@smalti smalti marked this pull request as draft October 2, 2023 06:35
@smalti smalti requested a review from Alex0vSky October 2, 2023 09:58
@smalti smalti added the bug Something isn't working label Oct 2, 2023
@smalti smalti changed the title FIX: deadlock in the setup of expectations for the mocked GetCurrentThreadId function [bug] fix: deadlock in the setup of expectations for the mocked GetCurrentThreadId function Oct 2, 2023
@smalti smalti changed the title [bug] fix: deadlock in the setup of expectations for the mocked GetCurrentThreadId function [bug] deadlock in the setup of expectations for the mocked GetCurrentThreadId function Oct 2, 2023
@Alex0vSky
Copy link
Collaborator

Yeah, optimization
got

const auto tid1 = ::GetCurrentThreadId();
call        ebx  

even in Debug configuration, without any optimization.
Give me some time

# Conflicts:
#	include/gmock-win32.h
#	src/gmock-win32.cpp
Alex0vSky
Alex0vSky previously approved these changes Oct 2, 2023
Copy link
Collaborator

@Alex0vSky Alex0vSky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done merging to current state.
For continue moving.

src/gmock-win32.cpp Outdated Show resolved Hide resolved
@Alex0vSky
Copy link
Collaborator

Alex0vSky commented Oct 2, 2023

Conflict resolved here #11

After merging minor changes
@Alex0vSky
Copy link
Collaborator

Alex0vSky commented Oct 2, 2023

Found my error in patchModuleFunc_##m impl
Fix in #12

@Alex0vSky
Copy link
Collaborator

We need to avoid optimizations for lock, see avoid_opt test code

I didn't find any errors with proxy_base::~proxy_base.
Naked, without avoid_opt, lock is successfully decremented every time.
Tried MSVC 2019 and clang
Maybe special building arguments? I have default/gray ones for release building.

The RESTORE_MODULE_FUNC call is required before expectations checks

It might be a good idea to restore all hooks when exiting from current test.

Current implementation made only for MOCK_MODULE_FUNC0_

Temporary, convenient.

@smalti
Copy link
Owner Author

smalti commented Oct 2, 2023

I didn't find any errors with proxy_base::~proxy_base.
Tried MSVC 2019 and clang.

My test setup (where I need to use the ugly avoid_opt) was:

  • MSVC 2015 Update 3, v140 toolchain, Release | x86 configuration
    Optimization settings: default settings were applied, which included: /O2 /Oi /GL for compilation and /OPT:REF /OPT:ICF for the linker.

Then I retested it on a newer setup, and it works without avoid_opt:

  • MSVC 2022, v143 toolchain, Release | x86 configuration, /std:c++14, /std:c++17, /std:c++20, /std:c++latest (Preview - Features from the Latest C++ Working Draft)

So, the problem exists only on my old setup: MSVC 2015 Update 3, v140, and I think that we can skip it (since it works properly on MSVC 2019, clang, MSVC 2022 /std:c++14, /std:c++17, /std:c++20).

As it turns out, the issue is related to my older configuration MSVC 2015 Update 3 with the v140 toolchain. Given that the problem is not present when using MSVC 2019, Clang, or MSVC 2022 with the specified C++ standards (/std:c++14, /std:c++17, /std:c++20), I think we can skip (what do you think ?) that problem for older setup (maybe we should recheck MSVS 2017).


So then, my next TODO list will be:

  • Remove the avoid_opt workaround
  • Apply the implementation from MOCK_MODULE_FUNC0_ to other macro-defines

@smalti
Copy link
Owner Author

smalti commented Oct 2, 2023

It might be a good idea to restore all hooks when exiting from current test.

I agree, and we need to call VERIFY_AND_CLEAR_MODULE_FUNC_EXPECTATIONS after tests since the NiceMock object is a static object.

@smalti smalti marked this pull request as ready for review October 3, 2023 10:15
@smalti smalti requested a review from Alex0vSky October 3, 2023 10:17
Copy link
Collaborator

@Alex0vSky Alex0vSky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/O2 /Oi /GL for compilation and /OPT:REF /OPT:ICF for the linker.
.../std:c++14, /std:c++17, /std:c++20)
I think that we can skip it (since it works properly on MSVC 2019, clang, MSVC 2022 /std:c++14, /std:c++17, /std:c++20)

Msvc2019(v142) and its clang.
/O2 /Oi /GL for compilation, /OPT:REF /OPT:ICF for the linker.

  • /std:c++14 passed
  • /std:c++17 passed
  • /std:c++20 error
    Need declare mockModule_patchModuleFunc
    before using it in patchModuleFunc_ function.
    After rewriting
  • /std:c++20 passed
  • /std:c++latest passed

We can skip it, I agree.

Alex0vSky
Alex0vSky previously approved these changes Oct 3, 2023
Copy link
Collaborator

@Alex0vSky Alex0vSky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

C++20 resolverd in #14

/std:c++20 function declaration
::patchModuleFunc_( &mock_module_##m::oldFn_, &::m, &mock_module_##m::stub ); \
} \
__pragma(optimize("", off))

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved to here

void mockModule_patchModuleFunc (void*, void*, void**);
void mockModule_restoreModuleFunc (void*, void*, void**);
#define MOCK_CDECL_FUNC(r, m, ...) \
MOCK_MODULE_OVERLOAD(MOCK_MODULE_FUNC, MOCK_MODULE_NBARG(__VA_ARGS__)##__CDECL_CONV)##(m, r(__VA_ARGS__))

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved from here

@Alex0vSky Alex0vSky merged commit 9eeef1a into main Oct 3, 2023
@smalti smalti deleted the 3-deadlock-in-the-setup-of-expectations-for-the-mocked-getcurrentthreadid-function branch October 5, 2023 12:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Deadlock in the setup of expectations for the mocked GetCurrentThreadId function
2 participants