3

While using boost::asio 1.66 I read in the documentation that boost::asio::io_context::post is deprecated for boost::asio::post, same for boost::asio::io_context::dispatch. Because before they where member functions of the io_context before, and of course the handler needs to be executed in the context of some io_context i.e. executor my question is:

How does boost::asio::io_context::post simplest overload know which io_context i.e. executor to use?

The documentation of template< typename CompletionToken> DEDUCED post(CompletionToken && token); states that

Obtains the handler's associated executor object ex by performing get_associated_executor(handler).

But the documentation of get_associated_executor does not make it clear to me either. My guess is due to the Template argument deduction it can grab it somehow in the currently executed handler, but I want to make sure and also, this wouldn't suffice if I call post outside of a boost::asio handler.

Superlokkus
  • 4,731
  • 1
  • 25
  • 57

1 Answers1

2

The core of the documentation is found with the associated_executor trait:

  • get()

    If T has a nested type executor_type, returns t.get_executor(). Otherwise returns ex.

  • executor_type

    If T has a nested type executor_type, T::executor_type. Otherwise Executor.

If your handler type¹ has a nested executor_type type, then it is assumed that calling token.get() will return the correct executor to use.

If you pass a vanilla calleable to post without specifying an executor/execution context you will get a default-constructed instance of an execution context: boost::asio::system_executor.

The purpose of this is for the implementation to DoTheRightThing with custom handler types. E.g. if you post something on a strand, the handler will be wrapped in a type specific to the strand implementation. The associated_executor trait and ditto get_executor() member function will then coordinate to direct to the executor for that strand.


¹ or any token, in case your call model is different, like a yield context

sehe
  • 374,641
  • 47
  • 450
  • 633
  • I guess "The system executor represents an execution context where functions are permitted to run on arbitrary threads." means, any thread waiting on any `io_context.run()/run_once()` etc? – Superlokkus Sep 23 '18 at 11:00
  • 1
    I can answer myself: schedule the function to run on an unspecified system thread pool". Guess I want to specify the executor, thanks for the pointer to look for. – Superlokkus Sep 23 '18 at 11:06
  • 1
    Yup. In general you want to specify the executor at the edges. Inside composed operations you want to let the system deduce the associated executors so that things like strands magically work – sehe Sep 23 '18 at 11:10
  • I don't like magic, especially when it comes to locking concurrent code. Guess it's a nice try by the boost::asio designers, but I fear they have overdone it. – Superlokkus Sep 23 '18 at 11:16
  • 1
    I hear you. However this is not a trivial problem to solve generically. You should realise that this is the new design that's also being proposed for the standard library. Regardless you don't have to know about the innards unless you intend to write library level code (so like composed operations, that you want to behave genetically and combine with any custom handler dispatch from another library). If not, it would suffice to just post to specific executors. – sehe Sep 23 '18 at 11:21
  • The funny thing is, for the most handlers I could just use it like this, because at the moment I only need strands to synchronise on my udp_sockets. But since I already assigned a nice bunch of threads on the io_context/executor of the udp_sockets, it seems wastefull to only use them for the udp_socket work ;-). So I just abuse the strand to get to the io_context, which post uses to get to the executor, to get the hungry threads some work. Guess it also works as a congestion control in this way, because the server won't respond if to much work is due. – Superlokkus Sep 23 '18 at 11:36