Boost C++ Libraries: Ticket #6174: packaged_task doesn't correctly handle moving results https://svn.boost.org/trac10/ticket/6174 <p> I want to create a packaged task which wraps a function which returns an object which is movable but non-copyable. I tried: </p> <pre class="wiki">#include &lt;boost/thread.hpp&gt; struct MovableButNonCopyable { MovableButNonCopyable() = default; MovableButNonCopyable(MovableButNonCopyable const&amp;) = delete; MovableButNonCopyable&amp; operator=(MovableButNonCopyable const&amp;) = delete; MovableButNonCopyable(MovableButNonCopyable&amp;&amp;) = default; MovableButNonCopyable&amp; operator=(MovableButNonCopyable&amp;&amp;) = default; }; int main() { boost::packaged_task&lt;MovableButNonCopyable&gt;([]{return MovableButNonCopyable();}); } </pre><p> This does not compile, because the instantiation of <code>packaged_task</code> results in the generation of a function that attempts to call the deleted copy constructor for <code>MovableButNonCopyable</code>. </p> <p> I have determined that this is due to a bug at <code>future.hpp:325</code>, where an rvalue-to-lvalue decay is allowed to occur, which results in the incorrect function overload being used. A patch is attached which adds the required cast. </p> en-us Boost C++ Libraries /htdocs/site/boost.png https://svn.boost.org/trac10/ticket/6174 Trac 1.4.3 onlyone@… Fri, 25 Nov 2011 12:38:44 GMT attachment set https://svn.boost.org/trac10/ticket/6174 https://svn.boost.org/trac10/ticket/6174 <ul> <li><strong>attachment</strong> → <span class="trac-field-new">patchfile.patch</span> </li> </ul> <p> Fix for <a class="closed ticket" href="https://svn.boost.org/trac10/ticket/6174" title="#6174: Patches: packaged_task doesn't correctly handle moving results (closed: fixed)">#6174</a> </p> Ticket onlyone@… Fri, 25 Nov 2011 12:57:54 GMT summary changed https://svn.boost.org/trac10/ticket/6174#comment:1 https://svn.boost.org/trac10/ticket/6174#comment:1 <ul> <li><strong>summary</strong> <span class="trac-field-old">packaged_task does correctly handle moving results</span> → <span class="trac-field-new">[thread] packaged_task does correctly handle moving results</span> </li> </ul> Ticket viboes Fri, 02 Dec 2011 20:47:05 GMT owner, status changed https://svn.boost.org/trac10/ticket/6174#comment:2 https://svn.boost.org/trac10/ticket/6174#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> <p> Please, could you stay with which compiler this doesn't compile and log the compile error? </p> <p> I have no access to lambdas now. The following compiles for me with clang </p> <pre class="wiki"> boost::packaged_task&lt;MovableButNonCopyable&gt;(MovableButNonCopyable()); </pre> Ticket viboes Fri, 02 Dec 2011 20:47:33 GMT cc changed https://svn.boost.org/trac10/ticket/6174#comment:3 https://svn.boost.org/trac10/ticket/6174#comment:3 <ul> <li><strong>cc</strong> <span class="trac-author">viboes</span> added </li> </ul> Ticket onlyone@… Sat, 03 Dec 2011 15:23:43 GMT <link>https://svn.boost.org/trac10/ticket/6174#comment:4 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/6174#comment:4</guid> <description> <p> Replying to <a class="ticket" href="https://svn.boost.org/trac10/ticket/6174#comment:2" title="Comment 2">viboes</a>: </p> <blockquote class="citation"> <p> Please, could you stay with which compiler this doesn't compile and log the compile error? </p> <p> I have no access to lambdas now. The following compiles for me with clang </p> <pre class="wiki"> boost::packaged_task&lt;MovableButNonCopyable&gt;(MovableButNonCopyable()); </pre></blockquote> <p> Bizarre. I tried the following: </p> <pre class="wiki">#include &lt;boost/thread.hpp&gt; struct MovableButNonCopyable { MovableButNonCopyable(){} MovableButNonCopyable(MovableButNonCopyable&amp;&amp;){} MovableButNonCopyable&amp; operator=(MovableButNonCopyable&amp;&amp;){return *this;} private: MovableButNonCopyable(MovableButNonCopyable const&amp;); MovableButNonCopyable&amp; operator=(MovableButNonCopyable const&amp;); }; MovableButNonCopyable construct() {return MovableButNonCopyable();} int main() { boost::packaged_task&lt;MovableButNonCopyable&gt;(construct); } </pre><p> And it compiles without issue with both Clang and GCC. However, the code that I presented in the original ticket fails under GCC 4.6.2 (Macports version, x86_64), with the following message: </p> <pre class="wiki">In file included from /Users/evan/Programming/boost/library/include/boost/thread.hpp:24:0, from main.cpp:1: /Users/evan/Programming/boost/library/include/boost/thread/future.hpp: In static member function 'static void boost::detail::future_traits&lt;T&gt;::init(boost::detail::future_traits&lt;T&gt;::storage_type&amp;, boost::detail::future_traits&lt;T&gt;::source_reference_type) [with T = MovableButNonCopyable, boost::detail::future_traits&lt;T&gt;::storage_type = boost::scoped_ptr&lt;MovableButNonCopyable&gt;, boost::detail::future_traits&lt;T&gt;::source_reference_type = const MovableButNonCopyable&amp;]': /Users/evan/Programming/boost/library/include/boost/thread/future.hpp:308:17: instantiated from 'void boost::detail::future_object&lt;T&gt;::mark_finished_with_result_internal(boost::detail::future_object&lt;T&gt;::source_reference_type) [with T = MovableButNonCopyable, boost::detail::future_object&lt;T&gt;::source_reference_type = const MovableButNonCopyable&amp;]' /Users/evan/Programming/boost/library/include/boost/thread/future.hpp:325:17: instantiated from 'void boost::detail::future_object&lt;T&gt;::mark_finished_with_result(boost::detail::future_object&lt;T&gt;::rvalue_source_type) [with T = MovableButNonCopyable, boost::detail::future_object&lt;T&gt;::rvalue_source_type = MovableButNonCopyable&amp;&amp;]' /Users/evan/Programming/boost/library/include/boost/thread/future.hpp:1224:21: instantiated from 'void boost::detail::task_object&lt;R, F&gt;::do_run() [with R = MovableButNonCopyable, F = main()::&lt;lambda()&gt;]' main.cpp:12:1: instantiated from here /Users/evan/Programming/boost/library/include/boost/thread/future.hpp:239:17: error: use of deleted function 'MovableButNonCopyable::MovableButNonCopyable(const MovableButNonCopyable&amp;)' main.cpp:4:5: error: declared here </pre><p> Unfortunately I do not have any other compilers that I can test it on right at the moment. When I get the chance, I will try a few more versions of GCC, Clang and MSVC. I will also see if I can find a way to reproduce the bug without using lambdas or defaulted/deleted functions. (I will report my findings some time this week). </p> </description> <category>Ticket</category> </item> <item> <dc:creator>viboes</dc:creator> <pubDate>Sun, 11 Dec 2011 11:40:48 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/6174#comment:5 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/6174#comment:5</guid> <description> <p> The is an action point to use Boost.Move. Could we consider this as a duplicate of <a class="closed ticket" href="https://svn.boost.org/trac10/ticket/6194" title="#6194: Feature Requests: Adapt to Boost.Move (closed: fixed)">#6194</a> Adapt to Boost.Move. </p> </description> <category>Ticket</category> </item> <item> <author>onlyone@…</author> <pubDate>Sun, 11 Dec 2011 12:07:03 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/6174#comment:6 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/6174#comment:6</guid> <description> <p> Replying to <a class="ticket" href="https://svn.boost.org/trac10/ticket/6174#comment:5" title="Comment 5">viboes</a>: </p> <blockquote class="citation"> <p> The is an action point to use Boost.Move. Could we consider this as a duplicate of <a class="closed ticket" href="https://svn.boost.org/trac10/ticket/6194" title="#6194: Feature Requests: Adapt to Boost.Move (closed: fixed)">#6194</a> Adapt to Boost.Move. </p> </blockquote> <p> <a class="closed ticket" href="https://svn.boost.org/trac10/ticket/6174" title="#6174: Patches: packaged_task doesn't correctly handle moving results (closed: fixed)">#6174</a> does not involve the move emulation (as the bug occurs in C++11 mode). That said, I have spent the last week working on <a class="closed ticket" href="https://svn.boost.org/trac10/ticket/6194" title="#6194: Feature Requests: Adapt to Boost.Move (closed: fixed)">#6194</a>, and any patch that fixes it will certainly also include a fix for this. </p> </description> <category>Ticket</category> </item> <item> <dc:creator>viboes</dc:creator> <pubDate>Sun, 11 Dec 2011 14:12:52 GMT</pubDate> <title>summary changed https://svn.boost.org/trac10/ticket/6174#comment:7 https://svn.boost.org/trac10/ticket/6174#comment:7 <ul> <li><strong>summary</strong> <span class="trac-field-old">[thread] packaged_task does correctly handle moving results</span> → <span class="trac-field-new">packaged_task doesn't correctly handle moving results</span> </li> </ul> Ticket viboes Mon, 19 Dec 2011 18:37:01 GMT <link>https://svn.boost.org/trac10/ticket/6174#comment:8 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/6174#comment:8</guid> <description> <p> Replying to <a class="ticket" href="https://svn.boost.org/trac10/ticket/6174#comment:6" title="Comment 6">onlyone@…</a>: </p> <blockquote class="citation"> <p> Replying to <a class="ticket" href="https://svn.boost.org/trac10/ticket/6174#comment:5" title="Comment 5">viboes</a>: </p> <blockquote class="citation"> <p> The is an action point to use Boost.Move. Could we consider this as a duplicate of <a class="closed ticket" href="https://svn.boost.org/trac10/ticket/6194" title="#6194: Feature Requests: Adapt to Boost.Move (closed: fixed)">#6194</a> Adapt to Boost.Move. </p> </blockquote> <p> <a class="closed ticket" href="https://svn.boost.org/trac10/ticket/6174" title="#6174: Patches: packaged_task doesn't correctly handle moving results (closed: fixed)">#6174</a> does not involve the move emulation (as the bug occurs in C++11 mode). That said, I have spent the last week working on <a class="closed ticket" href="https://svn.boost.org/trac10/ticket/6194" title="#6194: Feature Requests: Adapt to Boost.Move (closed: fixed)">#6194</a>, and any patch that fixes it will certainly also include a fix for this. </p> </blockquote> <p> I have a patch that maybe could help waiting for <a class="closed ticket" href="https://svn.boost.org/trac10/ticket/6194" title="#6194: Feature Requests: Adapt to Boost.Move (closed: fixed)">#6194</a>. Please could you try it? </p> </description> <category>Ticket</category> </item> <item> <dc:creator>viboes</dc:creator> <pubDate>Mon, 19 Dec 2011 18:38:57 GMT</pubDate> <title>attachment set https://svn.boost.org/trac10/ticket/6174 https://svn.boost.org/trac10/ticket/6174 <ul> <li><strong>attachment</strong> → <span class="trac-field-new">detail_thread.hpp.diff</span> </li> </ul> <p> Use of forward to try to solve the issue </p> Ticket viboes Thu, 29 Dec 2011 14:16:32 GMT type changed https://svn.boost.org/trac10/ticket/6174#comment:9 https://svn.boost.org/trac10/ticket/6174#comment:9 <ul> <li><strong>type</strong> <span class="trac-field-old">Bugs</span> → <span class="trac-field-new">Patches</span> </li> </ul> Ticket viboes Sat, 07 Jan 2012 23:06:18 GMT milestone changed https://svn.boost.org/trac10/ticket/6174#comment:10 https://svn.boost.org/trac10/ticket/6174#comment:10 <ul> <li><strong>milestone</strong> <span class="trac-field-old">To Be Determined</span> → <span class="trac-field-new">Boost 1.49.0</span> </li> </ul> Ticket viboes Tue, 17 Jan 2012 06:40:06 GMT <link>https://svn.boost.org/trac10/ticket/6174#comment:11 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/6174#comment:11</guid> <description> <p> Committed in trunk at revision <a class="changeset" href="https://svn.boost.org/trac10/changeset/76543" title=" * [@http://svn.boost.org/trac/boost/ticket/2741 #2741] Proposal to ...">[76543]</a>. </p> </description> <category>Ticket</category> </item> <item> <dc:creator>viboes</dc:creator> <pubDate>Mon, 28 May 2012 15:44:10 GMT</pubDate> <title>status, milestone changed; resolution set https://svn.boost.org/trac10/ticket/6174#comment:12 https://svn.boost.org/trac10/ticket/6174#comment:12 <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">fixed</span> </li> <li><strong>milestone</strong> <span class="trac-field-old">Boost 1.49.0</span> → <span class="trac-field-new">Boost 1.50.0</span> </li> </ul> <p> Committed in release branch at <a class="changeset" href="https://svn.boost.org/trac10/changeset/78543" title="Merged boost.thread from trunk">[78543]</a> </p> Ticket onlyone@… Thu, 19 Jul 2012 13:21:52 GMT attachment set https://svn.boost.org/trac10/ticket/6174 https://svn.boost.org/trac10/ticket/6174 <ul> <li><strong>attachment</strong> → <span class="trac-field-new">newpatch.patch</span> </li> </ul> <p> Patch for 6174, as well for the incorrect tests for 6174 </p> Ticket onlyone@… Thu, 19 Jul 2012 13:25:32 GMT <link>https://svn.boost.org/trac10/ticket/6174#comment:13 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/6174#comment:13</guid> <description> <p> I'm reopening this as it has not been fixed in 1.50.0 or in trunk. </p> <p> The original <code>patchfile.patch</code> is still an appropriate fix. </p> <p> To ensure that this does not again fail to be fixed, I will attempt to explain very clearly what the issue is, what the fix is, and why I think it wasn't fixed earlier (so bear with me on the longwinded descriptions). I have also attached a patch that fixes both the bug and test case (<code>test_6174.cpp</code>) that previously failed to pick up this bug. </p> <p> <strong>What the issue is:</strong><br /> </p> <p> The issue only occurs in C++11. The issue is that <code>packaged_task</code> incorrectly attempts to copy (instead of move) the return value of its function. This has two implications: <code>packaged_task</code> is performing an unnecessary copy of the return value, and worse, <code>packaged_task</code> cannot be used with return values that are not copyable (but movable). </p> <p> <strong>What the fix is:</strong><br /> </p> <p> The problem happens when you attempt to call <code>packaged_task::operator()</code>. The call tree to the actual problem is as follows: </p> <pre class="wiki"> Executed code Called function +-------------------------------------------------------------------+-------------------------------------------------------------- |(packaged_task&lt;MovableButNonCopyale&gt;(construct))() |packaged_task::operator() |task-&gt;run() |detail::task_base::run() |do_run() |task_object::do_run() //Dispatched through a virtual call |this-&gt;mark_finished_with_result(f()) |future_object::mark_finished_with_result(rvalue_source_type result_) |mark_finished_with_result_internal(result_) //Here is the bad line |//Should call `future_object::mark_finished_with_result_internal(rvalue_reference_type result_)`, | |//but doesn't because of the bad line. Instead calls: | |future_object::mark_finished_with_result_internal(source_reference_type result_) |future_traits&lt;T&gt;::init(result,result_) |future_traits&lt;T&gt;::init(storage_type&amp; storage,source_reference_type t) |storage.reset(new T(t)); // Here is the incorrect copy | +-------------------------------------------------------------------+-------------------------------------------------------------- </pre><p> That is, the bad code is in <code>future_object::mark_finished_with_result(rvalue_source_type result_)</code>. The broken version of <code>future_object::mark_finished_with_result(rvalue_source_type result_)</code> looks like this: </p> <pre class="wiki">void mark_finished_with_result(rvalue_source_type result_) { boost::lock_guard&lt;boost::mutex&gt; lock(mutex); mark_finished_with_result_internal(result_); } </pre><p> The call to <code>mark_finished_with_result_internal(result_)</code> picks the incorrect overload, because <code>result_</code> is an lvalue. To fix this, it is necessary to add a cast (as is done in every other part of the code). </p> <p> The fixed version looks like this: </p> <pre class="wiki">void mark_finished_with_result(rvalue_source_type result_) { boost::lock_guard&lt;boost::mutex&gt; lock(mutex); mark_finished_with_result_internal(static_cast&lt;rvalue_source_type&gt;(result_)); } </pre><p> <strong>Problems with the original example:</strong><br /> </p> <p> The original example failed to consistently trigger the issue, because it did not include a call to <code>packaged_task::operator()</code> (so the broken code would sometimes not be instantiated). Here is a new example without that issue: </p> <pre class="wiki">#include &lt;boost/thread.hpp&gt; struct MovableButNonCopyable { MovableButNonCopyable() = default; MovableButNonCopyable(MovableButNonCopyable const&amp;) = delete; MovableButNonCopyable&amp; operator=(MovableButNonCopyable const&amp;) = delete; MovableButNonCopyable(MovableButNonCopyable&amp;&amp;) = default; MovableButNonCopyable&amp; operator=(MovableButNonCopyable&amp;&amp;) = default; }; MovableButNonCopyable construct() { return MovableButNonCopyable(); } int main() { boost::packaged_task&lt;MovableButNonCopyable&gt; pt(construct); pt(); } </pre><p> <strong>Problems with the test case:</strong><br /> </p> <p> The test case in trunk was totally broken. The main part of the test case looked like this: </p> <pre class="wiki">int main() { boost::packaged_task&lt;MovableButNonCopyable&gt;(MovableButNonCopyable()); } </pre><p> This is attempting to package up a <code>MovableButNonCopyable</code> as a task that returns a <code>MovableButNonCopyable</code>. <code>MovableButNonCopyable</code> is not callable, so this will never work. To make the test case work, it must be changed to: </p> <pre class="wiki">MovableButNonCopyable construct() { return MovableButNonCopyable(); } int main() { boost::packaged_task&lt;MovableButNonCopyable&gt; pt(construct); pt(); } </pre><p> <strong>Problems with the test runner:</strong><br /> Even once the test case was fixed, it still did not correctly identify the issue, as there was an additional problem with the way in which the tests were being run.<br /> </p> <p> The Jamfile for the Boost.Thread test suite causes the the <code>-ansi</code> flag to be added when building with clang or gcc. This overrides any <code>-std=c++11</code> flags that may have been added, and means that the relevant part of <code>test_6174.cpp</code> is never run on these compilers (because the bug only occurs in C++11). To fix this, the Jamfile was modified so that it did not give the <code>-ansi</code> flag. </p> <p> <strong>Other unrelated problems:</strong><br /> </p> <p> After the <code>-ansi</code> flag was removed from the test runner, I was able to run the Boost.Thread test suite with clang in C++11 configuration. Everything passed except for <code>sync/mutual_exclusion/locks/unique_lock/obs/op_int_fail.cpp</code>. It appears that Clang is incorrectly able to compile this test when in C++11 mode. While this is interesting, I did not investigate further, as it is unrelated to <code>6174</code>. </p> <p> Also, I did not understand the reasoning behind the <code>-ansi</code> flag being present. It was also present in the Jamfile for the main Boost.Thread build, but it did not seem necessary. </p> <p> <strong>The solution:</strong><br /> </p> <p> I have attached a patch which modifies <code>mark_finished_with_result</code>, <code>test_6174.cpp</code> and the test Jamfile to fix the problems described above. </p> </description> <category>Ticket</category> </item> <item> <author>onlyone@…</author> <pubDate>Thu, 19 Jul 2012 13:26:28 GMT</pubDate> <title>status, milestone changed; resolution deleted https://svn.boost.org/trac10/ticket/6174#comment:14 https://svn.boost.org/trac10/ticket/6174#comment:14 <ul> <li><strong>status</strong> <span class="trac-field-old">closed</span> → <span class="trac-field-new">reopened</span> </li> <li><strong>resolution</strong> <span class="trac-field-deleted">fixed</span> </li> <li><strong>milestone</strong> <span class="trac-field-old">Boost 1.50.0</span> → <span class="trac-field-new">Boost 1.51.0</span> </li> </ul> Ticket viboes Sun, 12 Aug 2012 16:38:32 GMT status changed https://svn.boost.org/trac10/ticket/6174#comment:15 https://svn.boost.org/trac10/ticket/6174#comment:15 <ul> <li><strong>status</strong> <span class="trac-field-old">reopened</span> → <span class="trac-field-new">new</span> </li> </ul> <p> Thanks for the clear explanations. I missed completely the bug. </p> <p> Committed in trunk revision 79980. </p> Ticket viboes Sun, 12 Aug 2012 16:38:52 GMT milestone changed https://svn.boost.org/trac10/ticket/6174#comment:16 https://svn.boost.org/trac10/ticket/6174#comment:16 <ul> <li><strong>milestone</strong> <span class="trac-field-old">Boost 1.51.0</span> → <span class="trac-field-new">Boost 1.52.0</span> </li> </ul> Ticket viboes Sun, 12 Aug 2012 19:02:56 GMT status changed https://svn.boost.org/trac10/ticket/6174#comment:17 https://svn.boost.org/trac10/ticket/6174#comment:17 <ul> <li><strong>status</strong> <span class="trac-field-old">new</span> → <span class="trac-field-new">assigned</span> </li> </ul> Ticket viboes Wed, 15 Aug 2012 09:56:40 GMT status, milestone changed; resolution set https://svn.boost.org/trac10/ticket/6174#comment:18 https://svn.boost.org/trac10/ticket/6174#comment:18 <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">fixed</span> </li> <li><strong>milestone</strong> <span class="trac-field-old">Boost 1.52.0</span> → <span class="trac-field-new">Boost 1.51.0</span> </li> </ul> <p> Committed revision 80040. </p> Ticket