Michał Jaroń
Michał Jaroń

Reputation: 624

Implementing custom boost asio async operation

I'm trying to implement my own boost::asio operation, executed asynchronously during io_context.run().

Also I need to implement a cancellable operation which waits for some condition / predicate is met (or boost::signals2::signal is called) - and next, calls any completion handler in io_context thread? Is such functionality already available in Boost?

My code is inspired by Boost documentation:

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

/// From boost documentation:
/// — If the initiating function is not a member function,
/// the associated executor is that returned by the get_executor member function
/// of the first argument to the initiating function.
struct executor_owner
{
    using executor_type = boost::asio::io_context;
    executor_type* m_executor = nullptr;

    explicit executor_owner(executor_type& ex)
        : m_executor(&ex)
    {
    }

    executor_type& get_executor() BOOST_ASIO_NOEXCEPT
    {
        std::cout << "get_executor() called." << std::endl;
        return *m_executor;
    }
};

/// My initiating function
template<class CompletionToken>
auto async_xyz(executor_owner executorOwner, CompletionToken&& token)
{
    using completion_handler_t = typename boost::asio::async_completion<CompletionToken, void(executor_owner&)>::completion_handler_type;
    return boost::asio::async_initiate<CompletionToken, void(executor_owner&)>(
        [](completion_handler_t completion_handler, executor_owner& owner) {
            // initiate the operation and cause completion_handler to be invoked
            // with the result
            std::cout << "I'm performing the async func operation here!" << std::endl;
            completion_handler();
        },
        token, executorOwner);
}

int main()
{
    boost::asio::io_context io_context;
    executor_owner executorOwner(io_context);

    async_xyz(executorOwner, []()
    {
        std::cout << "completion handler here!" << std::endl;
    });

    // I expect that completion handler will be called asynchronously, during io_context::run().
    // Unfortunately, completion handler has already been called.

    // std::cout << "io_context begin." << std::endl;
    // io_context.run(); // It doesn't matter. completion handler
    // std::cout << "io_context done." << std::endl;

    return 0;
}


// Output:
// I'm performing the async func operation here!
// completion handler here!

My questions:

  1. How to implement custom async operation compliant with boost::asio design?
  2. Why my executor wasn't obtained using get_executor() method as described in boost documentation?
  3. Does boost::asio::async_initiate internally posts operation to io_context, or have I call io_context::post(...) manually in my custom operation implementation (to trigger the completion handler)?
  4. How to implement easy wait operation which waits for a met predicate or a signal?
  5. How to add timeout / cancellation functionality to such operation?

I'm using boost 1.82 and C++14 (but C++11 solution is preferred).

All hints welcome, regards. And in case of down vote, please explain what's wrong.

Upvotes: 0

Views: 1042

Answers (1)

sehe
sehe

Reputation: 393769

A few notes

/// From boost documentation:
/// — If the initiating function is not a member function,
/// the associated executor is that returned by the get_executor member function
/// of the first argument to the initiating function.

The first argument to the initiating function is completion_handler_t. If you need to bind an executor to it, simply use bind_executor. (The other common method is implementing a hand-coded initiation function object that has the associated executor).

    // initiate the operation and cause completion_handler to be invoked
    // with the result

You should dispatch/post it to "cause [it] to be invoked":

asio::dispatch(completion_handler);

Thus, everything simplifies to:

Live On Coliru

#include <boost/asio.hpp>
#include <iostream>
namespace asio = boost::asio;

/// My initiating function
template <typename CompletionToken> auto async_xyz(CompletionToken&& token) {
    return asio::async_initiate<CompletionToken, void()>(
        [](auto completion_handler) {
            // initiate the operation and cause completion_handler to be invoked
            // with the result
            std::cout << "I'm performing the async func initiation here!" << std::endl;
            asio::dispatch(completion_handler);
        },
        token);
}

int main() {
    asio::io_context io_context;

    auto f = [] { std::cout << "completion handler here!" << std::endl; };
    async_xyz(bind_executor(io_context.get_executor(), f));

    std::cout << "io_context begin." << std::endl;
    io_context.run();
    std::cout << "io_context done." << std::endl;
}

Printing the expected

I'm performing the async func initiation here!
io_context begin.
completion handler here!
io_context done.

Upvotes: 1

Related Questions