Everything that I read in the Boost ASIO docs, and here, in StackOverflow, I can stop the operation async_accept
by calling close
the acceptor on the socket. However, I get an intermittent error not_socket
in the handler async_accept
when I try to do this. Am I doing something wrong or does Boost ASIO not support this?
(Related questions: here and here .)
(Note: I am running on Windows 7 and using the Visual Studio 2015 compiler.)
The main problem I am facing is the race condition between the operation async_accept
accepting the incoming connection and my call close
. This happens even when using a string, explicit or implicit.
Please note that my call async_accept
is done strictly before my call close
. I will conclude that the race condition is between my call close
and the code under the hood in Boost ASIO, which accepts an incoming connection.
I have included code demonstrating the problem. The program repeatedly creates an acceptor, connects to it and immediately closes the acceptor. He expects the operation to async_accept
be successfully completed or canceled. Any other error leads to the interruption of the program, which I see intermittently.
For synchronization, the program uses an explicit chain. Nevertheless, the call is not close
synchronized with the effect of the operation async_accept
, so sometimes the acceptor closes before it accepts the incoming connection, sometimes it closes later, and sometimes it is not a problem.
Here is the code:
int main()
{
boost::asio::io_service ios;
auto work = std::make_unique<boost::asio::io_service::work>(ios);
const auto ios_runner = [&ios]()
{
boost::system::error_code ec;
ios.run(ec);
if (ec)
{
std::cerr << "io_service runner failed: " << ec.message() << '\n';
abort();
}
};
auto thread = std::thread{ios_runner};
const auto make_acceptor = [&ios]()
{
boost::asio::ip::tcp::resolver resolver{ios};
boost::asio::ip::tcp::resolver::query query{
"localhost",
"",
boost::asio::ip::resolver_query_base::passive |
boost::asio::ip::resolver_query_base::address_configured};
const auto itr = std::find_if(
resolver.resolve(query),
boost::asio::ip::tcp::resolver::iterator{},
[](const boost::asio::ip::tcp::endpoint& ep) { return true; });
assert(itr != boost::asio::ip::tcp::resolver::iterator{});
return boost::asio::ip::tcp::acceptor{ios, *itr};
};
for (auto i = 0; i < 1000; ++i)
{
auto acceptor = make_acceptor();
const auto saddr = acceptor.local_endpoint();
boost::asio::io_service::strand strand{ios};
boost::asio::ip::tcp::socket server_conn{ios};
std::promise<void> accept_promise;
strand.post(
[&]()
{
acceptor.async_accept(
server_conn,
strand.wrap(
[&](const boost::system::error_code& ec)
{
accept_promise.set_value();
if (ec.category() == boost::asio::error::get_system_category()
&& ec.value() == boost::asio::error::operation_aborted)
return;
if (ec)
{
std::cerr << "async_accept failed (" << i << "): " << ec.message() << '\n';
abort();
}
}));
});
std::promise<void> connect_promise;
strand.post(
[&]()
{
boost::asio::ip::tcp::socket client_conn{ios};
{
boost::system::error_code ec;
client_conn.connect(saddr, ec);
if (ec)
{
std::cerr << "connect failed: " << ec.message() << '\n';
abort();
}
connect_promise.set_value();
}
});
connect_promise.get_future().get();
std::promise<void> stop_promise;
strand.post([&acceptor, &stop_promise]()
{
acceptor.close();
stop_promise.set_value();
});
stop_promise.get_future().get();
accept_promise.get_future().get();
}
work.reset();
thread.join();
}
:
async_accept failed (5): An operation was attempted on something that is not a socket
, .
# 1: Tanner Sansbury std::promise
async_accept
. , .
# 2: not_socket
setsockopt
, call_setsockopt
, socket_ops::setsockopt
boost\asio\detail\impl\socket_ops.ipp
(Boost 1.59). :
socket_ops::setsockopt(new_socket, state,
SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT,
&update_ctx_param, sizeof(SOCKET), ec);
Microsoft setsockopt
SO_UPDATE_ACCEPT_CONTEXT
:
.
, , , - , . , Windows close
, async_accept
.
, - , , .