开发者

Is boost::asio::strand broken on Ubuntu 11.04 (boost_all_dev 1.42)

开发者 https://www.devze.com 2023-03-07 00:57 出处:网络
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_wr

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_ and strand_ is switched. Fix it by declaring interlock_ _after_ the strand_ member;

  • The readIn function returns no value (uninitialized data). You probably intend to return n?


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 and boost::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
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号