#include <thread> #include <cstdio> using namespace std; void foo() { printf("Goodbye world.\r\n"); } int main() { thread t1(foo); thread t2(foo); t1.join(); t2.join(); }
Very simple. You would expect
Goodbye world. Goodbye world.
and it did work in YOUR pc, but in your customer's PC this is what happened:
Goodbye woGoodbyrld. e world.
The reason of course is that the calls to printf() were simultaneous. How to make them atomic?
Template Solution
#include <thread> #include <cstdio> using namespace std; static std::recursive_mutex InterlockedCallMutex; template <typename R,typename... Args> R InterlockedCall(void* pfoo,Args... args) { std::lock_guard<std::recursive_mutex> lock(InterlockedCallMutex); typedef R(__stdcall * function_pointer)(Args...); function_pointer P = (function_pointer)pfoo; const auto return_value = (*P)(std::forward<Args>(args)...); return (R)return_value; } void foo() { InterlockedCall<int,const char*>(printf,"Goodbye world.\r\n"); } int main() { thread t1(foo); thread t2(foo); t1.join(); t2.join(); }
This also works with a void-returning function, for return (void)return_value is valid.
Perhaps for integer-returning functions, which are the most common, you might want to specialize:
template <typename... Args> size_t InterlockedCall(void* pfoo,Args... args) { std::lock_guard<std::recursive_mutex> lock(InterlockedCallMutex); typedef size_t(__stdcall * function_pointer)(Args...); function_pointer P = (function_pointer)pfoo; const auto return_value = (*P)(std::forward<Args>(args)...); return (size_t)return_value; }
Now you can call without the types in the <>, because they are automatically deduced:
Code
void foo() { InterlockedCall<>(printf,"Goodbye world.\r\n"); }
A catch
It works only for __cdecl calling convention. You must create another template for __stdcall or other conventions.
No comments:
Post a Comment