<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Issue 3504: condition_variable::wait_for is overspecified</title>
<meta property="og:title" content="Issue 3504: condition_variable::wait_for is overspecified">
<meta property="og:description" content="C++ library issue. Status: New">
<meta property="og:url" content="https://cplusplus.github.io/LWG/issue3504.html">
<meta property="og:type" content="website">
<meta property="og:image" content="http://cplusplus.github.io/LWG/images/cpp_logo.png">
<meta property="og:image:alt" content="C++ logo">
<style>
  p {text-align:justify}
  li {text-align:justify}
  pre code.backtick::before { content: "`" }
  pre code.backtick::after { content: "`" }
  blockquote.note
  {
    background-color:#E0E0E0;
    padding-left: 15px;
    padding-right: 15px;
    padding-top: 1px;
    padding-bottom: 1px;
  }
  ins {background-color:#A0FFA0}
  del {background-color:#FFA0A0}
  table.issues-index { border: 1px solid; border-collapse: collapse; }
  table.issues-index th { text-align: center; padding: 4px; border: 1px solid; }
  table.issues-index td { padding: 4px; border: 1px solid; }
  table.issues-index td:nth-child(1) { text-align: right; }
  table.issues-index td:nth-child(2) { text-align: left; }
  table.issues-index td:nth-child(3) { text-align: left; }
  table.issues-index td:nth-child(4) { text-align: left; }
  table.issues-index td:nth-child(5) { text-align: center; }
  table.issues-index td:nth-child(6) { text-align: center; }
  table.issues-index td:nth-child(7) { text-align: left; }
  table.issues-index td:nth-child(5) span.no-pr { color: red; }
  @media (prefers-color-scheme: dark) {
     html {
        color: #ddd;
        background-color: black;
     }
     ins {
        background-color: #225522
     }
     del {
        background-color: #662222
     }
     a {
        color: #6af
     }
     a:visited {
        color: #6af
     }
     blockquote.note
     {
        background-color: rgba(255, 255, 255, .10)
     }
  }
</style>
</head>
<body>
<hr>
<p><em>This page is a snapshot from the LWG issues list, see the <a href="lwg-active.html">Library Active Issues List</a> for more information and the meaning of <a href="lwg-active.html#New">New</a> status.</em></p>
<h3 id="3504"><a href="lwg-active.html#3504">3504</a>. <code>condition_variable::wait_for</code> is overspecified</h3>
<p><b>Section:</b> 32.7.4 <a href="https://wg21.link/thread.condition.condvar">[thread.condition.condvar]</a> <b>Status:</b> <a href="lwg-active.html#New">New</a>
 <b>Submitter:</b> Jonathan Wakely <b>Opened:</b> 2020-11-18 <b>Last modified:</b> 2024-06-18</p>
<p><b>Priority: </b>3
</p>
<p><b>View other</b> <a href="lwg-index-open.html#thread.condition.condvar">active issues</a> in [thread.condition.condvar].</p>
<p><b>View all other</b> <a href="lwg-index.html#thread.condition.condvar">issues</a> in [thread.condition.condvar].</p>
<p><b>View all issues with</b> <a href="lwg-status.html#New">New</a> status.</p>
<p><b>Discussion:</b></p>
<p>
32.7.4 <a href="https://wg21.link/thread.condition.condvar">[thread.condition.condvar]</a> p24 says:
</p>
<blockquote><p>
<i>Effects:</i> Equivalent to: <code>return wait_until(lock, chrono::steady_clock::now() + rel_time);</code>
</p></blockquote>
<p>
This is overspecification, removing implementer freedom to make <code>cv.wait_for(duration&lt;float&gt;(1))</code> work accurately.
<p/>
The type of <code>steady_clock::now() + duration&lt;float&gt;(n)</code> is <code>time_point&lt;steady_clock, 
duration&lt;float, steady_clock::period&gt;&gt;</code>. If the steady clock's period is <code>std::nano</code> 
and its epoch is the time the system booted, then in under a second a 32-bit <code>float</code> becomes unable 
to exactly represent those <code>time_points</code>! Every second after boot makes <code>duration&lt;float, nano&gt;</code> 
less precise.
<p/>
This means that adding a <code>duration&lt;float&gt;</code> to a <code>time_point</code> (or <code>duration</code>) 
measured in nanoseconds is unlikely to produce an accurate value. Either it will round down to a value less 
than <code>now()</code>, or round up to one greater than <code>now() + 1s</code>. Either way, the <code>wait_for(rel_time)</code> 
doesn't wait for the specified time, and users think the implementation is faulty.
<p/>
A possible solution is to use <code>steady_clock::now() + ceil&lt;steady_clock::duration&gt;(rel_time)</code> 
instead. This converts the relative time to a suitably large integer, and then the addition is not affected 
by floating-point rounding errors due to the limited precision of 32-bit <code>float</code>. Libstdc++ has been 
doing this for nearly three years. Alternatively, the standard could just say that the relative timeout is 
converted to an absolute timeout measured against the steady clock, and leave the details to the implementation. 
Some implementations might not be affected by the problem (e.g. if the steady clock measures milliseconds, 
or processes only run for a few seconds and use the process start as the steady clock's epoch).
<p/>
This also affects the other overload of <code>condition_variable::wait_for</code>, and both overloads of 
<code>condition_variable_any::wait_for</code>.
</p>

<p><i>[2020-11-29; Reflector prioritization]</i></p>

<p>
Set priority to 3 during reflector discussions.
</p>

<p><i>[2024-06-18; Jonathan adds wording]</i></p>



<p id="res-3504"><b>Proposed resolution:</b></p>
<p>
This wording is relative to <a href="https://wg21.link/N4981" title=" Working Draft, Programming Languages — C++">N4981</a>.
</p>
<ol>
<li>
<p>Modify 32.7.1 <a href="https://wg21.link/thread.condition.general">[thread.condition.general]</a> as indicated,
adding a new paragraph to the end:</p>
<blockquote>
<ins>-6-
The definitions in 32.7 <a href="https://wg21.link/thread.condition">[thread.condition]</a> make use of the
following exposition-only function:
</ins>
<blockquote>
<pre><code><ins>template&lt;class Dur&gt;
  chrono::steady_clock::time_point <em>rel-to-abs</em>(const Dur&amp; rel_time)
  { return chrono::steady_clock::now() + chrono::ceil&lt;chrono::steady_clock::duration&gt;(rel_time); }
</ins>
</code></pre>
</blockquote>
</blockquote>
</li>

<li>
<p>Modify 32.7.4 <a href="https://wg21.link/thread.condition.condvar">[thread.condition.condvar]</a> as indicated:</p>
<blockquote>
<pre><code>
template&lt;class Rep, class Period&gt;
  cv_status wait_for(unique_lock&lt;mutex&gt;&amp; lock,
                     const chrono::duration&lt;Rep, Period&gt;&amp; rel_time);
</code></pre>
<p>-23- <i>Preconditions</i>:
<code class='backtick'>lock.owns_lock()</code> is <code class='backtick'>true</code> and <code class='backtick'>lock.mutex()</code> is locked by the calling thread,
and either [...]
</p>
<p>-24- <i>Effects</i>: Equivalent to:
<blockquote><code>
return wait_until(lock, <del>chrono::steady_clock::now() + rel_time</del> <ins><em>rel-to-abs</em>(rel_time)</ins>);
</code></blockquote>
</p>
<p>[...]</p>
<pre><code>
template&lt;class Rep, class Period, class Predicate&gt;
  cv_status wait_for(unique_lock&lt;mutex&gt;&amp; lock,
                     const chrono::duration&lt;Rep, Period&gt;&amp; rel_time,
                     Predicate pred);
</code></pre>
<p>-35- <i>Preconditions</i>:
<code class='backtick'>lock.owns_lock()</code> is <code class='backtick'>true</code> and <code class='backtick'>lock.mutex()</code> is locked by the calling thread,
and either [...]
</p>
<p>-36- <i>Effects</i>: Equivalent to:
<blockquote><code>
return wait_until(lock, <del>chrono::steady_clock::now() + rel_time</del> <ins><em>rel-to-abs</em>(rel_time)</ins>, std::move(pred));
</code></blockquote>
</p>
<p>
[<i>Note 8</i>:
There is no blocking if <code class='backtick'>pred()</code> is initially <code class='backtick'>true</code>, even if the timeout has already expired.
&mdash; <i>end note</i>]
</p>
</blockquote>
</li>

<li>
<p>Modify 32.7.5.2 <a href="https://wg21.link/thread.condvarany.wait">[thread.condvarany.wait]</a> as indicated:</p>
<blockquote>
<pre><code>
template&lt;class Lock, class Rep, class Period&gt;
  cv_status wait_for(Lock&amp; lock, const chrono::duration&lt;Rep, Period&gt;&amp; rel_time);
</code></pre>
<p>-11- <i>Effects</i>: Equivalent to:
<blockquote><code>
return wait_until(lock, <del>chrono::steady_clock::now() + rel_time</del> <ins><em>rel-to-abs</em>(rel_time)</ins>);
</code></blockquote>
</p>
<p>[...]</p>
<pre><code>
template&lt;class Lock, class Rep, class Period, class Predicate&gt;
  cv_status wait_for(Lock&amp; lock, const chrono::duration&lt;Rep, Period&gt;&amp; rel_time, Predicate pred);
</code></pre>
<p>-19- <i>Effects</i>: Equivalent to:
<blockquote><code>
return wait_until(lock, <del>chrono::steady_clock::now() + rel_time</del> <ins><em>rel-to-abs</em>(rel_time)</ins>, std::move(pred));
</code></blockquote>
</p>
</blockquote>
</li>
</ol>





</body>
</html>
