Bug 58931 - condition_variable::wait_until overflowed by large time_point<steady_clock>
Summary: condition_variable::wait_until overflowed by large time_point<steady_clock>
Status: NEW
Alias: None
Product: gcc
Classification: Unclassified
Component: libstdc++ (show other bugs)
Version: 4.8.2
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2013-10-30 22:46 UTC by Johan Lundberg
Modified: 2020-10-03 17:55 UTC (History)
5 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2013-11-11 00:00:00


Attachments
Patch to check for overflow (344 bytes, patch)
2018-01-27 01:28 UTC, Aaron Graham
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Johan Lundberg 2013-10-30 22:46:21 UTC
With valid but large steady clock time_points, condition_variable.wait_until does not sleep at all, but instead continues as if the time was passed. 

Perhaps related to http://gcc.gnu.org/bugzilla/show_bug.cgi?id=54562

Example: 

#include <chrono>
#include <mutex>
#include <condition_variable>
int main(){
  std::mutex m;
  std::condition_variable cv;
  std::unique_lock<std::mutex> lk(m);
  // does not sleep at all:
  cv.wait_until(lk,
     std::chrono::time_point<std::chrono::steady_clock>::max());
  // sleeps fine:
  //cv.wait_until(lk,
  //   std::chrono::steady_clock::now()+10000*std::chrono::hours{24*365});
}

    cheers / Johan -thanks for a great compiler! 

PS.
* I compiled gcc with --enable-libstdcxx-time=yes. Using 64 bit linux 3.5.0
* The bug does not occur with system_clock.
* I used time_point max() to let a worker thread wait when a queue of delayed events was empty.
Comment 1 Jonathan Wakely 2013-10-31 13:29:30 UTC
I have a fix for PR 54562 so I'll see if it solves this.

N.B. --enable-libstdcxx-time=yes should not be necessary for 4.8 if you have glibc 2.17 or later.
Comment 2 Jonathan Wakely 2013-11-11 12:55:02 UTC
Fixing PR 54562 doesn't help. This can be reduced to

#include <chrono>
#include <cassert>

int main()
{
  using StClock = std::chrono::steady_clock;
  using SysClock = std::chrono::system_clock;
  auto st_atime = std::chrono::time_point<StClock>::max();
  const StClock::time_point st_now = StClock::now();
  const SysClock::time_point sys_now = SysClock::now();
  const auto delta = st_atime - st_now;
  const auto sys_atime = sys_now + delta;
  assert( sys_atime > sys_now );
}
Comment 3 Aaron Graham 2015-05-09 21:29:22 UTC
This is still a problem in current gcc trunk.

The bug is in the condition_variable::wait_until clock conversion. It doesn't check for overflow in that math. Since the steady_clock and system_clock epochs can be very different, it's likely to overflow with values much less than max().

    template<typename _Clock, typename _Duration>
      cv_status
      wait_until(unique_lock<mutex>& __lock,
                 const chrono::time_point<_Clock, _Duration>& __atime)
      {
        // DR 887 - Sync unknown clock to known clock.
        const typename _Clock::time_point __c_entry = _Clock::now();
        const __clock_t::time_point __s_entry = __clock_t::now();
        const auto __delta = __atime - __c_entry;
        const auto __s_atime = __s_entry + __delta;

        return __wait_until_impl(__lock, __s_atime);
      }

I modified my version of gcc to use steady_clock as condition_variable's "known clock" (__clock_t). This is more correct according to the C++ standard and most importantly it makes condition_variable resilient to clock changes when used in conjunction with steady_clock. Because of this, in my case, it works fine with steady_clock::time_point::max(), but fails with system_clock::time_point::max().

Because I made that change and since I don't do timed waits on system_clock (which is unsafe), the overflow hasn't been a problem for me and I haven't fixed it.
Comment 4 Aaron Graham 2015-05-09 22:45:19 UTC
See bug 41861 for discussion of steady_clock wrt condition_variable.
Comment 5 Aaron Graham 2018-01-27 01:28:53 UTC
Created attachment 43261 [details]
Patch to check for overflow
Comment 6 Mike Crowe 2020-10-03 17:55:49 UTC
This problem isn't as serious now that waiting on std::chrono::steady_clock doesn't use the generic wait_until implemenation, but it's worth fixing regardless.

I have a reproduction case and Aaron's fix in attachment 43261 [details] (with some minor tweaks to accommodate the recent addition of __detail::ceil) still appears to work.