2011-10-27 7 views
5

Sto usando un esempio di coda di blocco che ho ricevuto da questo sito Web, ritenendolo piuttosto carino. Questa coda di blocco utilizza boost :: mutex. E 'a volte un'eccezione:boost mutex lancio (dispari?) Eccezione

terminate called after throwing an instance of 'boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::system::system_error> >' 

cosa(): file descriptor Bad

Ecco il codice coda Blocco:

#include <boost/thread/mutex.hpp> 
#include <boost/thread/thread.hpp> 
#include <boost/thread/condition_variable.hpp> 
#include <exception> 
#include <list> 
#include <stdio.h> 

struct BlockingQueueTerminate 
    : std::exception 
{}; 

namespace tools { 
    template<class T> 
    class BlockingQueue 
    { 
    private: 
    boost::mutex mtx_; 
    boost::condition_variable cnd_; 
    std::list<T> q_; 
    unsigned blocked_; 
    bool stop_; 

    public: 
    BlockingQueue() 
     : blocked_() 
     , stop_() 
    {} 

    ~BlockingQueue() 
    { 
     this->stop(true); 
    } 

    void stop(bool wait) 
    { 
     // tell threads blocked on BlockingQueue::pull() to leave 
     boost::mutex::scoped_lock lock(mtx_); 
     stop_ = true; 
     cnd_.notify_all(); 

     if(wait) // wait till all threads blocked on the queue leave BlockingQueue::pull() 
    while(blocked_) 
     cnd_.wait(lock); 
    } 

    void put(T t) 
    { 
     boost::mutex::scoped_lock lock(mtx_); // The exception is thrown here ! 
     q_.push_back(t); 
     cnd_.notify_one(); 
    } 

    T pull() 
    { 
     boost::mutex::scoped_lock lock(mtx_); 
     ++blocked_; 
     while(!stop_ && q_.empty()) 
    cnd_.wait(lock); 
     --blocked_; 

     if(stop_) { 
    cnd_.notify_all(); // tell stop() this thread has left 
    throw BlockingQueueTerminate(); 
     } 

     T front = q_.front(); 
     q_.pop_front(); 
     return front; 
    } 
    }; 
} 

Chiunque può individuare cosa sta andando male qui? perché ho provato tutto il giorno a scoprirlo invano. Credo di aver bisogno di un occhio esterno per vederlo. Cerca il commento '// L'eccezione è lanciata qui!' per vedere dove si verifica esattamente il problema.

EDIT 1:

Il contesto: sto usando questa coda di blocco al fine di creare un involucro async MySQL.

Ecco la mia MySQL.hh

#ifndef MYSQL_HH_ 
# define MYSQL_HH_ 
# include <boost/asio.hpp> 
# include <boost/thread.hpp> 
# include <boost/function.hpp> 
# include <mysql++/mysql++.h> 
# include <queue> 
# include "async_executor.hh" 
# include "BlockingQueue.hh" 

class t_mysql_event { 
public: 
    t_mysql_event(std::string query, boost::function<void(mysqlpp::StoreQueryResult)> cb) : 
    m_query(query), m_store_cb(cb), m_store_bool(true) {} 

    t_mysql_event(std::string query, boost::function<void()> cb) : 
    m_query(query), m_exec_cb(cb), m_store_bool(false) {} 

    bool is_store_query() { 
    return m_store_bool; 
    } 

    std::string toString() { 
    return m_query; 
    } 

    std::string      m_query; 
    boost::function<void(mysqlpp::StoreQueryResult)> m_store_cb; 
    boost::function<void()>    m_exec_cb; 

private: 
    bool       m_store_bool; 
}; 

namespace pools { 
    class MySQL { 
    public: 
    ~MySQL() {} 

    static MySQL* create_instance(boost::asio::io_service& io); 

    static MySQL* get_instance(); 

    void exec(std::string query, boost::function<void()> cb); 
    void store(std::string query, boost::function<void(mysqlpp::StoreQueryResult)> cb); 

    private: 
    MySQL(boost::asio::io_service& io) : executor(io, 100), parent_io(io), m_strand(io) 
    { 
     for (int i=0; i < 100; ++i) { 
    boost::thread(boost::bind(&MySQL::retreive, this)); 
     } 
    } 

    void async_exec(std::string query, boost::function<void()> cb, mysqlpp::Connection& conn); 
    void async_store(std::string query, boost::function<void(mysqlpp::StoreQueryResult)> cb, mysqlpp::Connection& conn); 

    void retreive(); 

    private: 
    tools::async_executor   executor; 
    boost::asio::io_service&  parent_io; 
    boost::asio::strand   m_strand; 
    tools::BlockingQueue<t_mysql_event*> m_events; 
    std::queue<mysqlpp::Connection*> m_stack; 
    }; 
} 

#endif //MYSQL_HH_ 

Ecco il MySQL.cc:

#include "MySQL.hh" 

static pools::MySQL* _instance = 0; 

namespace pools { 


    MySQL* MySQL::create_instance(boost::asio::io_service& io) { 
    if (!_instance) 
     _instance = new MySQL(io); 
    return _instance; 
    } 

    MySQL* MySQL::get_instance() { 
    if (!_instance) { 
     exit(1); 
    } 
    return _instance; 
    } 

    void MySQL::exec(std::string query, boost::function<void()> cb) { 
    m_events.put(new t_mysql_event(query, cb)); 
    } 

    void MySQL::store(std::string query, boost::function<void(mysqlpp::StoreQueryResult)> cb) { 
    m_events.put(new t_mysql_event(query, cb)); 
    } 

    void MySQL::retreive() { 
    mysqlpp::Connection conn("***", "***", "***", "***"); 
    for(;;) { 
     t_mysql_event *event = m_events.pull(); 
     if (event->is_store_query()) 
    async_store(event->m_query, event->m_store_cb, conn); 
     else 
    async_exec(event->m_query, event->m_exec_cb, conn); 
     delete event; 
    } 
    } 

    void MySQL::async_exec(std::string query, boost::function<void()> cb, mysqlpp::Connection& conn) { 
    mysqlpp::Query db_q = conn.query(query.c_str()); 
    db_q.exec(); 
    parent_io.post(cb); 
    } 

    void MySQL::async_store(std::string query, boost::function<void(mysqlpp::StoreQueryResult)> cb, mysqlpp::Connection& conn) { 
    mysqlpp::Query db_q = conn.query(query.c_str()); 
    mysqlpp::StoreQueryResult res = db_q.store(); 
    parent_io.post(boost::bind(cb, res)); 
    } 
} 

seguito:

class MyClass { 
public: 
    MyClass() : _mysql(pools::MySQL::get_instance()) {} 

    startQueries(); 
private: 
    void Query1() { 
     std::stringstream query(""); 
     query << "INSERT INTO Table1 ***"; 
     _mysql->exec(query.str(), 
        boost::bind(&MyClass::Query2, this, _1)); 
    } 
    void Query2() { 
     std::stringstream query(""); 
     query << "INSERT INTO Table2 ***"; 
     _mysql->exec(query.str(), 
        boost::bind(&MyClass::Query3, this, _1)); 
    } 
    void Query3() { 
     std::stringstream query(""); 
     query << "INSERT INTO Table3 ***"; 
     _mysql->exec(query.str(), 
        boost::bind(&MyClass::done, this, _1)); 
    } 
    void done() {} 
    pools::MySQL *_mysql; 
}; 

Sperando che risponderà a qualche richiesta di più informazioni ..

Funny thing:

Se sostituisco ogni _mysql di pools :: MySQL :: get_instance(), non sembra che si verifichi un arresto anomalo. Ma ho il sospetto c'è un errore di gran lunga più importante di sotto di quello ...

+0

Sei sicuro che sia questo codice e non quello che non hai pubblicato? –

+0

Sì, sono abbastanza sicuro che l'eccezione derivi esattamente da questo punto. Ho usato gdb e un sacco di std :: cout per essere sicuro prima di chiedere. – TheSquad

+0

Questo è strano, perché non penso che i costruttori possano lanciare un'eccezione: http://www.boost.org/doc/libs/1_37_0/doc/html/boost/interprocess/scoped_lock.html#id2914282-bb –

risposta

0

questa eccezione può essere generata se coda è già distrutta, ma si tenta di chiamare il suo metodo put. Verifica questo mettendo un breakpoint (o dichiarazione di stampa) nel distruttore della coda.

+0

Non viene distrutto poiché è un oggetto come attributo in una classe singleton. Vero che non lo sapevi. Ho aggiunto molte più informazioni nel mio post. – TheSquad

+0

@Andy T: genererebbe davvero un'eccezione? Immagino che preferirebbe segfault. – Atmocreations

+0

@Atmocreations Se il codice boost :: mutex rileva uno stato non valido può generare un'eccezione. L'indirizzo dell'oggetto può ancora essere indirizzi validi associati al processo anche dopo che l'oggetto è stato cancellato. Se questo è il caso, non si verificherà alcun errore di seg. – selalerer