Single-threaded application.
It happens not every time, only after 1.5 hours of high load.
- tcp::socket::async_connect
- tcp::socket::close (by deadline_timer)
- async_connect_handler gives success error_code (one of a million times), but socket is closed by(2). 99.999% of time it gives errno=125 (ECANCELED).
Is it possible that socket implementation or boost asio somehow do this:
- async_connect
- async success posted to io_service
- close by timer
- async success handled by me, not affected by close
Right now solved by saving state in my variables, ignoring accept success.
Linux 2.6 (fedora).
Boost 1.46.0
PS: ofcouse possible bug on my part… But runs smoothly for days if not this.
As Igor mentions in the comments, the completion handler is already queued.
This scenario is the result of a separation in time between when an operation executes and when a handler is invoked. The documentation for
io_service::run(),io_service::run_one(),io_service::poll(), andio_service::poll_one()is specific to mention handlers, and not operations. In the scenario, thesocket::async_connect()operation anddeadline_timer::async_wait()operation complete in the same event loop iteration. This results in both handlers being added to theio_servicefor deferred invocation, in an unspecified order.Consider the following snippet that accentuates the scenario:
When
io_service_.run_one()is invoked, bothsocket::async_connect()anddeadline_timer::async_wait()operations may have completed, causinghandle_waitandhandle_connectto be ready for invocation from within theio_servicein an unspecified order. To properly handle this unspecified order, additional logic need to occur from withinhandle_wait()andhandle_connect()to query the current state, and determine if the other handler has been invoked, rather than depending solely on the status (error_code) of the operation.The easiest way to determine if the other handler has invoked is:
handle_connect(), check if the socket is still open viais_open(). If the socket is still open, thenhandle_timer()has not been invoked. A clean way to indicate tohandle_timer()thathandle_connect()has ran is to update the expiry time.handle_timer(), check if the expiry time has passed. If this is true, thenhandle_connect()has not ran, so close the socket.The resulting handlers could look like the following:
Boost.Asio provides some examples for handling timeouts.