Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

How can I let the asio eventloop call a lambda from another thread?

Assume a thread is running the asio event loop, how can I defer a lambda to be called in this thread?

#include <thread>
#include <iostream>
#include <asio.hpp>


int main()
{
    asio::io_context io_context;
    // hook something into io_context
    std::jthread t([&]{io_context.run();});
    // now how to let eventoop thread call lambda
    io_context.async_call([]{std::cout<<"Hello World!";}); // this method does not exist
}

I am looking for a functionality in the spirit of http://docs.libuv.org/en/v1.x/async.html#c.uv_async_t, in libuv this is possible with uv_async_send these guarantees:

  • Async handles allow the user to “wakeup” the event loop and get a
    callback called from another thread.

    MEDevel.com: Open-source for Healthcare and Education

    Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

    Visit Medevel

  • It’s safe to call this function from any thread. The callback will be
    called on the loop thread.

  • uv_async_send() is async-signal-safe. It’s safe to call this function
    from a signal handler.

What would be the equivalent in asio?

>Solution :

In Asio you post[1] work to an executor.

The executor is associated with an execution context. In your example io_context is such an execution context.

So, you would say:

asio::post(io_context.get_executor(), []{std::cout<<"Hello World!";});

ADL makes it so that you can probably use unqualified:

post(io_context.get_executor(), []{std::cout<<"Hello World!";});

Next up, as a convenience Asio supports posting “to” an execution context, in which case its default executor will be used:

post(io_context, []{std::cout<<"Hello World!";});

Differences

If you use asio::dispatch the posted handler may be invoked on the calling thread iff the executor matches that thread. E.g. in the given example asio::dispatch would behave just like asio::post unless you used it from inside a handler already running on the “service thread” (a thread running the io context).

Associated Executors

There are multiple types of executors. They might add custom behavior. For example strand<> executors make sure only a single handler runs concurrently, enabling you to get serialization of access similarly to mutual exclusion in synchronous code.

It stands to reason the choice of executor will be important for this mechanism to be correct. The library has an “associated executor” facility to allow it to honor executor requirements correctly in generic code.

For example, composed operations (like asio::async_read_until) will correctly use the associated executor even for any intermediate handlers that it posts internally.

To associate an executor, use bind_executor, e.g.:

Live On Colriu

#include <iostream>
#include <boost/asio.hpp>

int main() {
    boost::asio::thread_pool tp; // multi threading execution context
    auto strand = make_strand(tp.get_executor()); // using a strand to serialize handlers

    auto handler = [&strand] { std::cout << "On the strand: " << strand.running_in_this_thread() << std::endl; };
    auto bound = bind_executor(strand, handler);

    // Now even posting _without_ an executor will invoke the bound handler on the intended executor:
    post(bound);

    tp.join();
}

Which prints

On the strand: 1

See for more related information:

More related to the core execution guarantees of the library:

[1] asio::post, asio::defer, asio::dispatch

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading