Could please someone explain me why both of the threads in this program (when compiled using the compilers shipped with Visual Studio 2012/2013) are blocked until both std::call_once
calls are executed? Another Visual Studio bug (given that it behaves as expected when compiled with GCC)? Can someone please come up with a workaround? Imagine all the pain I've been through to narrow the problem down and please, be merciful.
#include <chrono>
#include <iostream>
#include <mutex>
#include <thread>
namespace
{
std::once_flag did_nothing;
void do_nothing()
{ }
void sleep_shorter_and_do_nothing_once()
{
std::this_thread::sleep_for(std::chrono::seconds(3));
std::cout << "1\n";
std::call_once(did_nothing, do_nothing);
std::cout << "2\n";
}
std::once_flag sleeped_longer;
void sleep_longer()
{
std::this_thread::sleep_for(std::chrono::seconds(10));
}
void sleep_longer_once()
{
std::cout << "3\n";
std::call_once(sleeped_longer, sleep_longer);
std::cout << "4\n";
}
}
int main()
{
std::thread t1(sleep_shorter_and_do_nothing_once);
std::thread t2(sleep_longer_once);
t1.join();
t2.join();
return 0;
}
To elaborate, it behaves as expected when compiled using GCC:
- prints "3",
- waits 3 seconds,
- prints "1",
- immediately prints "2",
- waits another 6-7 seconds,
- and prints "4".
When compiled using the compilers shipped with Visual Studio 2012/2013, it behaves like this:
- prints "3",
- waits 3 seconds,
- prints "1",
- waits another 6-7 seconds,
- and only then immediately prints "2" and "4".
Clearly, it's a misbehavior. Or isn't it?
UPDATE: I cannot submit this as a Visual Studio bug, because my work account is for some reason "not authorized to submit feedback for this connection", whatever this means. I've got a reply from the Microsoft STL maintainer saying he'll hopefully investigate the issue at some time in the future.
I've got a reply from the STL maintainer at Microsoft saying the bug's fixed in Visual Studio 2015.
It prefers to be a misbehavior.
"The completion of an effective call to call_once on a once_flag object synchronizes with all subsequent calls to call_once on the same once_flag object." (N3242, 30.4.4.2-clause-3).