I have a program which uses an io_service and several threads.
It instantiates some number of socket objects. These objects each have a strand for synchronization. All calls to async_read(), async_write(), and similar functions go through strand_.wrap(boost::bind(…)). Each object also has an int interlock_ variable that is initialized to 0.
Inside one of these functions (the on-data-receive callback), I do the following:
Class::startRead(...)
{
...
boost::asio::async_read(socket_, boost::asio::buffer(ptr, 16384), boost::asio::transfer_at_least(1),
strand_.wrap(boost::bind(&EagerConnection::on_read, this, placeholders::error, placeholders::bytes_transferred)));
}
Class::on_read(...)
{
...
startRead();
assert(0 == __sync_fetch_and_add(&interlock_, 1));
onData_();
assert(1 == __sync_fetch_and_add(&interlock_, -1));
}
Because everything is synchronized through the strand, that first assert should never fire. However, it does fire! When I check the value in GDB, the end value of interlock_ is 2, which means that two separate calls to on_read() are active at the same time.
Does this mean that boost::asio::strand is broken? (I’ve already checked that I don’t have any re-entrancy within the completion function — the onData_ signal handler does not re-call on_data()).
Can the “early” startRead somehow cause an immediate re-entry? (Both the semantics of async_x and strand seem to indicate it can’t)
If you really, really want to see the full context of the class, it’s available as a gist: https://gist.github.com/979212
I have spotted a few minor(?) issues:
Minor: The initialization order of
interlock_andstrand_is switched. Fix it by declaringinterlock__after_the strand_ member;The
readInfunction returns no value (uninitialized data). You probably intend to returnn?Good news:
boost::asioandboost::signalsinternals).