Subject: Re: [Boost-bugs] [Boost C++ Libraries] #6174: packaged_task doesn't correctly handle moving results
From: Boost C++ Libraries (noreply_at_[hidden])
Date: 2012-07-19 13:25:32
#6174: packaged_task doesn't correctly handle moving results
--------------------------------------+-------------------------------------
Reporter: onlyone@⦠| Owner: viboes
Type: Patches | Status: closed
Milestone: Boost 1.50.0 | Component: thread
Version: Boost Development Trunk | Severity: Showstopper
Resolution: fixed | Keywords: move packaged_task
--------------------------------------+-------------------------------------
Comment (by onlyone@â¦):
I'm reopening this as it has not been fixed in 1.50.0 or in trunk.
The original `patchfile.patch` is still an appropriate fix.
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
(`test_6174.cpp`) that previously failed to pick up this bug.
'''What the issue is:'''[[BR]]
The issue only occurs in C++11. The issue is that `packaged_task`
incorrectly attempts to copy
(instead of move) the return value of its function. This has two
implications: `packaged_task` is
performing an unnecessary copy of the return value, and worse,
`packaged_task` cannot be used with
return values that are not copyable (but movable).
'''What the fix is:'''[[BR]]
The problem happens when you attempt to call `packaged_task::operator()`.
The call tree to the actual problem is as follows:
{{{
Executed code
Called function
+-------------------------------------------------------------------+--------------------------------------------------------------
|(packaged_task<MovableButNonCopyale>(construct))()
|packaged_task::operator()
|task->run()
|detail::task_base::run()
|do_run()
|task_object::do_run() //Dispatched through a virtual call
|this->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<T>::init(result,result_)
|future_traits<T>::init(storage_type& storage,source_reference_type t)
|storage.reset(new T(t)); // Here is the incorrect copy |
+-------------------------------------------------------------------+--------------------------------------------------------------
}}}
That is, the bad code is in
`future_object::mark_finished_with_result(rvalue_source_type result_)`.
The broken version of
`future_object::mark_finished_with_result(rvalue_source_type result_)`
looks like this:
{{{
void mark_finished_with_result(rvalue_source_type result_)
{
boost::lock_guard<boost::mutex> lock(mutex);
mark_finished_with_result_internal(result_);
}
}}}
The call to `mark_finished_with_result_internal(result_)` picks the
incorrect overload, because `result_` is an lvalue.
To fix this, it is necessary to add a cast (as is done in every other part
of the code).
The fixed version looks like this:
{{{
void mark_finished_with_result(rvalue_source_type result_)
{
boost::lock_guard<boost::mutex> lock(mutex);
mark_finished_with_result_internal(static_cast<rvalue_source_type>(result_));
}
}}}
'''Problems with the original example:'''[[BR]]
The original example failed to consistently trigger the issue, because it
did not include a call to `packaged_task::operator()`
(so the broken code would sometimes not be instantiated). Here is a new
example without that issue:
{{{
#include <boost/thread.hpp>
struct MovableButNonCopyable {
MovableButNonCopyable() = default;
MovableButNonCopyable(MovableButNonCopyable const&) = delete;
MovableButNonCopyable& operator=(MovableButNonCopyable const&) =
delete;
MovableButNonCopyable(MovableButNonCopyable&&) = default;
MovableButNonCopyable& operator=(MovableButNonCopyable&&) = default;
};
MovableButNonCopyable construct() { return MovableButNonCopyable(); }
int main()
{
boost::packaged_task<MovableButNonCopyable> pt(construct);
pt();
}
}}}
'''Problems with the test case:'''[[BR]]
The test case in trunk was totally broken. The main part of the test case
looked like this:
{{{
int main() {
boost::packaged_task<MovableButNonCopyable>(MovableButNonCopyable());
}
}}}
This is attempting to package up a `MovableButNonCopyable` as a task that
returns a `MovableButNonCopyable`. `MovableButNonCopyable` is not
callable, so this will never work. To make the test case work, it must be
changed to:
{{{
MovableButNonCopyable construct() { return MovableButNonCopyable(); }
int main() {
boost::packaged_task<MovableButNonCopyable> pt(construct);
pt();
}
}}}
'''Problems with the test runner:'''[[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]]
The Jamfile for the Boost.Thread test suite causes the the `-ansi` flag to
be added when building with clang or gcc. This overrides any `-std=c++11`
flags that may have been added, and means that the relevant part of
`test_6174.cpp` 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 `-ansi` flag.
'''Other unrelated problems:'''[[BR]]
After the `-ansi` 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
`sync/mutual_exclusion/locks/unique_lock/obs/op_int_fail.cpp`. 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 `6174`.
Also, I did not understand the reasoning behind the `-ansi` flag being
present. It was also present in the Jamfile for the main Boost.Thread
build, but it did not seem necessary.
'''The solution:'''[[BR]]
I have attached a patch which modifies `mark_finished_with_result`,
`test_6174.cpp` and the test Jamfile to fix the problems described above.
-- Ticket URL: <https://svn.boost.org/trac/boost/ticket/6174#comment:13> Boost C++ Libraries <http://www.boost.org/> Boost provides free peer-reviewed portable C++ source libraries.
This archive was generated by hypermail 2.1.7 : 2017-02-16 18:50:10 UTC