Why does Boost.Asio not support an event-based interface?

Boost.Asio is a C++ library for network and low-level I/O programming. As such, OS-level synchronization objects, such as condition variables, are outside of the scope of the library, and a much better fit for Boost.Thread. The Boost.Asio author often presents the boost::asio::io_service as the bridge or link between the application and the OS. While this may be an over simplification, it is within the context of the OS’s I/O services.

Asynchronous programming already has an innate complexity due to the separation in time and space between operation initiation and completion. Strands provided a fairly clean solution to provide strict sequential invocation of handlers, without the need of explicit locking. As the locking is both implicit and thread-safe, application code can use strands without the fear of deadlocking. On the other hand, having boost::asio::io_service::condition_service perform implicit synchronization on an externally provided object may turn a complex library into a complicated one. It may not be clear to the application developer what mutex on which the handler was synchronized, and the state of the mutex. Additionally, it introduces the ability for applications to more easily deadlock the event processing loop due to the implicit locking.


If event-based handler invocation needs to occur, then one fairly simple alternative is use the same approach Boost.Asio’s timeout server example uses: boost::asio::deadline_timer. A deadline_timer‘s expiry time can be set to posix_time::pos_infin, causing an async_wait‘s handler to only be invoked once the timer has been canceled:

  • cancel() could function as notify_all(), where all outstanding handlers are queued for invocation.
  • cancel_one() could function as notify_one(), where a max of one outstanding handler is queued for invocation.

A simple example, ignoring error code handling, is as follows:

#include <iostream>

#include <boost/asio.hpp>
#include <boost/thread.hpp>

class event
{
public:
  explicit
  event(boost::asio::io_service& io_service) 
    : timer_(io_service)
  {
    // Setting expiration to infinity will cause handlers to
    // wait on the timer until cancelled.
    timer_.expires_at(boost::posix_time::pos_infin);
  }

  template <typename WaitHandler>
  void async_wait(WaitHandler handler)
  {
    // bind is used to adapt the user provided handler to the deadline 
    // timer's wait handler type requirement.
    timer_.async_wait(boost::bind(handler));
  }

  void notify_one() { timer_.cancel_one(); }
  void notify_all() { timer_.cancel();     }

private:
  boost::asio::deadline_timer timer_;
};

void on_event() { std::cout << "on_event" << std::endl; }

int main()
{
  boost::asio::io_service io_service;
  event event(io_service);

  // Add work to service.
  event.async_wait(&on_event);

  // Run io_service.
  boost::thread thread(boost::bind(&boost::asio::io_service::run,
                       &io_service));

  // Trigger event, causing the on_event handler to run.
  event.notify_one();

  thread.join();  
}

Leave a Comment