I'm working with std::condition_variable::wait_until
in C++ and I'm using std::chrono::system_clock
and std::chrono::steady_clock
to manage the waiting time. After reading the documentation, I understand that the sleeping time should not be affected by changes in the system time when using std::chrono::steady_clock
, while it may change when using std::chrono::system_clock
.
However, my code is behaving differently: for both clock types, the waiting time is affected by the system time. Here's a minimal reproducible example:
#include <iostream>
#include <chrono>
#include <thread>
#include <mutex>
#include <ctime>
#include <sys/time.h>
#include <condition_variable>
#include <atomic>
std::atomic<bool> flag = false;
std::condition_variable cv;
void wait_using_system_clock()
{
std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now();
std::mutex mtx;
std::unique_lock lock(mtx);
cv.wait_until(lock, std::chrono::system_clock::now() + std::chrono::seconds(20), [](){return flag.load();});
std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
std::cout << "Time difference [using system clock] = " << std::chrono::duration_cast<std::chrono::seconds>(end - begin).count() << "s" << std::endl;
}
void wait_using_steady_clock()
{
std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now();
std::mutex mtx;
std::unique_lock lock(mtx);
cv.wait_until(lock, std::chrono::steady_clock::now() + std::chrono::seconds(20), [](){return flag.load();});
std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
std::cout << "Time difference [using steady clock] = " << std::chrono::duration_cast<std::chrono::seconds>(end - begin).count() << "s" << std::endl;
}
void set_system_time_forward()
{
std::this_thread::sleep_for(std::chrono::seconds(2)); // Ensure other threads has executed cv.wait_until
struct timeval tv;
gettimeofday(&tv, NULL);
tv.tv_sec += 15;
settimeofday(&tv, NULL);
}
int main()
{
std::thread t1(wait_using_system_clock);
std::thread t2(wait_using_steady_clock);
std::thread t3(set_system_time_forward);
t1.join();
t2.join();
t3.join();
return 0;
}
I execute the wait_using_system_clock
and wait_using_steady_clock
functions on different threads. Another thread is used to change the system time after 2 seconds. I expect that the function wait_using_steady_clock
should not be affected by this change and should wait for 20 seconds, but instead, both functions finish their execution in approximately 5 seconds, printing:
Time difference [using system clock] = 5s
Time difference [using steady clock] = 5s
Can anyone help me understand why this discrepancy exists between the expected and actual behavior? Is there something I'm missing in the documentation or misunderstanding about these classes?
First things first, the std::mutex
's in your functions
wait_using_system_clock
and wait_using_steady_clock
will not do
anything, because you have to acquire the SAME mutex when you
want to lock some data. This can easily be detected by the thread sanitizer.
You can turn it on with the option -fsanitize=thread
on clang and gcc.
Now, for the part that you are missing about std::condition_varaible::wait_until
. We know from the documentation that:
wait_until causes the current thread to block until the condition variable is notified, a specific time is reached, or a spurious wakeup occurs, optionally looping until some predicate is satisfied (
bool(stop_waiting()) == true
).
But the interesting part is in the 2nd paragraph of the Notes section:
Even if the clock in use is std::chrono::steady_clock or another monotonic clock, a system clock adjustment may induce a spurious wakeup.
And that explains why using std::chrono::steady_clock
in your case made no difference.