5

I am using a blocking queue example I got from this website, thinking it was pretty nice. This blocking queue is using boost::mutex. It is sometime throwing an exception :

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

what(): Bad file descriptor

Here's the Blocking Queue code :

#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;
    }
  };
}

Anyone can spot what's going wrong here ? because I have tried the all day figuring it out in vain. I guess I need a outside eye to see it. Look for the comment '//The exception is thrown here !' to see where exactly the problem occurs.

EDIT 1 :

The context : I'm using this blocking queue in order to create a MySQL async wrapper.

Here's my 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_

Here's the 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));
   }
}

Afterwards :

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;
};

Hoping that will answer to some request for more informations...

Funny thing :

If I replace every _mysql by pools::MySQL::get_instance() I does not seems to crash. But I suspect there is an error far more important below that...

TheSquad
  • 7,385
  • 8
  • 40
  • 79
  • Are you sure it is this code, and not the one you didn't post? – BЈовић Oct 27 '11 at 19:26
  • Yes I'm pretty sure the exception is throwing from this point exactly. I have used gdb and a lot of std::cout to be sure before asking. – TheSquad Oct 27 '11 at 19:34
  • That's weird, cause I don't think constructors can throw an exception : http://www.boost.org/doc/libs/1_37_0/doc/html/boost/interprocess/scoped_lock.html#id2914282-bb – BЈовић Oct 27 '11 at 21:13
  • I'm not sure the exception is sent from the constructor, but most likely from the boost::mutex object. As you say, it is weird, reason I choose to put (odd?) on the title – TheSquad Oct 27 '11 at 21:38
  • 1
    I would like to point out, that although exception can be raised from here, the bug might not be here actually. Maybe somewhere else you are using the class incorrectly, or even trashing your memory or stack? .... – CygnusX1 Oct 29 '11 at 21:39
  • 3
    @TheSquad Please edit your question with a [minimally complete example](http://sscce.org/) exhibiting the problem. There is nothing obviously wrong with the code you have posted. At a minimum, include the stack trace where the exception is thrown. As other comments indicate, I suspect the bug is elsewhere in your program. – Sam Miller Oct 30 '11 at 02:37
  • @CygnusX1 I don't think I'm using it wrong, you tell me... post edited with more information – TheSquad Oct 30 '11 at 19:05
  • @Sam Miller : Here you go, Hope it is enough information to find my error – TheSquad Oct 30 '11 at 19:06
  • @TheSquad your example code is not minimally complete. Help us help you by posting something that we can **compile** to reproduce the problem you describe. – Sam Miller Oct 30 '11 at 19:27
  • @Sam Miller : Will work asap on an "minimally complete" example that unfortunately will not be exactly as my current project that I can't disclose. Hoping that I'll be able to reproduce the issue. – TheSquad Oct 30 '11 at 20:02
  • You should try valgrind or similar tools, I am sure they will show up some use-after-release. – PlasmaHH Nov 03 '11 at 22:00
  • @PlasmaHH : Used Valgring Eletric-fence, everything I knew... it really seems to be a bit special exception... – TheSquad Nov 03 '11 at 22:32
  • @TheSquad: then maybe try digging up in the source code what variable values cause that exception and track its origin back, maybe with gdb reverse debugging. – PlasmaHH Nov 04 '11 at 09:12

1 Answers1

0

this exception can be thrown if queue is already destroyed but you try to call its put method. Check this by putting a breakpoint (or print statement) in queue destructor.

Andriy Tylychko
  • 15,967
  • 6
  • 64
  • 112
  • It is not destroyed since it is an object as attribute in a singleton class. True you did not knew that. I added a lot more information in my post. – TheSquad Oct 30 '11 at 19:02
  • @Andy T: Would it really throw an exception? I guess it would rather segfault. – Atmocreations Nov 04 '11 at 19:24
  • @Atmocreations If the boost::mutex code detect an invalid state it may throw an exception. The object address may still be valid addresses mapped to the process even after the object has been deleted. If this is the case, no seg-fault will occur. – selalerer Nov 05 '11 at 15:30
  • @TheSquad Maybe you overrun _mysql somewhere and it returns an invalid address, while using get_instance() keep getting you the right address. – selalerer Nov 05 '11 at 15:36
  • @selalerer : The thing is the MySQL object is only created on the main.cc with a create_instance(io_service), get_instance() will only return the instance created by create_instance. Since It is only called in the main.cc (I have checked to see if I had not put it anywhere else) it can't be overrun. – TheSquad Nov 05 '11 at 22:38
  • @TheSquad I was talking about MyClass::_mysql member that maybe gets overrun. – selalerer Nov 06 '11 at 12:15
  • @selalerer : There is no where it is set a second time. Only on the constructor. Afterwards I only access it with _mysql-> – TheSquad Nov 06 '11 at 14:35
  • @TheSquad Try maybe putting a data-break-point on it and see in the debugger when it is getting accessed. – selalerer Nov 06 '11 at 15:26
  • @selalerer : already done that, gdb returns me the correct address and the object is accessible correctly from gdb. – TheSquad Nov 06 '11 at 16:30