I have a report from a user of my SDL-based scientific software that consecutive calls to SDL_GetTicks() produces a jump backward in time, presumably on windows. I have logs of this, where events are timestamped with GetTicks() output every couple seconds. In one case, the jump backward was about 3.97 minutes, and in two cases about exactly 4.98 minutes; at least one time the resulting timer number went negative. This wasn’t regular (like not every 5 minutes). This happens well within the first few minutes of running the program, and the tick numbers are nowhere close to the point where it would hit its max and flip negative. It appears to happen randomly and is not reproducible with a specific set of actions. I’m just wondering what might cause this.
Does anyone know if an NTP client or process that checks the time to sync with a network could do something like this? Could ANY another program reset the timer SDL uses if it is running at the same time? I suspect it is something quirky about his computer, either some software running or some hardware is going haywire, but have no basis for a guess.
Thanks for any education you can give.
Yeah, if their computer’s internal clock is off, the NTP client will adjust it every so often. Depending on the method SDL_GetTicks() uses, it can also change if the OS switches which CPU core your app is running on.
Look into std::chrono, introduced in C++11, which has time counters that are guaranteed to only increment. It also has high resolution time sources that offer higher precision that SDL_GetTick()'s relatively low 1 millisecond.
You should just use SDL_GetPerformanceCounter and SDL_GetPerformanceFrequency instead. They do the same thing as what std::chrono tries to do without relying on possibly bad standard library implementations across different operating systems.
std::chrono has time sources guaranteed to only increment, which it seems is the OP’s primary concern.
edit: looking at the source, on some platforms SDL_GetPerformanceCounter() uses a monotonic source (aka guaranteed to never go backwards) but others it just uses gettimeofday() which can go backward, give different results on different CPU cores (depending on OS implementation) etc.
To speak to SDL:
SDL_GetTicks() on Windows uses QueryPerformanceCounters (the Win32 function), so it should not be affected by changes to system time from an NTP server, etc.
However: on older machines (and on machines running Windows XP), you can get either bad timestamps because the CPU speeds up and slows down as processor load changes, or on multicore systems you can get a different value if the process jumps to a different CPU. In practice, in 2020, this is almost certainly not happening, but it’s possible that flakey hardware or a really old OS could cause this.
Also: if for some reason our call at startup to QueryPerformanceFrequency() fails, SDL_GetTicks() will fall back to timeGetTime(), and who knows what will happen then? In practice, this should never happen on any version of Windows we support, afaik.
Quick followup question: Is the Windows system running in a virtual machine?
Thanks for all the possibilities. I looked in the windows library code and considered the possibility that the start-time counter was getting reset, which is the only thing I could imagine would do anything like this within the SDL code. But that wouldn’t create the issue I was seeing because the time values logged didn’t get reset back to 0, so it has to be happening to the numbers delivered to SDL via whichever timing call was used.
In a follow-up, the user told me this was an old Windows 7 based laptop–I don’t believe a virtual machine was involved. I think he was using 4 or 5 laptops for this application and the timer issues only happened on one of them, so it was something about that particular computer. He was using this to test reaction time of athletes after they were fatigued, and he said the time-travel only happened when they were fatigued and were really hitting the keys hard at that point so old hardware + old software + abuse could maybe occasionally do something weird to old circuits and timer logic.
Thanks–I’m satisfied that there is nothing boneheaded I did to cause this, and nothing obvious that SDL does that caused this either. The user is has a way of dealing with it so that is as good a resolution as I can hope for.