1

I want to run some code at the beginning of each minute, my code looks like:

#include <bits/stdc++.h>
int main(){
    using namespace std;
    using namespace std::chrono_literals;
    auto start_ctime = time(NULL)/60*60+60;//beginning second of next minute
    auto until= chrono::system_clock::from_time_t(start_ctime);
    for(;;until+=60s){
        this_thread::sleep_until(until);
        auto diff=time(NULL)-start_ctime;
        cout<<diff<<endl;
    }
}

output:

0
59
120
180
239
300

Seems in some iterations, either

  • sleep_for returned before the timepoint specified, which doesn't conform with the requirement of it or nanosleep(which seems to be called by the implementation).
  • Or time returns a time before the real time

What could happen? I am using g++ 11.2.0 and Ubuntu 22.04

James
  • 703
  • 1
  • 7
  • 22
  • `start_ctime = time(NULL)/60*60+60;`. What is the meaning of `time(NULL)-start_ctime;`? Don't you need to divide that one also before subtracting? – Özgür Murat Sağdıçoğlu Aug 20 '22 at 03:41
  • @ÖzgürMuratSağdıçoğlu both are in the format of seconds since epoch. `diff` is the seconds passed since `start_ctime` – James Aug 20 '22 at 03:44
  • Use `system_clock::now()` instead of `time(NULL)`. As `sleep_until` should be tied to `system_clock`. – ALX23z Aug 20 '22 at 03:45
  • @ALX23z I need to use the C timestamp somewhere else in the program. Per cppreference, `system_clock It is the only C++ clock that has the ability to map its time points to C-style time.` I assume they can perfectly map to each other? – James Aug 20 '22 at 03:48
  • How can they perfectly represent each other if `time` measures in seconds while `system_clock` measures in nanoseconds? And who knows what other differences there are between the two calls. – ALX23z Aug 20 '22 at 06:54
  • You are right, I was thinking about whole seconds only (in my case) when writing that. In my case `until` is initialized from `time_t` which is a whole second, then added whole seconds. So it should always point to the exact start of each minute. I can see no reason the subsequent `time(NULL)` will return a time less than that, as suggested by the output. – James Aug 20 '22 at 07:21
  • As far as I see, on Linux at least, the amount of time passed right after first `sleep_until` is about 60.00005214sec according to `chrono::system_clock` I presume that for whatever reason `std::time` thinks the amount of time passed is something about `59.996sec` and rounds it down to `59`. I don't believe they ever required perfect consistency and accuracy between calls of `system_clock` and `std::time` - it might be due to update frequency of the `std::time`, as it returns time in seconds it isn't as accurate as `system_clock`. – ALX23z Aug 20 '22 at 09:10
  • Thanks @ALX23z, I figured out the same. – James Aug 20 '22 at 15:52

1 Answers1

2

Due to the cache used by time, it may return the last second.

The glibc implementation uses CLOCK_REALTIME_COARSE, which depends a cache maintained by linux kernel tick. Seems on all of my machines, the frequency is 10ms.

What happened was there was a tick right before the expected second, keeping the cached time to be the last second, and the thread was woke at the second.

The this_thread::sleep looks good, I saw guard code like do{/*sleep*/} while(now()<expected); in the C++ implementation and generated assembly.

References: https://www.gnu.org/software/libc/manual/html_node/Getting-the-Time.html http://events17.linuxfoundation.org/sites/events/files/slides/Timekeeping%20in%20the%20Linux%20Kernel_0.pdf

How to check HZ in the terminal?

James
  • 703
  • 1
  • 7
  • 22