[Boost-users] [asio] Using asio for asynchronous event processing

217 views
Skip to first unread message

Edward Diener

unread,
May 27, 2012, 8:40:16 PM5/27/12
to boost...@lists.boost.org
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. But rather than complain about
the doc I will ask a specific problem related to what I am trying to do
and hopefully someone can help me or point me to something in the doc by
which I can understand how to use asio for my problem.

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 ?

The doc suggests that asio does not only deal in i/o events, such as
networking and sockets, but I could not understand whether it deals in
generalized asynchronous operations or not. The proactor pattern is
understandable but there appears to be nothing in the doc which connects
the elements of the proactor pattern to classes in asio. The basic
tutorial as well as the sockets tutorial does not enlighten me at all.
Hopefully someone here can help.

_______________________________________________
Boost-users mailing list
Boost...@lists.boost.org
http://lists.boost.org/mailman/listinfo.cgi/boost-users

Anthony Foiani

unread,
May 28, 2012, 1:32:34 AM5/28/12
to boost...@lists.boost.org
Edward Diener <eldi...@tropicsoft.com> 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.

Edward Diener

unread,
May 28, 2012, 9:42:29 AM5/28/12
to boost...@lists.boost.org
On 5/28/2012 1:32 AM, Anthony Foiani wrote:
> Edward Diener<eldi...@tropicsoft.com> 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".

More like "the docs don't mesh with my way of understanding".

> 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.

I agree with you that the doc is comprehensive, but I do not think it is
well explained.

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

I am pretty intolerant of library doc, no matter how comprehensive, that
takes the easy path of explaining how to use a library almost
exclusively through the "Tutorial" approach. With the latter, as soon
as anything falls outside the scope of the tutorial(s), the reader is
often lost.
I will look up this io_service::work to see why it is necessary.

>
> 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 );

Unfortunately the software uses Windows threads via VC++ rather than
boost::thread, but I gather that each thread must call
boost::asio::io_service::run().

>
> 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.

Understood.

>
> 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;
>
> // ...
> }
> }

Understood.

>
> 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.

Wish this explanation was in the asio doc. Thanks for clearing this up.

>
> 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.

Yes, somebody else suggested this also, outside of any reference to
asio. An interesting idea I will pursue.

> 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. :)

Thanks very much for the explanation about using asio for generalized
asynchronous operation. As mentioned above, it is not that I think the
doc for asio is not comprehensive but I do think that the author could
have explained much better what asio is about and how it works by
connecting the generalized concepts to the actual classes which
implement them within the library. Failure to do this led me to be
discouraged that I could understand how to use the library. Your brief
explanations helped greatly.
Reply all
Reply to author
Forward
0 new messages