This code calls the posted handle
boost::asio::io_context ioc;
boost::asio::post(ioc, []{ std::cout << "lol" << std::endl; });
ioc.poll();
whereas this doesn't:
boost::asio::io_context ioc;
ioc.poll(); // empty, set internal state to stopped_
boost::asio::post(ioc, []{ std::cout << "lol" << std::endl; });
ioc.poll(); // doesn't work as stopped() now returns true
Live example
Is it by design? If is, why? Can I configure io_context somehow to change this behavior?
io_service/io_context are designed to stop when they run out of work¹.
The docs for io_service and io_context contain:
Stopping the io_context from running out of work
Some applications may need to prevent an io_context object's run() call from returning when there is no more work to do. For example, the io_context may be being run in a background thread that is launched prior to the application's asynchronous operations. The run() call may be kept running by creating an object of type boost::asio::executor_work_guard<io_context::executor_type>:
boost::asio::io_context io_context; boost::asio::executor_work_guard<boost::asio::io_context::executor_type> = boost::asio::make_work_guard(io_context); ...To effect a shutdown, the application will then need to call the io_context object's stop() member function. This will cause the io_context run() call to return as soon as possible, abandoning unfinished operations and without permitting ready handlers to be dispatched.
Alternatively, if the application requires that all operations and handlers be allowed to finish normally, the work object may be explicitly reset.
boost::asio::io_context io_context; boost::asio::executor_work_guard<boost::asio::io_context::executor_type> = boost::asio::make_work_guard(io_context); ... work.reset(); // Allow run() to exit.
Note that the "old-fashioned" Asio interface used a less generalized io_service::work object:
io_service ios;
io_service::work work(ios); // old interface!
This would then require you to do extra work for to be able to reset it:
asio::io_service ios;
std::optional<asio::io_service::work> work(ios);
// ...
work.reset();
Finally when the context did run out of work, you will have to restart() it before re-using it:

I think the rationale for the design comes from the library not having any
opinions about how the service is run in terms of scheduling and threading, in
combination with the guarantee that io_context/io_service must be
thread-safe². See docs for
background.
¹ Sidenote: Likewise, thread_pool (which is-a execution_context just like io_context is) is not designed for re-use (see e.g. Boost asio thread_pool join does not wait for tasks to be finished)
² Except object lifetime (construction/destruction), of course
This documented (not in best place, it should be mentioned in poll documentation):
io_context::restart - develop
io_context::restartRestart the
io_contextin preparation for a subsequentrun()invocation.void restart();
This function must be called prior to any second or later set of invocations of the
run(),run_one(),poll()orpoll_one()functions when a previous invocation of these functions returned due to theio_contextbeing stopped or running out of work. After a call torestart(), theio_contextobject'sstopped()function will returnfalse.This function must not be called while there are any unfinished calls to the
run(),run_one(),poll()orpoll_one()functions.
So basically you need to add restart to make it work.
https://wandbox.org/permlink/aXzz5GCAIMIvStnl
Here is an extra clue why.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With