Boost.ASIO calling handler guarantees

Hello, Dear All, I'm playing with Boost.ASIO framework and failed to find a complete answer on the following question. Under which circumstances is it guaranteed that asynchronous operation completion handler is called? Plainly speaking, is this example code correct? void my_handler(const error_code &ec) { if(!ec) { /* process successful operation */ } else { /* process failed operation */ } try { async_write(port, my_handler2) } catch(...) { // processing failure to execute asynchronous operation } } Obviously here I rely heavily upon the above mentioned guarantee. Looking at least at async_write() implementation, it cannot be relied upon: - async_write executes a chain of boost::asio::detail::write_op() operations: - if asio_handler_allocate() may throw then - write_op::stream_.async_write_some() may throw then - write_op.handler_() might never be called So, either we should never rely on this assumption, or there's a bug in async_write(). Not relying on this assumption what is the reliable way of building applications with Boost.Asio? Sincerely yours, Fedor Trushkin

On 22 Aug 2014 at 16:20, wrote:
I'm playing with Boost.ASIO framework and failed to find a complete answer on the following question. [snip] Looking at least at async_write() implementation, it cannot be relied upon: - async_write executes a chain of boost::asio::detail::write_op() operations: - if asio_handler_allocate() may throw then - write_op::stream_.async_write_some() may throw then - write_op.handler_() might never be called
So, either we should never rely on this assumption, or there's a bug in async_write(). Not relying on this assumption what is the reliable way of building applications with Boost.Asio?
I can't speak for ASIO, but AFIO explicitly makes no guarantees if a bad_alloc exception is ever thrown. Otherwise it guarantees exception safety. I would be highly surprised if ASIO can do any better than AFIO here. Handling bad_alloc is extremely tough when your exception handling paths have no choice but to allocate memory, which for something involving async i/o they must. The traditional solution is to feed the code an allocator which will never, ever run out of memory. That may be useful for ASIO too. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/

On Fri, Aug 22, 2014 at 5:30 AM, Niall Douglas wrote:
On 22 Aug 2014 at 16:20, wrote:
Under which circumstances is it guaranteed that asynchronous operation completion handler is called? [snip] Not relying on this assumption what is the reliable way of building applications with Boost.Asio?
I asked something similar sometime ago, so I'm glad this subject is raised again. http://article.gmane.org/gmane.comp.lib.boost.devel/243496
I can't speak for ASIO, but AFIO explicitly makes no guarantees if a bad_alloc exception is ever thrown. Otherwise it guarantees exception safety.
I would be highly surprised if ASIO can do any better than AFIO here. Handling bad_alloc is extremely tough when your exception handling paths have no choice but to allocate memory, which for something involving async i/o they must.
After more recent experimentation in a project consisting of a stack of increasingly complex functionality following the Proactor design (the Boost.Asio flavor of it), it has become apparent that one cannot rely on the completion handler of an operation to be called. Especially if using an allocator that does throw bad_alloc (and does it often under testing conditions). This is because, once the completion handler becomes less trivial, even its copy constructor may throw right before being executed by the io_service. The only solution I found so far for coping with the possibility of missing handler calls is for each each of them to carry a RAII / guard object that, if destructed before being "disabled", triggers the closing of the context which waits for the completion handler. The premise of this approach is that an io_service is shared by multiple independent contexts each doing asynchronous operations internally, so outside io_service::run() there is little or no context on how to handle an exception coming out of it. The two main assumptions for such a close-guard solution to work are: - An exception bubbling through io_service::run will not cause handlers to leak - they are either destroyed or called successfully - close() functions do not throw as they will be potentially called from a destructor during exception stack unwinding. This is the other aspect I raised in my email from a year ago; how come close() functions fail in Boost.Asio and what is one supposed to do with that failure at least in the context of commit/rollback? Best regards, Sorin Fetche

On 22 Aug 2014 at 12:00, Sorin Fetche wrote:
This is the other aspect I raised in my email from a year ago; how come close() functions fail in Boost.Asio and what is one supposed to do with that failure at least in the context of commit/rollback?
I appreciate how annoying it is when unit tests fail due to no cause. But equally, most big iron users of ASIO never need to worry about running out of memory. And most embedded users are the same. So that leaves only a segment in between, and as I mentioned, a custom allocator fixes that too. I'm sure Chris is open to fixing blatent exception safety failures. But I am also sure that where user code could be designed better, he'll take the view that potential performance is more important than holding the hand of user code's exception safety. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
participants (3)
-
Niall Douglas
-
Sorin Fetche
-
Фёдор Трушкин