Sto sviluppando per iOS in XCode 4.6. Sto scrivendo una libreria per un servizio e uso boost per avviare i thread. Uno dei miei metodi si presenta così:Accesso non valido in boost :: future <>. Then() dopo l'accesso a date future
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;
});
}
Quando si esegue questo sul dispositivo ottengo una EXC_BAD_ACCESS
alla linea marcata. Sono molto sorpreso da questo, dal momento che la prima chiamata a onConnected
ha esito positivo e anche se aggiungo una chiamata onError
prima del if
anche quella funziona.
Essendo abbastanza inesperto con C++, sarei felice di ogni informazione su quale sia la ragione, su come eseguirne il debug e su come essere consapevole di questo problema la prossima volta. Inoltre non sono abbastanza sicuro di quali informazioni siano rilevanti. Quello che ho capito può essere rilevante da quello che ho trovato finora là fuori, il seguente potrebbe: ResultListener
e Service
sono boost::noncopyable
. Ho controllato il conteggio dei riferimenti dello shared_ptr
(usando use_count
) e aumenta nel seguito. Sto usando boost 1.53. Il metodo viene chiamato in questo modo
Servuce reco(/* ... */);
boost::shared_ptr<foo> f(new foo());
reco.start(f);
foo
essere una semplice classe che non fa altro che stampare su std::cout
se un metodo viene chiamato.
Edit: Ulteriori curiosare in giro mi permetta di ispezionare la chiamata get()
e ho trovato il seguente codice nel future.hpp
in esecuzione:
// 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();
}
Credo che questo sia il problema. La chiamata a reset()
sembra liberare la memoria dello future_
shared_ptr
. La mia ipotesi è, questo segna la memoria la continuazione è ancora in esecuzione in quanto non utilizzato per il sistema operativo e quindi invalida il puntatore listener
che viene quindi caugt come accesso alla memoria al di fuori del suo ambito. Questa supposizione è corretta? Posso in qualche modo evitare questo o è un bug in boost?
Edit 2: Il seguente è un esempio minimo creando il problema:
#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";
}
};
ho aggiunto due screenshot presi in XCode per mostrare la situazione nel futuro in cui la conitnuation è in esecuzione e sembra che Mankarnas (nei commenti) e me (sopra) sono corretti: sembra che la parte di memoria in cui è archiviata la continuazione sia liberata e quindi si verifichi un comportamento indefinito.
Questa è la situazione prima get()
si chiama:
Questa è la situazione dopo get()
è stato chiamato:
L'indirizzo px
indica è 0x00
dopo.
hai controllato "onError'? – inf
Cosa intendevi? Che contiene codice errato? Consiste di 'std :: cout <<" foo ";' e funziona se metto la chiamata a 'onError' prima della chiamata 'get()' del futuro. – Stephan
È 'listener.get()' non null prima della riga che si blocca? (Non riesco a riprodurre il problema localmente, sarebbe probabilmente utile se tu potessi costruire una prova _complete_ che presenta il problema per te, quindi non devo indovinare le parti che non hai descritto). – Mankarse