`std::condition_variable::wait_for` calls the predicate very often
Asked Answered
C

3

8

Consider the following codesnippet:

#include <iostream>
#include <condition_variable>
#include <chrono>
#include <mutex>

int main () {
  std::mutex y;
  std::condition_variable x;
  std::unique_lock<std::mutex>lock{y};
  int i = 0;
  auto increment = [&] {++i; return false;};
  using namespace std::chrono_literals;

  //lock 5s if increment returns false
  //let's see how often was increment called?
  x.wait_for(lock, 5s, increment);
  std::cout << i << std::endl;

  //compare this with a simple loop:
  //how often can my system call increment in 5s?
  auto const end = std::chrono::system_clock::now() + 5s;
  i = 0;
  while (std::chrono::system_clock::now() < end) {
    increment();
  }
  std::cout << i;
}

As I understand wait_for, i should be O(1) after wait_for (let's assume spurious unlocks are rare).

However, I get
i ~= 3e8 for kernel 4.17.14, Intel(R) Core(TM) i7-6700 CPU @ 3.40GHz,
i ~= 8e6 for kernel 3.10.0, Intel(R) Xeon(R) CPU E5-2630 v4 @ 2.20GHz.

This sounds funny, so i check by comparing with a simple loop that runs 5 seconds. Roughly same results for i, only a 5-10% difference.

Question:
what is wait_for doing? Does it work as expected and I just understood cppreference wrong, or did I mess up?

Second, (optional) question: Where does this enormous difference in i come from?

Additional info: (gcc7.3, gcc8.2, clang6.0), flags: -O3 --std=c++17 all yield comparable results.

Copenhaver answered 15/8, 2018 at 12:49 Comment(6)
Can't reproduce on Wandbox.Unrestrained
I cannot reproduce on wandbox. I get 2 for every version of clang and gcc I've tried for x.wait_for(lock, 5s, increment); std::cout << i << std::endl;. I also get 3 on my local machine with MSVS.Annadiane
I get 50K on 2.6.32 redhat kernelVoltcoulomb
gcc.gnu.org/bugzilla/show_bug.cgi?id=75402, gcc.gnu.org/bugzilla/show_bug.cgi?id=58929 could be relevant?Grenoble
@AlanBirtles indeed, adding -pthread fixed the "bug"(?) Thanks a lot. Let's see whether it is confirmed at some point..Copenhaver
FWIW, I think for Linux pthreads, spurious wakeup can only happen by signal interruption (and the return code will be EINTR which could be handled transparently, but is deliberately not, in order to allow the program to deal with signals more easily), and a scenario where a thread starts to wait just as another thread is currently being woken up (i.e. requiring at least two waiters). In the latter case, it is or was possible that both threads continue as the result of just one call to pthread_cond_signal. I don't even know if this still applies to the current implementation, though.Timecard
G
5

libstdc++ has an unfortunate ability to compile and seemingly work without pthread however it wont function correctly.

See this libstdc++ bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58929

You need to add "-pthread" to your compile command.

Grenoble answered 15/8, 2018 at 13:28 Comment(0)
V
1

You need to add -pthread flag to gcc when compiling, for example on gcc 5.1.0 on RE:

without pthread: 49752692
with pthread: 2
Voltcoulomb answered 15/8, 2018 at 13:18 Comment(0)
C
1

You need to link to the pthread library using the -pthread flag to g++:

g++ cond_test.cpp -pthread

Most linux systems require you to link to the pthread library to use threading features. However programs using standard C++ threading seems to link successfully without explicitly linking to pthread, and instead produce undefined behavior at runtime (It often crashes, but with this code it seems not to, but instead produce unexpected behavior).

Example for this code:

$ g++ t.cpp  && ./a.out
5817437
18860410
$ g++ t.cpp  -pthread && ./a.out
2
19718764
Childbirth answered 15/8, 2018 at 13:19 Comment(3)
It might be worth pointing out that the -pthread flag does more than just link libpthread - it may also introduce other necessary flags, defines in code, etc. I've seen people do -lpthread, which is veeeeery similar, but wrong.Needless
@Needless what else does -pthread do? since the assembly seems to be similar with and without -pthread (objdump & compiler explorer)Copenhaver
@kawillzocken It may set flags for compilation stages other than linking. Depends on the platform, but typically on Linux it also defines _REENTRANT: https://mcmap.net/q/15189/-significance-of-pthread-flag-when-compilingNeedless

© 2022 - 2024 — McMap. All rights reserved.