Pracuję dla iOS w XCode 4.6. Piszę bibliotekę dla usługi i używam boost, aby rozpocząć wątki. Jedną z moich metod wygląda następująco:Zły dostęp w boost :: future <>. Then then() po uzyskaniu dostępu do danej przyszłości
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;
});
}
Podczas wykonywania tego urządzenia dostaję EXC_BAD_ACCESS
w zaznaczonym wierszu. Bardzo mnie to zaskoczyło, ponieważ pierwsze wywołanie onConnected
zakończyło się sukcesem i nawet jeśli dodam wywołanie onError
przed if
, to także działa.
Będąc dość niedoświadczonym w C++, byłbym zadowolony z każdej informacji o tym, co jest powodem, jak go debugować i jak być świadomym, czy ten problem będzie następnym razem. Również nie jestem pewien, które informacje są istotne. To, co wymyśliłem, może być istotne z tego, co odkryłem tak daleko, że mogą być następujące: ResultListener
i Service
są boost::noncopyable
. Sprawdziłem liczbę referencyjną shared_ptr
(używając use_count
) i wzrasta ona w ramach kontynuacji. Używam doładowania 1.53. Metoda nazywa się tak
Servuce reco(/* ... */);
boost::shared_ptr<foo> f(new foo());
reco.start(f);
foo
jest prosta klasa, która nie robi nic innego, jak drukować na std::cout
jeśli metoda jest wywoływana.
Edit: Dalsze węszyć pozwól, aby sprawdzić połączenie get()
i znalazłem następujący kod w future.hpp
wykonywane:
// 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();
}
myślę, że to jest problem. Wywołanie reset()
wydaje się zwalniać pamięć future_
shared_ptr
. Przypuszczam, że to oznacza pamięć kontynuacji nadal działa, ponieważ nie jest używany w systemie operacyjnym, a tym samym unieważnia wskaźnik, który jest następnie odpowiedzialny za dostęp do pamięci poza jego zasięg. Czy to założenie jest poprawne? Czy mogę jakoś tego uniknąć lub czy jest to błąd w dopalaniu?
Edit 2: Poniżej znajduje się minimalny przykład tworzenie problemu:
#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 dodał dwa screeny wziąłem w Xcode pokazać sytuację w przyszłości, w której conitnuation pracuje i wydaje że Mankarnas (w komentarzach) i ja (powyżej) są poprawne: Wydaje się, że część pamięci, w której przechowywana jest kontynuacja, zostaje uwolniona i dlatego pojawia się niezdefiniowane zachowanie.
Jest to sytuacja przed get()
nazywa się:
Jest to sytuacja po get()
nazwano:
Adres px
wskazuje jest 0x00
później.
czy sprawdziłeś 'onError'? – inf
Co masz na myśli? Że zawiera błędny kod? Składa się z 'std :: cout <<" foo ";' i działa, jeśli umieściłem wywołanie 'onError' przed wywołaniem' get() 'przyszłości. – Stephan
Czy 'listener.get()' ma wartość inną niż null przed linią, która ulega awarii? (Nie mogę odtworzyć problemu lokalnie, prawdopodobnie byłby przydatny, gdybyś mógł skonstruować kompletną wersję testową, która eksponuje problem dla ciebie, więc nie muszę zgadywać na temat części, których nie opisałeś). – Mankarse