[asio] Using asio for asynchronous event processing
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.
Edward Diener
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.
On 5/28/2012 1:32 AM, Anthony Foiani wrote:
Edward Diener
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 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 );
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.
participants (2)
-
Anthony Foiani
-
Edward Diener