Manual event polling

113

Usually a client application maintains polling of system events. Some platforms let you maintain event polling completely. On Windows it's something like PeekMessageDispatchMessage 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.

Lesson 19
Share this page:

Learning plan

What's the difference between a client and a server, and what do they have in common
Resolving hostnames into IP addresses before connect
Writing a very simple client application in C++ with Boost.Asio
18. Manual event polling
How to deal with completion handlers manually to combine Boost.Asio with other APIs
Let's take a break and briefly look across everything we've learned so far
A closer look on how to pass data views into Boost.Asio functions
How to operate on the underlying buffer sequence data with Boost.Asio free functions