c++windowsvisual-studio-2013real-timespinning

Busy Loop/Spinning sometimes takes too long under Windows


I'm using a windows 7 PC to output voltages at a rate of 1kHz. At first I simply ended the thread with sleep_until(nextStartTime), however this has proven to be unreliable, sometimes working fine and sometimes being of by up to 10ms.

I found other answers here saying that a busy loop might be more accurate, however mine for some reason also sometimes takes too long.

while (true) {
        doStuff();  //is quick enough
        logDelays();

        nextStartTime = chrono::high_resolution_clock::now() + chrono::milliseconds(1);

        spinStart = chrono::high_resolution_clock::now();

        while (chrono::duration_cast<chrono::microseconds>(nextStartTime - 
                         chrono::high_resolution_clock::now()).count() > 200) {
            spinCount++; //a volatile int
        }
        int spintime = chrono::duration_cast<chrono::microseconds>
                              (chrono::high_resolution_clock::now() - spinStart).count();

        cout << "Spin Time micros :" << spintime << endl;

        if (spinCount > 100000000) {
            cout << "reset spincount" << endl;
            spinCount = 0;
        }

}

I was hoping that this would work to fix my issue, however it produces the output:

  Spin Time micros :9999
  Spin Time micros :9999
  ...

I've been stuck on this problem for the last 5 hours and I'd very thankful if somebody knows a solution.


Solution

  • According to the comments this code waits correctly:

    auto start = std::chrono::high_resolution_clock::now();
    const auto delay = std::chrono::milliseconds(1);
    while (true) {
        doStuff();  //is quick enough
        logDelays();
    
        auto spinStart = std::chrono::high_resolution_clock::now();
        while (start > std::chrono::high_resolution_clock::now() + delay) {}
        int spintime = std::chrono::duration_cast<std::chrono::microseconds>
                              (std::chrono::high_resolution_clock::now() - spinStart).count();
    
        std::cout << "Spin Time micros :" << spintime << std::endl;
        start += delay;
    }
    

    The important part is the busy-wait while (start > std::chrono::high_resolution_clock::now() + delay) {} and start += delay; which will in combination make sure that delay amount of time is waited, even when outside factors (windows update keeping the system busy) disturb it. In case that the loop takes longer than delay the loop will be executed without waiting until it catches up (which may be never if doStuff is sufficiently slow).

    Note that missing an update (due to the system being busy) and then sending 2 at once to catch up might not be the best way to handle the situation. You may want to check the current time inside doStuff and abort/restart the transmission if the timing is wrong by more then some acceptable amount.