Async wait on file descriptor using Boost Asio
Asked Answered
P

1

32

I'm trying to integrate D-Bus with my boost::asio application.

D-Bus has an API that enumerates a set of Unix file descriptors (mainly sockets but could also be FIFOs) to be watched. When those descriptors have something to be read I should inform the D-Bus API so it can read them and do it's thing.

Currently I'm doing this:

using boost::asio::posix::stream_descriptor;
void read_handle(stream_descriptor* desc, const boost::system::error_code& ec,
                 std::size_t bytes_read)
{
    if (!ec) {
        stream_descriptor::bytes_readable command(true);
        descriptor->io_control(command);
        std::size_t bytes_readable = command.get();
        std::cout << "It thinks I should read" << bytes_readable
            << " bytes" << std::endl;
    } else {
        std::cout << "There was an error" << std::endl;
    }
}

void watch_descriptor(boost::asio::io_service& ios, int file_descriptor)
{
    // Create the asio representation of the descriptor
    stream_descriptor* desc = new stream_descriptor(ios);
    desc->assign(file_descriptor);

    // Try to read 0 bytes just to be informed that there is something to be read
    std::vector<char> buffer(0);
    desc->async_read_some(boost::asio::buffer(buffer, 0),
        boost::bind(read_handle, desc, _1, _2));
}

But the handler is called right away saying that it has 0 bytes to be read. I would like it to be called only when there is something to be read, but boost::asio CAN NOT read it. It should act just as a glorified select(). Is there a simple way to do that?

PS: I'm extensively using boost::asio in my software, this is just a small part of it, so I would like not to depend on glib or other mainloops.

Pivotal answered 13/1, 2011 at 22:36 Comment(1)
What is the D-Bus API that you're using? Is it the low-level C API?Depreciate
D
34

This is precisely the problem null_buffers was designed for.

Sometimes a program must be integrated with a third-party library that wants to perform the I/O operations itself. To facilitate this, Boost.Asio includes a null_buffers type that can be used with both read and write operations. A null_buffers operation doesn't return until the I/O object is "ready" to perform the operation.

As an example, to perform a non-blocking read something like the following may be used:

ip::tcp::socket socket(my_io_service);
...
ip::tcp::socket::non_blocking nb(true);
socket.io_control(nb);
...
socket.async_read_some(null_buffers(), read_handler);
...
void read_handler(boost::system::error_code ec)
{
  if (!ec)
  {
    std::vector<char> buf(socket.available());
    socket.read_some(buffer(buf));
  }
}

There's also an excellent example included in the documentation.

Disini answered 13/1, 2011 at 23:32 Comment(5)
This was just what I was looking for. It integrated perfectly. Thanks a lot!Pivotal
Hi Sam, can you explain more about the excellent example included in the documentation you mentioned? I want to use the third_party_lib.session to Read/AsyncRead/Write/AsyncWrite something, how can I do it? I'm still not so clear. The example simulating the third_party_lib confused me actually. Thanks.Thacher
@Peter please ask a new question, include what part of the example you find confusing.Disini
@Sam, I have posted another question: #7235555 Please check it out. Thanks.Thacher
I was using async_read_some with libcurl in this manner, but had a problem where it returns without an error when the server closes the socket, and the boost/asio socket also still thought it was open. I used async_receive instead with the message_peek flag and a buffer of size 1 to detect the EOF.Instable

© 2022 - 2024 — McMap. All rights reserved.