Skip to content

<condition_variable>: The actual waiting time of cv.wait_for() is longer than duration param #2173

Description

@meng-zha

Describe the bug
It is found that the time interval is longer than the duration setting while using conditional_varaiable.wait_for(). If the duration is set 50ms, the actual waiting time is about 60ms..

Test case

#include <stdio.h>
#include <stdlib.h>
#include <thread>
#include <chrono>
#include <string.h>
#include <condition_variable>
#include <memory>
#include <fstream>
#include <list>
#include <vector>
#include <mutex>
std::mutex mtx;
std::condition_variable point_pack_condition;
using namespace std::chrono;

void wait()
{
    int i = 0;
    steady_clock::time_point last_time = steady_clock::now();
    for (i = 0; i < 20; ++i) {
        {
            std::unique_lock<std::mutex> lock(mtx);
            last_time = steady_clock::now();
            printf("%d\n",point_pack_condition.wait_for(lock, milliseconds(50)));
            printf("Delta time:%ld.\n", duration_cast<microseconds>(steady_clock::now() - last_time).count());
            last_time = steady_clock::now();
        }

        printf("Finish save %d frame to lvx file.\n", i);
    }
}

int main(int argc, const char *argv[]) {
  std::thread t1(wait);
  t1.join();
  getchar();
}

Expected behavior
The code tested on Ubuntu16.04 or win7 behaves as follows:
image

Actual behavior
On win10:
image

Version
Windows10
visual studio 2019
vc_tools:14.29.30133

Activity

  1. fsb4000 commented on Sep 5, 2021

    @fsb4000
    Contributor

    I doubt that STL implementation is wrong.

    But I found a small pattern:

    1. If OS is idle then I got ~60000, same as your results.
    2. If OS has some load(for Windows 10, running VirtualBox is enough, for Windows 7 running Edge), then I got ~50000.

    On Windows 10 21H1:

    изображение

    Same behavior on Windows 7 SP1.
    изображение
    изображение

    Possibly related to energy saving...

  2. fsb4000 commented on Sep 5, 2021

    @fsb4000
    Contributor

    https://en.cppreference.com/w/cpp/thread/condition_variable/wait_for

    This function may block for longer than timeout_duration due to scheduling or resource contention delays.

  3. sylveon commented on Sep 5, 2021

    @sylveon
    Contributor

    This is pretty typical for non-realtime operating systems. When you sleep, or block on something, you relinquish your time slice to the OS. Your thread will now be suspended until a time slice is available for your thread to run in, which may not correspond exactly to your timeout. Your thread could also theorically never run again, if there's always a higher priority thread that gets scheduled.

    You'll notice native Windows APIs like Sleep, WaitForSingleObject, timers, WaitOnAddress, etc. all expose that same behavior.

  4. fsb4000 commented on Sep 5, 2021

    @fsb4000
    Contributor

    If you really need such resolution try timeBeginPeriod:

    https://docs.microsoft.com/en-us/windows/win32/api/timeapi/nf-timeapi-timebeginperiod

    It works for me:

    #include <stdio.h>
    #include <stdlib.h>
    #include <thread>
    #include <chrono>
    #include <string.h>
    #include <condition_variable>
    #include <memory>
    #include <fstream>
    #include <list>
    #include <vector>
    #include <mutex>
    
    #include <windows.h>
    
    #pragma comment(lib, "Winmm.lib")
    
    std::mutex mtx;
    std::condition_variable point_pack_condition;
    using namespace std::chrono;
    
    void wait()
    {
      int i = 0;
      steady_clock::time_point last_time = steady_clock::now();
      for (i = 0; i < 20; ++i) {
        {
          std::unique_lock<std::mutex> lock(mtx);
          last_time = steady_clock::now();
          printf("%d\n", point_pack_condition.wait_for(lock, milliseconds(50)));
          printf("Delta time:%ld.\n", duration_cast<microseconds>(steady_clock::now() - last_time).count());
          last_time = steady_clock::now();
        }
    
        printf("Finish save %d frame to lvx file.\n", i);
      }
    }
    
    
    int main(int argc, const char* argv[]) {
      timeBeginPeriod(1);
      std::thread t1(wait);
      t1.join();
      timeEndPeriod(1);
      getchar();
    }
  5. sylveon commented on Sep 5, 2021

    @sylveon
    Contributor

    I generally don't recommend it unless it's a time sensitive workload, as this affects battery usage (especially if you call it at start and then just leave it there forever)

  6. MikeGitb commented on Sep 5, 2021

    @MikeGitb

    Imho, even on a non-rtos OS, 10ms difference is quite a lot. But yes - I don't think there is a lot the STL can or should do about it.

  7. AlexGuteniev commented on Sep 12, 2021

    @AlexGuteniev
    Contributor

    I vote for documenting that the precision of condition_variable wait may vary depending on OS state and leaving it as it is.

  8. MikeGitb commented on Sep 12, 2021

    @MikeGitb

    I think it would be valuable to explicitly state the ballpark of what can be seen in real-life (e.g. tens of ms/OS tick length). I
    wpait_for anyway doesn't guarantee any particular worst case delay (in theory it can wait for seconds, minutes, hours or days after the min delay), so " can vary depending on OS state" doesn't tell the user anything new. On the other hand, something like "We have seen 10s of ms in Benchmarks" Gives the user an Idea of what to expect in regular use and not just weird/pathological edge cases.

  9. changed the title [-]<conditional_variable>: The actual waiting time of cv.wait_for() is longer than duration param[/-] [+]`<condition_variable>`: The actual waiting time of `cv.wait_for()` is longer than duration param[/+] on Mar 26, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    documentationRelated to documentation or comments

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions