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 co开发者_高级运维ntext 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
readIn
function returns no value (uninitialized data). You probably intend to returnn
?
Good news:
- Running with valgrind turned up clear.
- Running with helgrind turned up clear (but: I'm not using threads in my minimal example, I guess; Don't know about
boost::asio
andboost::signals
internals).
I am trying to reproduce things, but my installation fails to raise the asserts when doing this.
I tacked on the following fragment at the end of the gist:
int split(std::string const &src, char ch, std::string &oLeft, std::string &oRight)
{
std::size_t pos = src.find(ch);
if (pos == std::string::npos)
{
oLeft = src;
oRight.clear();
return 1;
} else
{
oLeft = src.substr(0, pos);
oRight = src.substr(pos+1);
return 2;
}
}
namespace {
boost::asio::io_service svc;
EagerConnection c(svc);
void onconnect()
{
std::cout << "ONCONNECT" << std::endl;
const char data[] = "GET / HTTP/1.0\r\n\r\n";
c.writeOut(data, sizeof(data));
}
void ondata()
{
std::cout << "ONDATA" << std::endl;
std::ostringstream oss;
char buf[1024];
int read;
while ((read = c.readIn(buf, 1024)))
oss.write(buf, read);
std::cout << "response: " << oss.str() << std::endl;
}
void ondisconnect()
{
std::cout << "ON__DIS__CONNECT" << std::endl;
}
}
int main(int argc, char* argv[])
{
if (argc>1 && argv[1])
{
c.onConnect_.connect(&onconnect);
c.onData_.connect(&ondata);
c.onDisconnect_.connect(&ondisconnect);
c.open(argv[1]);
svc.run();
}
return 0;
}
As you can see, I'm really trying to do the SimplestThingThatCouldPossiblyWork. My connect/reconnect is working nicely (including the increasing backoff time).
I compile this with
strand: strand.cpp
g++ -Wall -Werror -o $@ $^ -g -O0 -lboost_system -lboost_thread -lboost_signals -lpthread
And invoke it with
./strand 127.0.0.1:6767
I have a responding script sitting there doing (basically)
netcat -l -p 6767 -e rev
One other thing to note: the write buffer never seems to actually be sent/flushed until I interrupt the strand
tester (client side). This happens regardless how large I make data
... This is probably due to a step I'm missing?
Edit:
Tested identical on
- ubuntu meerkat, gcc 4.4.5, boost 1.42.0
- debian sid, gcc 4.5.2-8, boost 1.46.1
精彩评论