EDIT: tl;dr -- this problem appears to be limited to a small set of OS/compiler/library combinations and is now tracked in the GCC Bugzilla as Bug 68921 thanks to @JonathanWakely.
I'm waiting on a future and I've noticed that top
shows 100% CPU usage and strace
shows a steady stream of futex
calls:
... [pid 15141] futex(0x9d19a24, FUTEX_WAIT, -2147483648, {4222429828, 3077922816}) = -1 EINVAL (Invalid argument) ...
This is on Linux 4.2.0 (32-bit i686
), compiled with gcc version 5.2.1.
Here is my minimum-viable example program:
#include <future> #include <iostream> #include <thread> #include <unistd.h> int main() { std::promise<void> p; auto f = p.get_future(); std::thread t([&p](){ std::cout << "Biding my time in a thread.\n"; sleep(10); p.set_value(); }); std::cout << "Waiting.\n"; f.wait(); std::cout << "Done.\n"; t.join(); return 0; }
and here is the compiler invocation (same behavior without -g
):
g++ --std=c++11 -Wall -g -o spin-wait spin-wait.cc -pthread
Is there a more-performant alternative?
Here is a logically-similar program using std::condition_variable
that seems to perform much better:
#include <condition_variable> #include <iostream> #include <mutex> #include <thread> #include <unistd.h> int main() { bool done = 0; std::mutex m; std::condition_variable cv; std::thread t([&m, &cv, &done](){ std::cout << "Biding my time in a thread.\n"; sleep(10); { std::lock_guard<std::mutex> lock(m); done = 1; } cv.notify_all(); }); std::cout << "Waiting.\n"; { std::unique_lock<std::mutex> lock(m); cv.wait(lock, [&done]{ return done; }); } std::cout << "Done.\n"; t.join(); return 0; }
Am I doing something wrong with my std::future
-based code, or is the implementation in my libstdc++
just that bad?
std::shared_futureAccess to the same shared state from multiple threads is safe if each thread does it through its own copy of a shared_future object.
std::future A future is an object that can retrieve a value from some provider object or function, properly synchronizing this access if in different threads. "Valid" futures are future objects associated to a shared state, and are constructed by calling one of the following functions: async. promise::get_future.
No of course it shouldn't be doing that, it's a bug in the implementation, not a property of std::future
.
This is now https://gcc.gnu.org/bugzilla/show_bug.cgi?id=68921 - the loop that keeps calling futex(2)
is in __atomic_futex_unsigned::_M_load_and_test_until
It looks like a simple missing argument to the syscall
function, so a garbage pointer is passed to the kernel, which complains that it's not a valid timespec*
argument. I'm testing the fix and will commit tomorrow, so it will be fixed in GCC 5.4
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With