Boost logo

Boost Users :

Subject: Re: [Boost-users] [asio] Using asio for asynchronous event processing
From: Anthony Foiani (tkil_at_[hidden])
Date: 2012-05-28 01:32:34


Edward Diener <eldiener_at_[hidden]> writes:

> I do not understand the documentation for asio, or even if it
> pertains to the programming problem I am trying to solve. The doc,
> for whatever reason, is poor regarding practical use.

For whatever it's worth, I tend to try to view this as "the docs don't
mesh with my way of thinking". The reference is comprehensive, but
ASIO is so flexible that even the examples and tutorials can only
cover a small portion of the design space; if you don't "get it" from
that, then it hurts.

(Speaking as one who most certainly did *not* "get it" from the first
few readings of the docs...)

> I need to design generalized asynchronous events triggered by an
> event source and handled by an event handler. The event source would
> trigger an event declared as a callable object, as in
> boost::function, and an event handler would eventually handle the
> event asynchronously. The event source and the event handler would
> be in different threads of an application but not in different
> applications.
>
> The event source triggers the event but does not block in any way
> waiting for the event to be processed. The event source may
> subsequently trigger other asynchronous events to be eventually
> handled in the same way without blocking.
>
> The event handler is able to check for events periodically from
> within its own thread.
>
> Each event itself could be a totally different callable object, but
> the event source and the event handler both know the callable
> prototype for any given event. The event source and the event
> handler are completely disconnected in that neither knows about the
> other. For any given asynchronous event there may be any number of
> event handlers when an event is triggered.
>
> Can I use asio to implement such a solution ?

I believe so. Further, I believe that you can even strap it onto a
signals2 solution (as per your previous question, although maybe you
were just exploring options for doing a pure async solution).

Here's what should work. In your main program, create an io_service
to manage all the events, and add some fake work to it:

  // in main program
  boost::asio::io_service io;
  boost::scoped_ptr< boost::asio::io_service::work > work_ptr( io );

If you want threads that are dedicated to handling events, you can do
that by having the thread just run io_service.run:

  boost::thread handler1( boost::asio::io_service::run, &io );
  boost::thread handler2( boost::asio::io_service::run, &io );

Otherwise, you'll have to call io.poll_one() (or some similar variant)
to execute work as it becomes available during the allocated slots on
your handler threads.

In your event generator thread, you might do something like this:

  // event generator
  while ( event = wait_for_event() )
  {
      switch ( event.type )
      {
          case MOUSE_EVENT :
              io.post( boost::bind( handle_mouse_event, event ) );
              break;

          case WINDOW_EVENT :
              io.post( boost::bind( handle_window_event, event ) );
              break;

          // ...
      }
  }

You can view the io_service object ("io") as a "work queue", with
items peeled off the front and handed out to whatever thread calls
"run", "run_one", "poll", or "poll_one" on that same io object.

If you find signals2 to be a helpful organizational tool, you can
integrate it with ASIO by having signals2 make asynchronous calls to
the various slots hooked up to the triggered signal.

It's probably a bit hacky, but I wedged that async call into the
combiner object used by the signal. First, we define some types at
namespace or file scope (I ran into issues using block-scoped types):

  struct async_combiner_t
  {
      typedef void result_type;

      async_combiner_t( boost::asio::io_service * io_ptr )
        : io_ptr( io_ptr ) {}
      async_combiner_t( const async_combiner_t & ) = default;

      boost::asio::io_service * io_ptr;

      template < typename InputIter >
      void operator()( InputIter first, InputIter last )
      {
          for ( InputIter i = first; i != last; ++i )
              io_ptr->post( [=](){ *i; } );
      }
  };

  typedef boost::signals2::signal< void (), async_combiner_t > async_sig_t;

Now we create the block-scoped objects using those types, and start
sending events at them:

  boost::asio::io_service io;

  typedef boost::scoped_ptr< boost::asio::io_service::work > work_ptr_t;
  work_ptr_t work_ptr( new boost::asio::io_service::work( io ) );

  const int n_threads = argc > 1 ? boost::lexical_cast< int >( argv[1] ) : 5;
  boost::thread_group threads;
  for ( int i = 0; i < n_threads; ++i )
      threads.create_thread( [&](){ io.run(); } );

  async_combiner_t async_combiner( &io );
  async_sig_t async_sig( async_combiner );
  async_sig.connect( slot1 );
  async_sig.connect( slot2 );

  async_sig();

  work_ptr.reset();

  if ( n_threads )
      threads.join_all();
  else
      run_io( io );

Having said that, I am having issues getting that to work with
multiple threads; I'm going to post a question here in a bit.

The full program is available at:

   https://github.com/tkil/boost-async/blob/master/async-signals2.cpp

Hopefully it'll give you ideas. Hopefully they'll be good ideas. :)

Happy hacking,
t.


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net