-1

I'm trying to execute a method with a stable cycle time (e.g. 20ms). My current approach is to create a thread using std::thread. Inside this thread I do the following (pseudocode):

while(true)
{
  tStart = GetCurrentTime();
  ExecuteMethod();
  tEnd = GetCurrentTime();

  actualCycleTime = tEnd - tStart;
  SleepFor(DesiredCycleTime - actualCycleTime);
}

For time measurement and sleeping I use std::chrono (std::steady_clock and std::thread::sleep_for).

The problem is that my loop does not run at the expected stable 20ms. Instead, I get cycle times between 20 and 60ms. My guess is that this is caused by the Windows scheduler.

Is there a better way to achieve a stable cycle time (busy waiting, etc.)?

Craig Scott
  • 9,238
  • 5
  • 56
  • 85
Boris
  • 8,551
  • 25
  • 67
  • 120
  • You are using a repeated relative timing, which is vulnerable to racing conditions between determining the actual cycle time and setting up the desired cycle time. You need an externally controlled periodic trigger of the desired cycle time. – Yunnosch Jul 06 '17 at 05:05
  • You may want to look at https://stackoverflow.com/questions/2904887/sub-millisecond-precision-timing-in-c-or-c/2905082#2905082 – jodag Jul 06 '17 at 05:08
  • timeSetEvent used to be fairly reliable when used with timeBeginPeriod. MSDN says it's obsolete though and you should use https://msdn.microsoft.com/en-us/library/ms682485(v=vs.85).aspx I don't have any experience with that so I can't say how reliable or precise it is. – Retired Ninja Jul 06 '17 at 05:08
  • Use `sleep_until` instead, for example: https://stackoverflow.com/a/44418375/576911 – Howard Hinnant Jul 06 '17 at 14:16

1 Answers1

2

You could use a timer event. If you need a really solid clock, you need to raise your priority level to the max. This code will give you the best possible performance for a user-mode application. I've omitted the usual error checking, for clarity, but I've marked the calls that should be checked. If in doubt, consult MSDN.

Windows timer resolution is limited to a global time-slice Windows uses to switch between threads. On modern CPUs, that value is usuallu 2-5ms. on older CPUs this value is 10-15ms. You can control this global setting by calling timeBeginPeriod(). This will affect the precision of the interrupts.

// use this event to exit the loop, by calling SetEvent(hExitEvent).
HANDLE hExitEvent = CreateEvent(NULL, NULL, FALSE, NULL);  

void RealTimeLoop()
{
    // You may want to raise the process priority...
    HANDLE hProcess = GetCurrentProcess();                       // never fails
    SetPriorityClass(hProcess, REALTIME_PRIORITY_CLASS);

    // setting the priority is critical.
    HANDLE hThread = GetCurrentThread();                         // never fails
    SetThreadPriority(hThread, THREAD_PRIORITY_TIME_CRITICAL);   // could fail

    timeBeginPeriod(1);                                          // could fail

    HANDLE hTimer = CreateWaitableTimer(NULL, FALSE, NULL);      // could fail

    // could also set a call back here, but I've never tried it.
    LARGE_INTEGER dueTime = {};
    SetWaitableTimer(hTimer, &dueTime, 20, NULL, NULL, FALSE);   // could fail

    HANDLE ah[2] = { hExitEvent, hTimer };
    bool exitLoop = false;

    while(!exitLoop)
    {
        switch (WaitForMultipleObjects(2, ah, FALSE, INFINITE))
        {
            default:  // error would arrive here 
            case 0:  exitLoop = true; break;
            case 1:  ExecuteMethod(); break;
        }
   }
   timeEndPeriod(1);
   CloseHandle(hTimer);
   CloseHandle(hThread);
   CloseHandle(hProcess);
}
Michaël Roy
  • 6,338
  • 1
  • 15
  • 19