6

I had intended to have a thread in my program which would wait on two file descriptors, one for a socket and a second one for a FD describing the file system (specifically waiting to see if a new file is added to a directory). Since I expect to rarely see either the new file added or new TCP messages coming in I wanted to have one thread waiting for either input and handle whichever input is detected when it occures rather then bothering with seperate threads.

I then (finally!) got permission from the 'boss' to use boost. So now I want to replace the basic sockets with boost:asio. Only I'm running into a small problem. It seems like asio implimented it's own version of select rather then providing a FD I could use with select directly. This leaves me uncertain how I can block on both conditions, new file and TCP input, at the same time when one only works with select and the other doesn't seem to support the use of select. Is there an easy work around to this I'm missing?

Sam Miller
  • 23,808
  • 4
  • 67
  • 87
dsollen
  • 6,046
  • 6
  • 43
  • 84

1 Answers1

8

ASIO is best used asynchronously (that's what it stands for): you can set up handlers for both TCP reads and the file descriptor activity, and the handlers would be called for you.

Here's a demo example to get you started (written for Linux with inotify support):

#include <iostream>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <sys/inotify.h>

namespace asio = boost::asio;
void start_notify_handler();
void start_accept_handler();

// this stuff goes into your class, only global for the simplistic demo
asio::streambuf buf(1024);
asio::io_service io_svc;
asio::posix::stream_descriptor stream_desc(io_svc);
asio::ip::tcp::socket sock(io_svc);
asio::ip::tcp::endpoint end(asio::ip::tcp::v4(), 1234);
asio::ip::tcp::acceptor acceptor(io_svc, end);

// this gets called on file system activity
void notify_handler(const boost::system::error_code&,
                    std::size_t transferred)
{
    size_t processed = 0;
    while(transferred - processed >= sizeof(inotify_event))
    {
        const char* cdata = processed
                            + asio::buffer_cast<const char*>(buf.data());
        const inotify_event* ievent =
                                 reinterpret_cast<const inotify_event*>(cdata);
        processed += sizeof(inotify_event) + ievent->len;
        if(ievent->len > 0 && ievent->mask & IN_OPEN)
            std::cout << "Someone opened " << ievent->name << '\n';
    }
    start_notify_handler();
}

// this gets called when nsomeone connects to you on TCP port 1234
void accept_handler(const boost::system::error_code&)
{
    std::cout << "Someone connected from " 
              << sock.remote_endpoint().address() << '\n';
    sock.close(); // dropping connection: this is just a demo
    start_accept_handler();
}

void start_notify_handler()
{
    stream_desc.async_read_some( buf.prepare(buf.max_size()),
        boost::bind(&notify_handler, asio::placeholders::error,
                    asio::placeholders::bytes_transferred));
}

void start_accept_handler()
{
    acceptor.async_accept(sock,
        boost::bind(&accept_handler, asio::placeholders::error));
}

int main()
{
    int raw_fd = inotify_init(); // error handling ignored
    stream_desc.assign(raw_fd);
    inotify_add_watch(raw_fd, ".", IN_OPEN);
    start_notify_handler();
    start_accept_handler();
    io_svc.run();
}
Cubbi
  • 46,567
  • 13
  • 103
  • 169
  • Cubbi: Great example. Thx. Stupid question: since we are registering for two `async_read`'s, does it mean one async event which is being processed could potentially be interrupted by the arrival of another async event and is this why [`boost::asio::strand`](http://www.boost.org/doc/libs/1_53_0/boost/asio/strand.hpp) exists? – decimus phostle Feb 15 '13 at 04:27
  • @decimusphostle It wouldn't be interrupted. The new event would run on another thread or wait in asio's queue if all threads in the pool are busy. In this example, only one thread was added to asio's pool. – Cubbi Feb 15 '13 at 05:12
  • Cubbi: I wrote [this](http://pastebin.com/v7HEabjv) test program based on your example here. However this program fails to get notified of create/delete/modify events to the file. Adding a watch on the directory doesn't seem to help either. – decimus phostle Mar 06 '13 at 00:39
  • To further debug this I stripped out the asio code and wrote a [simpler test program](http://pastebin.com/tK83Fx5X) which also [was not working right](http://stackoverflow.com/questions/15228337/inotify-not-firing-notifications-on-file-modification). Any thoughts/comments? TIA. – decimus phostle Mar 06 '13 at 00:40
  • @decimusphostle did you try my example as-is? It works for me (and is based on production code that has been heavily used for years). One thing that jumps out to me in your code is that you're watching a file, but ignoring events with `len` field of zero, which it is for files. I'll take a look at your post. – Cubbi Mar 06 '13 at 02:04