I'm developing for iOS in XCode 4.6. I'm writing a library for a service and use boost to start threads. One of my methods looks like this:
void Service::start(boost::shared_ptr<ResultListener> listener) {
boost::future<bool> con = boost::make_future(true);
listener->onConnected(); // Works
boost::future<bool> initDone = con.then([&, listener](boost::future<bool>& connected) {
listener->onConnected(); // Works
if (!connected.get()) {
listener->onError("...");
return false;
}
listener->onError("..."); // EXC_BAD_ACCESS
/* ... */
return true;
});
}
When executing this on the device I get an EXC_BAD_ACCESS
at the marked line. I'm very suprised by this, since the first call to onConnected
is successful and even if I add an onError
call before the if
that one is also working.
Being quite inexperienced with C++ I would be happy about every piece of information on what the reason is, how to debug it and how to be aware if this issue the next time around. Also I'm not quite sure which information is relevant. What I figured can be relevant from what I've found so far out there, the following might: ResultListener
and Service
are boost::noncopyable
. I checked the reference count of the shared_ptr
(using use_count
) and it increases within the continuation. I'm using boost 1.53. The method is called like this
Servuce reco(/* ... */);
boost::shared_ptr<foo> f(new foo());
reco.start(f);
foo
being a simple class which does nothing else but print to std::cout
if a method is called.
Edit: Further snooping around let me to inspect the get()
call and I found the following code in future.hpp
being executed:
// retrieving the value
move_dest_type get()
{
if(!this->future_)
{
boost::throw_exception(future_uninitialized());
}
future_ptr fut_=this->future_;
this->future_.reset();
return fut_->get();
}
I think this is the problem. The call to reset()
seems to frees the memory of the future_
shared_ptr
. My guess is, this marks the memory the continuation is still running in as not used for the OS and thus invalidates the listener
pointer which is then caugt as memory access out of its scope. Is this assumption correct? Can I somehow avoid this or is this a bug in boost?
Edit 2: The following is a minimal example creating the problem:
#define BOOST_THREAD_VERSION 4
#include <boost/thread.hpp>
class Test {
public:
void test() {
boost::shared_ptr<Test> listener(new Test());
boost::future<bool> con = boost::make_future(true);
listener->foo(); // Works
boost::future<bool> initDone = con.then([listener](boost::future<bool>& connected) {
listener->foo(); // Works
if (!connected.get()) {
listener->foo();
return false;
}
listener->foo(); // EXC_BAD_ACCESS
return true;
});
}
void foo() {
std::cout << "foo";
}
};
I added two screenshots I took in XCode to show the situation in the future in which the conitnuation is running and it seems that Mankarnas (in the comments) and me (above) are correct: It seems the memory part in which the continuation is stored is freed and therefore undefined behaviour occurs.
This is the situation before get()
is called:
This is the situation after get()
was called:
The address px
points to is 0x00
afterwards.
I opened a ticket against boost 1.53 and it was confirmed as a bug. It seems that future.then
is not yet stable and thus not ready for production use. One advice from there was to use
#define BOOST_THREAD_DONT_PROVIDE_FUTURE_INVALID_AFTER_GET
But it was clearly stated that this feature is not stable yet (and that the documentation is lacking this bit of information).
I have switched now to use a separate thread in which I'll wait for the future and execute the proper actions then.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With