How to store and call `boost::asio` Handler correctly?

Viewed 441

Currently, I am implementing a little complicated networking protocol using boost::asio framework. The protocol supports request multiplexing, so I implement a statefull composed operation to provide high-level access. Say, something like the following:

connection::async_request(Request req, Handler&& h);

This requests are permitted to be run concurrently, so I have to collect all pending completion handlers in a container internally to be able to call appropriate handler for every request. Unfortunately, boost::asio Handler is more than just a Callable, so a container of std::function<> cannot be used here. AFAIU, each Handler consists of three parts:

  • callable function
  • optional associated executor Executor
  • optional associated allocator Allocator

additionally boost::asio::executor_work_guard<Executor> object must be stored along with the Handler to prevent the associated Executor object from terminating due to lack of work. I didn't find anything ready in Boost.Asio library. Am I right?

I've found that there is boost::beast::saved_handler in Boost.Beast library, which, I suppose, is designed for the same kind of problem. However, I see that saved_handler::invoke() is designed as ordinarily function call. As far as I understand, this may lead to an issue, when the handler associated executor differs from the executor of the internal handler of the composed operation. So this part is still unclear to me.

The composed operation Asio examples (cpp14/operations) inherit the handler associated allocator and executor for internal async operations. Contrarily, my connection::async_request uses async_write/async_read with strand object internally, it is required to provide correct implementation. At the same time, the Handler passed to the function may use another strand, which must not be lost during the composed operation. Do I understand correctly, that running boost::asio::dispatch() is more correct way to call the stored handler? What should be recommended way to provide this kind of functionality?

Upd: @sehe pointed me the Reddit thread: https://www.reddit.com/r/cpp/comments/6aygos/boostasio_asynchronous_composed_operation_tutorial/

I would rephrase my question in the terms of the cited thread as the following. Asio makes the following assumption on how composed operation works internally:

When asynchronous operations are composed from other asynchronous operations, all intermediate handlers should be invoked using the same method as the final handler.

And this is probably why the final handler is called just as handler(...) in all examples found all around the Internet. (Including, probably, beast::saved_handler implementation).

Unfortunately, this is not the case for me. connection::async_request starts new composed asynchronous operation or appends more work to the already ongoing operation. In the latter case, new final handler provided by the user cannot affect the way (Executor and Allocator) the intermediate handlers are invoked.

0 Answers
Related