Boost C++ Libraries: Ticket #9856: [windows] condition_variable::wait_for returns wrong cv_status on timeout. https://svn.boost.org/trac10/ticket/9856 <p> <strong>The following bug seems to work on *nix systems. But not on Windows 7 with Visual Studio 2013 Express.</strong><br /> If a condition variable is waiting for a defined amount of time, sometimes (nearly always) the returned cv_status is wrong. </p> <pre class="wiki">int main() { mutex mtx; condition_variable cond; unique_lock&lt;mutex&gt; lk(mtx); std::cout &lt;&lt; BOOST_VERSION &lt;&lt; "\n"; for(int i=0; i&lt;10; i++) { std::cout &lt;&lt; i &lt;&lt; " : " &lt;&lt; chrono::steady_clock::now() &lt;&lt; "\n"; if (cv_status::no_timeout == cond.wait_for(lk, chrono::seconds(1))) std::cout &lt;&lt; "no_timeout\n"; } } </pre><p> While the condition is <strong>never</strong> notified, i get <strong>no_timeout</strong>.<br /> </p> <p> debuging the code: </p> <pre class="wiki">steady_clock::time_point c_now = steady_clock::now(); do_wait(lock, ceil&lt;milliseconds&gt;(d).count()); return steady_clock::now() - c_now &lt; d ? cv_status::no_timeout : cv_status::timeout; </pre><p> <strong>steady_clock::now() - c_now &lt; d</strong> is <em>true</em>. It seems do_wait is sleeping to short. </p> en-us Boost C++ Libraries /htdocs/site/boost.png https://svn.boost.org/trac10/ticket/9856 Trac 1.4.3 pi88el@… Tue, 08 Apr 2014 19:06:54 GMT <link>https://svn.boost.org/trac10/ticket/9856#comment:1 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/9856#comment:1</guid> <description> <p> This is also happening with <strong>wait_until</strong>. </p> </description> <category>Ticket</category> </item> <item> <dc:creator>viboes</dc:creator> <pubDate>Sat, 19 Apr 2014 07:13:02 GMT</pubDate> <title>owner, status changed https://svn.boost.org/trac10/ticket/9856#comment:2 https://svn.boost.org/trac10/ticket/9856#comment:2 <ul> <li><strong>owner</strong> changed from <span class="trac-author">Anthony Williams</span> to <span class="trac-author">viboes</span> </li> <li><strong>status</strong> <span class="trac-field-old">new</span> → <span class="trac-field-new">assigned</span> </li> </ul> Ticket viboes Sat, 19 Apr 2014 11:09:36 GMT summary changed https://svn.boost.org/trac10/ticket/9856#comment:3 https://svn.boost.org/trac10/ticket/9856#comment:3 <ul> <li><strong>summary</strong> <span class="trac-field-old">condition_variable::wait_for returns wrong cv_status on timeout.</span> → <span class="trac-field-new">[windows] condition_variable::wait_for returns wrong cv_status on timeout.</span> </li> </ul> Ticket sclark@… Tue, 16 Sep 2014 14:43:55 GMT <link>https://svn.boost.org/trac10/ticket/9856#comment:4 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/9856#comment:4</guid> <description> <p> This bug seems closely related to, or the same as, something I found on Linux in Boost 1.54, 1.48, and 1.46. When passed a deadline represented as a system_time, condition_variable::timed_wait() returns false (i.e. timed out) after a little under a millisecond. For more info and a test program, see the boost-users mailing list archive, thread subject "[Thread] thousands of spurious wakeups from timed_wait() per second" around Sept. 11, 2014. </p> <p> (Sorry, I can't get a link to it past trac's spam filter. The spam filter says "Please respond to the following" and nothing follows.) </p> </description> <category>Ticket</category> </item> <item> <dc:creator>viboes</dc:creator> <pubDate>Tue, 23 Sep 2014 00:17:26 GMT</pubDate> <title>milestone changed https://svn.boost.org/trac10/ticket/9856#comment:5 https://svn.boost.org/trac10/ticket/9856#comment:5 <ul> <li><strong>milestone</strong> <span class="trac-field-old">To Be Determined</span> → <span class="trac-field-new">Boost 1.57.0</span> </li> </ul> Ticket Niall Douglas Sat, 04 Oct 2014 19:41:50 GMT <link>https://svn.boost.org/trac10/ticket/9856#comment:6 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/9856#comment:6</guid> <description> <p> I am unable to replicate this issue with trunk 1.57 on VS2013 x86 on Win8.1. </p> </description> <category>Ticket</category> </item> <item> <dc:creator>viboes</dc:creator> <pubDate>Wed, 08 Oct 2014 05:27:30 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/9856#comment:7 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/9856#comment:7</guid> <description> <p> @pi88el,@sclark please, could you check on trunk? </p> </description> <category>Ticket</category> </item> <item> <dc:creator>viboes</dc:creator> <pubDate>Sat, 11 Oct 2014 05:28:23 GMT</pubDate> <title>status changed; resolution set; milestone deleted https://svn.boost.org/trac10/ticket/9856#comment:8 https://svn.boost.org/trac10/ticket/9856#comment:8 <ul> <li><strong>status</strong> <span class="trac-field-old">assigned</span> → <span class="trac-field-new">closed</span> </li> <li><strong>resolution</strong> → <span class="trac-field-new">worksforme</span> </li> <li><strong>milestone</strong> <span class="trac-field-deleted">Boost 1.57.0</span> </li> </ul> Ticket yinon Wed, 24 Dec 2014 13:55:40 GMT <link>https://svn.boost.org/trac10/ticket/9856#comment:9 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/9856#comment:9</guid> <description> <p> It reproduces on 1.57. Windows 8.1 Visual Studio 2013 Ultimate </p> <p> Using the function </p> <p> boost::condition_variable condition; condition.wait_until(lock, _waitUntil) </p> </description> <category>Ticket</category> </item> <item> <author>yinon@…</author> <pubDate>Wed, 24 Dec 2014 13:56:42 GMT</pubDate> <title>status, version changed; resolution deleted https://svn.boost.org/trac10/ticket/9856#comment:10 https://svn.boost.org/trac10/ticket/9856#comment:10 <ul> <li><strong>status</strong> <span class="trac-field-old">closed</span> → <span class="trac-field-new">reopened</span> </li> <li><strong>version</strong> <span class="trac-field-old">Boost 1.55.0</span> → <span class="trac-field-new">Boost 1.57.0</span> </li> <li><strong>resolution</strong> <span class="trac-field-deleted">worksforme</span> </li> </ul> Ticket Niall Douglas Sun, 04 Jan 2015 05:58:40 GMT <link>https://svn.boost.org/trac10/ticket/9856#comment:11 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/9856#comment:11</guid> <description> <p> I had a look for the testcase I wrote for <a class="closed ticket" href="https://svn.boost.org/trac10/ticket/9856" title="#9856: Bugs: [windows] condition_variable::wait_for returns wrong cv_status on timeout. (closed: wontfix)">#9856</a> but it looks like I deleted it last time I reset my Boost installation. </p> <p> However, if I am to be blunt here, this must be a behaviour which differs between people's machines. Perhaps an AMD vs Intel difference, or maybe a HPET difference? On Windows it also matters hugely if any multimedia program is running because then the Windows context switch granularity goes from ~43ms to ~1ms. A multimedia program famous for this is the Chrome web browser. </p> <p> Obviously whether a multimedia program is running should make no difference to Boost code. But I am trying to think of a repeatable demonstration, as otherwise finding and fixing any problem is very hard. </p> </description> <category>Ticket</category> </item> <item> <author>yinon@…</author> <pubDate>Sun, 04 Jan 2015 06:14:20 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/9856#comment:12 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/9856#comment:12</guid> <description> <p> I have reproduced the problem on a Parallels Desktop 10 Virtual Machine, running on a <a class="missing wiki">MacBook</a> Pro, Processor: Intel Core i5. </p> </description> <category>Ticket</category> </item> <item> <dc:creator>Niall Douglas</dc:creator> <pubDate>Sat, 10 Jan 2015 13:13:59 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/9856#comment:13 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/9856#comment:13</guid> <description> <p> I've put together the following test case test_9856.cpp: </p> <pre class="wiki">#include "boost/thread.hpp" #include &lt;iostream&gt; using namespace boost; int main() { mutex mtx; condition_variable cond; unique_lock&lt;mutex&gt; lk(mtx); std::cout &lt;&lt; BOOST_VERSION &lt;&lt; "\n"; for(int i=0; i&lt;10; i++) { //std::cout &lt;&lt; i &lt;&lt; " : " &lt;&lt; chrono::steady_clock::now().count() &lt;&lt; "\n"; if (cv_status::timeout != cond.wait_for(lk, chrono::seconds(1))) { std::cout &lt;&lt; "ERROR: Wrong return code!\n"; return 1; } } return 0; } </pre><p> I'll test it on Windows when I reboot out of Linux later today. I've also lined up a KVM virtualised Windows to test upon. </p> <p> Niall </p> </description> <category>Ticket</category> </item> <item> <dc:creator>viboes</dc:creator> <pubDate>Sat, 10 Jan 2015 15:07:55 GMT</pubDate> <title>owner, status changed https://svn.boost.org/trac10/ticket/9856#comment:14 https://svn.boost.org/trac10/ticket/9856#comment:14 <ul> <li><strong>owner</strong> changed from <span class="trac-author">viboes</span> to <span class="trac-author">Niall Douglas</span> </li> <li><strong>status</strong> <span class="trac-field-old">reopened</span> → <span class="trac-field-new">new</span> </li> </ul> Ticket Niall Douglas Sat, 10 Jan 2015 20:38:35 GMT <link>https://svn.boost.org/trac10/ticket/9856#comment:15 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/9856#comment:15</guid> <description> <p> I've successfully replicated this bug! You have to loop running the test case a few times, but it'll eventually appear. This was on Windows 8.1 x64. </p> <p> I'll see if I can fix it next Saturday. </p> <p> Niall </p> </description> <category>Ticket</category> </item> <item> <dc:creator>Niall Douglas</dc:creator> <pubDate>Thu, 22 Jan 2015 18:38:11 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/9856#comment:16 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/9856#comment:16</guid> <description> <p> I have an update on this issue. Firstly, I have committed a testcase for this to Boost.Thread trunk, so it is now being tested as part of regular unit testing. </p> <p> I have some quite scary news though. Boost.Thread decides whether the wait has timed out by comparing steady_clock (really <a class="missing wiki">QueryPerformanceCounter</a>) from before and after, and if the wait took the timeout or longer it assumes that timeout occurred. Unfortunately, <a class="missing wiki">WaitForMultipleObjectsEx</a>() provides no guarantees that it actually waits for the timeout requested - you might request 50ms, but might actually get 10ms. Here is an example program: </p> <pre class="wiki">#include &lt;windows.h&gt; #include &lt;stdio.h&gt; #include &lt;chrono&gt; int main(void) { ULONG ulDelay_ms = 20; HANDLE hSemaphoreDelay = CreateSemaphore(NULL, 0, 1, NULL); for (size_t n = 0; n &lt; 50; n++) { while (1) { auto begin = std::chrono::high_resolution_clock::now(); ULONG hr = WaitForSingleObject(hSemaphoreDelay, ulDelay_ms); auto end = std::chrono::high_resolution_clock::now(); auto diff = end - begin; if (hr == WAIT_ABANDONED) printf("Wait Abandoned "); else if (hr == WAIT_TIMEOUT) printf("Timed out "); else printf("Signaled "); DWORD lTDelta = std::chrono::duration_cast&lt;std::chrono::milliseconds&gt;(diff).count(); printf("Target Wait Interval: %u Real Wait Interval: %u (%u)\n", ulDelay_ms, lTDelta, diff.count()); if (lTDelta &gt;= ulDelay_ms) break; } printf("\n"); } CloseHandle(hSemaphoreDelay); return 0; } </pre><p> ... and indeed very occasionally it is returning after 16-18ms instead of the requested 20ms at the same frequency that Boost.Thread is not returning timeout. </p> <p> This is scary because I believe every single wait routine in Windows in Boost.Thread, and possibly other Boost libraries, will need to be audited and potentially refactored. I'll raise it on boost-dev tomorrow morning. I believe this new early timeout feature was probably introduced when Microsoft added timer coalescing to Windows to save battery on mobile devices, and so only appeared in Windows 7 and later. The fact it hasn't presented sooner is worrying. </p> <p> Niall </p> </description> <category>Ticket</category> </item> <item> <dc:creator>Niall Douglas</dc:creator> <pubDate>Fri, 23 Jan 2015 17:15:43 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/9856#comment:17 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/9856#comment:17</guid> <description> <p> FYI the current consensus in the discussion on boost-dev is that this should be a wontfix. See <a class="ext-link" href="http://comments.gmane.org/gmane.comp.lib.boost.devel/257246"><span class="icon">​</span>http://comments.gmane.org/gmane.comp.lib.boost.devel/257246</a>. </p> <p> Personally speaking I'd prefer to adjust the code to produce fewer spurious wakeups, but not completely prevent them. We'll see if Microsoft come back to us to say what their STL does. </p> </description> <category>Ticket</category> </item> <item> <dc:creator>Niall Douglas</dc:creator> <pubDate>Thu, 29 Jan 2015 17:57:38 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/9856#comment:18 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/9856#comment:18</guid> <description> <p> Did some testing of the performance of passing a timeout to <a class="missing wiki">WaitForMultipleObjects</a> versus using <a class="missing wiki">SetWaitableTimerEx</a>: </p> <p> Where underlying object is not signalled, 1ms timeout: </p> <p> <a class="missing wiki">WaitForMultipleObjects</a> alone: 1293.457580 microsecs </p> <p> <a class="missing wiki">CreateWaitableTimer</a> + <a class="missing wiki">SetWaitableTimer</a> + <a class="missing wiki">WaitForMultipleObjects</a> + <a class="missing wiki">CloseHandle</a>: 1000.870380 microsecs </p> <p> Where underlying object is signalled: </p> <p> <a class="missing wiki">WaitForMultipleObjects</a> alone: 0.246493 microsecs </p> <p> <a class="missing wiki">SetWaitableTimer</a> + <a class="missing wiki">WaitForMultipleObjects</a>: 1.062579 microsecs </p> <p> <a class="missing wiki">CreateWaitableTimer</a> + <a class="missing wiki">SetWaitableTimer</a> + <a class="missing wiki">WaitForMultipleObjects</a> + <a class="missing wiki">CloseHandle</a>: 2.055087 microsecs </p> <p> Moving the creation of the waitable timer outside of interruptible_wait() will be a pain - every interruptible_wait() user will need to cache a copy, or else we keep a copy in thread local storage. Given that the number of occasions where the underlying semaphore goes signalled in between the check before starting the wait and the wait beginning will be few, I reckon the 8x slowdown for timeouts &lt; 20ms will be worth the ease of implementation. So this is what I will do. </p> <p> Niall </p> </description> <category>Ticket</category> </item> <item> <dc:creator>viboes</dc:creator> <pubDate>Sat, 31 Jan 2015 10:03:52 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/9856#comment:19 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/9856#comment:19</guid> <description> <p> It seems this is related to <a class="new ticket" href="https://svn.boost.org/trac10/ticket/10967" title="#10967: Tasks: Timed wait points inconsistently convert relative to absolute waits (new)">#10967</a> and <a class="closed ticket" href="https://svn.boost.org/trac10/ticket/9720" title="#9720: Bugs: boost::this_thread::sleep_for() sometimes returns immediately on win32 (closed: fixed)">#9720</a>. </p> </description> <category>Ticket</category> </item> <item> <dc:creator>Niall Douglas</dc:creator> <pubDate>Sun, 01 Feb 2015 01:41:22 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/9856#comment:20 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/9856#comment:20</guid> <description> <p> Replying to <a class="ticket" href="https://svn.boost.org/trac10/ticket/9856#comment:19" title="Comment 19">viboes</a>: </p> <blockquote class="citation"> <p> It seems this is related to <a class="new ticket" href="https://svn.boost.org/trac10/ticket/10967" title="#10967: Tasks: Timed wait points inconsistently convert relative to absolute waits (new)">#10967</a> and <a class="closed ticket" href="https://svn.boost.org/trac10/ticket/9720" title="#9720: Bugs: boost::this_thread::sleep_for() sometimes returns immediately on win32 (closed: fixed)">#9720</a>. </p> </blockquote> <p> It does appear that Windows respects the timeout you pass to a waitable timer, whilst it fuzzes any timeout given to <a class="missing wiki">WaitForMultipleObjects</a> to be either too short or too long (on average 30% too long). </p> <p> BTW I have more empirical findings. It turns out Windows ignores all <a class="missing wiki">TolerableDelay</a> values &lt;= 26 ms. I have no idea why. But 27 ms is definitely the minimum for it to have any effect at all. </p> <p> Windows usually ticks at 15.6 ms, so a tolerable delay of 27 ms is about 1.75 context switches. Do I have permission to set that minimum for all timers i.e. </p> <p> 27 ms &lt;= tolerable delay &lt;= 5% of duration between now and timeout. </p> <p> Do we need an upper bound on tolerable delay? I can't see Windows running for ten seconds without at least one interrupt firing though ... </p> <p> Niall </p> </description> <category>Ticket</category> </item> <item> <dc:creator>viboes</dc:creator> <pubDate>Sun, 01 Feb 2015 07:42:39 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/9856#comment:21 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/9856#comment:21</guid> <description> <p> Niall, are these data dependent on the windows version? </p> </description> <category>Ticket</category> </item> <item> <dc:creator>Niall Douglas</dc:creator> <pubDate>Sun, 01 Feb 2015 15:44:18 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/9856#comment:22 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/9856#comment:22</guid> <description> <p> @viboes I only have access to Windows 8.1 nowadays. They added coalesced timer support in Vista and a tickless kernel in Windows 8, so I would assume each Windows will be a little bit different. </p> <p> The code I have written should compile and work on Win XP however. As with GetTickCount64(), it emulates the <a class="missing wiki">SetWaitableTimerEx</a>() function on XP and earlier. </p> <p> Niall </p> </description> <category>Ticket</category> </item> <item> <dc:creator>viboes</dc:creator> <pubDate>Mon, 02 Feb 2015 05:54:49 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/9856#comment:23 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/9856#comment:23</guid> <description> <p> I let you take the best decision. </p> </description> <category>Ticket</category> </item> <item> <dc:creator>Niall Douglas</dc:creator> <pubDate>Wed, 04 Feb 2015 14:31:03 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/9856#comment:24 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/9856#comment:24</guid> <description> <p> Addition of coalesced timers is in <a class="ext-link" href="https://github.com/boostorg/thread/pull/44"><span class="icon">​</span>https://github.com/boostorg/thread/pull/44</a>. </p> <p> The fact that Thread doesn't return timeout up to 10% of the time on Windows is wontfix. </p> </description> <category>Ticket</category> </item> <item> <dc:creator>Niall Douglas</dc:creator> <pubDate>Wed, 04 Feb 2015 14:32:01 GMT</pubDate> <title>status changed; resolution set https://svn.boost.org/trac10/ticket/9856#comment:25 https://svn.boost.org/trac10/ticket/9856#comment:25 <ul> <li><strong>status</strong> <span class="trac-field-old">new</span> → <span class="trac-field-new">closed</span> </li> <li><strong>resolution</strong> → <span class="trac-field-new">wontfix</span> </li> </ul> Ticket