Manual event polling
Usually a client application maintains polling of system events. Some platforms let you maintain event polling completely. On Windows it's something like PeekMessage
— DispatchMessage
loop. The others may restrict this and provide you something like update handler, like it does UIKit framework on iOS. In both cases you may want to fuse pollings of Boost.Asio and OS events. Of course you can run io_context::run
in additional thread, however it's not a good idea in general because you'll need to communicate between those pollings somehow, and that may lead to a need for a bunch of tricky, inconvenient or even unsafe code. You should do so if you really have to. A better way in general is to poll both types of events in the same thread.
An event polling loop of a simple Windows application with a window and OpenGL graphics could look like that:
application app;
for(;;)
{
MSG message;
while(PeekMessage(&message, window, 0, 0, PM_REMOVE))
{
TranslateMessage(&message);
DispatchMessage(&message);
}
app.draw();
SwapBuffers(device_context);
}
Okay. Now we need to add Boost.Asio event polling into this loop. If we put io_context::run
into this loop then it will block and no windows messages will be processed. That's not what we want. To invoke Boost.Asio completion handlers manually we will use io_context::poll
function instead of io_context::run
. It invokes all handlers of the tasks completed so far and returns to the caller thread:
io::io_context io_context;
auto work_guard = io::make_work_guard(io_context);
application app(io_context);
for(;;)
{
MSG message;
while(PeekMessage(&message, window, 0, 0, PM_REMOVE))
{
TranslateMessage(&message);
DispatchMessage(&message);
}
// Asynchronous tasks will perform somewhere on the OS side
io_context.poll(); // Completion handlers will be invoked here
app.draw();
SwapBuffers(device_context);
}
Note that in this case work_guard
is needed as well so io_context
won't stop processing asynchronous tasks if there was no job to do at some point of time.
Now both windows messages and Boost.Asio events are processed in the same thread, and it's safe to exchange data between them without any synchronization.
In real life client you may want to perform some tasks in a parallel thread (or even several parallel threads) depending on the features and requirements of your application and that's fine. In such case you may use several io_context
instances, one of which should still do io_context::poll
in the main system polling loop and exchange messages with other contexts via boost::asio::post
function.