[Boost-users] boost::asio destructor segfault?

952 views
Skip to first unread message

Sean McAllister

unread,
Mar 18, 2010, 2:36:16 PM3/18/10
to boost-users
I've got a class that's using ASIO to run a simple server listening for command messages from a remote interface.

It's got some private members to help with this (class abridged for shortness):

class ClassifierContext {
private:
    io_service      ioService_;
    t_socket_shptr  socket_;
};

It runs it's event loop in it's own thread (started with boost::thread)

// Hosts thread to run IO event loop
void ClassifierContext::io_loop() {
  try {
    connect(); 
  } catch (std::exception &e) {
    RERROR("Error connecting to site server -- %s", e.what());
  }

  // Work object prevents run() from stopping when it runs out of work
  asio::io_service::work work(ioService_);
  ioService_.run();
}

And it periodically sends data back along a single socket associated with the ioService and also has
an asynchronous read pending to listen for command messages (I can post this code if needed, it's just a couple async_sends and an async_read)

The problem I'm seeing comes when the destructor for the class is run:

ClassifierContext::~ClassifierContext() {
  ioService_.stop();
}

Periodically (every 20 times or so) I get a segfault when the destructor for ioService_ is called.  A representative stack trace looks like this:

#0  0x0921bfd6 in ?? ()
#1  0x08125877 in boost::asio::detail::reactor_op_queue<int>::destroy_operations (
    this=0x8ec5f40)
    at opt/linux/include/boost/asio/detail/reactor_op_queue.hpp:268
#2  0x081274ad in boost::asio::detail::epoll_reactor<false>::shutdown_service (
    this=0x8ec5ed8)
    at opt/linux/include/boost/asio/detail/epoll_reactor.hpp:119
#3  0x08127b3b in ~service_registry (this=0x8ec5958)
    at opt/linux/include/boost/asio/detail/service_registry.hpp:75
#4  0x08127c29 in ~io_service (this=0x8492360)
    at opt/linux/include/boost/asio/impl/io_service.ipp:62
#5  0x08117110 in ~ClassifierContext (this=0x8492360)
    at ClassifierContext.cpp:91
#6  0xb781cbb9 in exit () from /lib/tls/i686/cmov/libc.so.6
#7  0xb780477d in __libc_start_main () from /lib/tls/i686/cmov/libc.so.6
#8  0x081079c1 in _start () at ../sysdeps/i386/elf/start.S:119

Has anyone seen a segfault like this before?  It's intermittent so that tells me there's some kind of race condition.  Any idea how I can work around it?

Rutger ter Borg

unread,
Mar 18, 2010, 4:32:35 PM3/18/10
to boost...@lists.boost.org
Sean McAllister wrote:

> Has anyone seen a segfault like this before? It's intermittent so that
> tells me there's some kind of race condition. Any idea how I can work
> around it?

Could it be a dangling event handler that is being called? Do you use
shared_ptr/shared_from_this?

Cheers,

Rutger

_______________________________________________
Boost-users mailing list
Boost...@lists.boost.org
http://lists.boost.org/mailman/listinfo.cgi/boost-users

Sean McAllister

unread,
Mar 18, 2010, 5:53:18 PM3/18/10
to boost...@lists.boost.org
Not using a shared_ptr_from_this, but I am using shared_ptr wrapped objects as parameters to my asynchronous callbacks...

Rutger ter Borg

unread,
Mar 19, 2010, 2:50:34 AM3/19/10
to boost...@lists.boost.org
Sean McAllister wrote:

> Not using a shared_ptr_from_this, but I am using shared_ptr wrapped
> objects as parameters to my asynchronous callbacks...
>

If you use patterns like

boost::bind( &some_class::some_mem_fn, this, .... )

then it could be that the instance of some_class is destructed before the
bind-expression is. If the handler is called with "operation cancelled"
(which should happen with all waiting handlers if you call
io_service.stop()), it tries to make a call into freed memory -- you can
expect a crash, indeed. To solve this, one can use shared_from_this(),

boost::bind( &some_class::some_mem_fn, shared_from_this(), .... )

to make sure the object's lifetime will be at least that of the incomplete
handler.

Sean McAllister

unread,
Mar 19, 2010, 10:25:40 AM3/19/10
to boost...@lists.boost.org
The only functions I'm binding with boost::bind are members of ClassifierContext, eg:

ICMessage::async_read(socket_, 
                              boost::bind(&ClassifierContext::handleCmdMsg, this,
                     _1,_2,_3));

Since the io_service object is stopped in the destructor of ClassifierContext, shouldn't that be OK?  Or should
I still try to create a shared_from_this off of the ClassifierContext class?

Rutger ter Borg

unread,
Mar 19, 2010, 10:45:57 AM3/19/10
to boost...@lists.boost.org
Sean McAllister wrote:

> The only functions I'm binding with boost::bind are members of
> ClassifierContext, eg:
>
> ICMessage::async_read(socket_,
>
boost::bind(&ClassifierContext::handleCmdMsg,
> this,
> _1,_2,_3));
>
> Since the io_service object is stopped in the destructor of
> ClassifierContext, shouldn't that be OK? Or should
> I still try to create a shared_from_this off of the ClassifierContext
> class?
>

Not sure, given you are running multiple threads. According to the
documentation, io_service::stop() doesn't block, so it could still be
needing the shared_from_this stuff.

Another approach is to allocate the work object in your member io_loop of
ClassifierContext, and in the destructor, do something like

m_work_ptr.reset();
ioService_.run();

this makes sure all outstanding operations are finished.

Sean McAllister

unread,
Mar 19, 2010, 10:54:37 AM3/19/10
to boost...@lists.boost.org
On Fri, Mar 19, 2010 at 7:45 AM, Rutger ter Borg <rut...@terborg.net> wrote:
Sean McAllister wrote:

> The only functions I'm binding with boost::bind are members of
> ClassifierContext, eg:
>
> ICMessage::async_read(socket_,
>
boost::bind(&ClassifierContext::handleCmdMsg,
> this,
>                      _1,_2,_3));
>
> Since the io_service object is stopped in the destructor of
> ClassifierContext, shouldn't that be OK?  Or should
> I still try to create a shared_from_this off of the ClassifierContext
> class?
>

Not sure, given you are running multiple threads. According to the
documentation, io_service::stop() doesn't block, so it could still be
needing the shared_from_this stuff.

Another approach is to allocate the work object in your member io_loop of
ClassifierContext, and in the destructor, do something like

m_work_ptr.reset();
ioService_.run();

this makes sure all outstanding operations are finished.

Cheers,

Rutger



Hmmm, I've only got the one extra thread to run the asynchronous event loop in.  If I have
async_read events outstanding, wouldn't the above code cause my destructor to block 
until I've read another message (which may happen quite infrequently)?

Rutger ter Borg

unread,
Mar 19, 2010, 11:36:08 AM3/19/10
to boost...@lists.boost.org
Sean McAllister wrote:
>
> Hmmm, I've only got the one extra thread to run the asynchronous event
> loop
> in. If I have
> async_read events outstanding, wouldn't the above code cause my destructor
> to block
> until I've read another message (which may happen quite infrequently)?
>

That's right -- but you still would like to have all operations finished
before the end of the destructor. If you need to close sockets, call, e.g.,
socket::close.

Cheers,

Rutger

Sean McAllister

unread,
Mar 19, 2010, 11:39:09 AM3/19/10
to boost...@lists.boost.org
On Fri, Mar 19, 2010 at 8:36 AM, Rutger ter Borg <rut...@terborg.net> wrote:
Sean McAllister wrote:
>
> Hmmm, I've only got the one extra thread to run the asynchronous event
> loop
> in.  If I have
> async_read events outstanding, wouldn't the above code cause my destructor
> to block
> until I've read another message (which may happen quite infrequently)?
>

That's right -- but you still would like to have all operations finished
before the end of the destructor. If you need to close sockets, call, e.g.,
socket::close.

Cheers,

Rutger

Oh duh, I should be calling stop on my io_service object and then joining the thread that
has the event loop waiting for it to exit.  Doing that seems to have solved the problem.
Reply all
Reply to author
Forward
0 new messages