On Mon, Mar 26, 2018 at 3:46 PM, Gavin Lambert via Boost-users
Out of curiosity, do you have any link to discussion or documents that explain the reasoning and/or motivation for this?
The discussion took place privately via email unfortunately.
I would have assumed that composed operations should not need the equivalent of io_service::work because work should be considered in-progress from the time that it is originally posted until such time as the completion handler *finishes* executing (thus if the completion handler posts additional work, there is no "gap").
The composed operation might not call any initiating function and instead choose to complete immediately, via boost::asio::post. In Beast for example, this can happen on an HTTP read when the caller's buffer already has a complete message (no I/O is performed in this case).
That's certainly how Asio has historically worked. You only need an explicit work guard if there are times when you have no outstanding work (eg. you let a completion handler exit without posting something new, or you run your io_service/io_context before you post your first operation).
In Net.TS (and by extension, Net.TS-flavored Boost.Asio) work guards are associated with executors not the io_context. There are two executors at play here: 1. The executor of the io_context associated with the I/O object 2. The handler's associated executor (obtained by get_associated_executor) The 2-argument form of boost::asio::post creates the executor_work_guard for the handler's associated executor, but the composed operation is responsible for either directly or indirectly maintaining the lifetime of an executor_work_guard for the io_context's executor. This is described in [async.reqmts.async.work]: 13.2.7.10 Outstanding work Until the asynchronous operation has completed, the asynchronous operation shall maintain: (1.1) — an object work1 of type executor_work_guard, initialized as work1(ex1), and where work1.owns_work() == true; and (1.2) — an object work2 of type executor_work_guard, initialized as work2(ex2), and where work2.owns_work() == true. See http://cplusplus.github.io/networking-ts/draft.pdf
Or is that last comment indicating that you're using two separate io_services, so you do have times where operations aren't pending on one or the other?
No, there is only one io_context, which is the io_context associated with the stream. There are two executors. Thanks