0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-06-02 10:08:56 +02:00

ircd::net: Improve wait related; fix issues with bad behavior.

This commit is contained in:
Jason Volk 2018-01-08 17:18:25 -08:00
parent b0579d0963
commit 89c481d1f8
3 changed files with 50 additions and 22 deletions

View file

@ -68,9 +68,9 @@ struct ircd::net::socket
bool handle_verify(bool, asio::ssl::verify_context &, const open_opts &) noexcept;
void handle_disconnect(std::shared_ptr<socket>, eptr_handler, const error_code &) noexcept;
void handle_handshake(std::weak_ptr<socket>, eptr_handler, const error_code &) noexcept;
void handle_connect(std::weak_ptr<socket>, const open_opts, eptr_handler, const error_code &) noexcept;
void handle_connect(std::weak_ptr<socket>, open_opts, eptr_handler, const error_code &) noexcept;
void handle_timeout(std::weak_ptr<socket>, ec_handler, const error_code &) noexcept;
void handle(std::weak_ptr<socket>, ec_handler, const error_code &) noexcept;
void handle_ready(std::weak_ptr<socket>, ready, ec_handler, error_code) noexcept;
public:
operator const ip::tcp::socket &() const { return sd; }

View file

@ -29,6 +29,8 @@ namespace ircd::net
using wait_callback_ec = std::function<void (const error_code &)>;
using wait_callback_eptr = std::function<void (std::exception_ptr)>;
string_view reflect(const ready &);
// Asynchronous callback when ready with error_code
void wait(socket &, const wait_opts &, wait_callback_ec);

View file

@ -318,6 +318,20 @@ ircd::net::wait(socket &socket,
socket.wait(wait_opts, std::move(callback));
}
ircd::string_view
ircd::net::reflect(const ready &type)
{
switch(type)
{
case ready::ANY: return "ANY"_sv;
case ready::READ: return "READ"_sv;
case ready::WRITE: return "WRITE"_sv;
case ready::ERROR: return "ERROR"_sv;
}
return "????"_sv;
}
///////////////////////////////////////////////////////////////////////////////
//
// net/close.h
@ -1067,7 +1081,7 @@ ircd::net::listener::acceptor::configure(const json::object &opts)
//| ssl.no_tlsv1_2
//| ssl.no_sslv2
//| ssl.no_sslv3
//| ssl.single_dh_use
//ssl.single_dh_use
);
*/
//TODO: XXX
@ -1366,25 +1380,21 @@ ircd::net::socket::wait(const wait_opts &opts,
{
auto handle
{
std::bind(&socket::handle, this, weak_from(*this), std::move(callback), ph::_1)
std::bind(&socket::handle_ready, this, weak_from(*this), opts.type, std::move(callback), ph::_1)
};
switch(opts.type)
{
case net::ready::ERROR:
case ready::ERROR:
sd.async_wait(wait_type::wait_error, std::move(handle));
break;
case net::ready::WRITE:
case ready::WRITE:
sd.async_wait(wait_type::wait_write, std::move(handle));
break;
// The new async_wait() on linux triggers a bug which is only
// reproducible when serving a large number of assets: a ready status
// for the socket is not indicated when it ought to be, at random.
// This is fixed below by doing it the old way.
case net::ready::READ:
sd.async_receive(buffer::null_buffers, std::move(handle));
case ready::READ:
sd.async_wait(wait_type::wait_read, std::move(handle));
break;
default:
@ -1409,18 +1419,16 @@ ircd::net::socket::wait(const wait_opts &opts)
switch(opts.type)
{
case net::ready::ERROR:
case ready::ERROR:
sd.async_wait(wait_type::wait_error, yield_context{to_asio{}});
break;
case net::ready::WRITE:
case ready::WRITE:
sd.async_wait(wait_type::wait_write, yield_context{to_asio{}});
break;
// See bug comment in callback version
case net::ready::READ:
sd.async_receive(buffer::null_buffers, yield_context{to_asio{}});
break;
case ready::READ:
sd.async_wait(wait_type::wait_read, yield_context{to_asio{}});
default:
throw ircd::not_implemented{};
@ -1428,9 +1436,10 @@ ircd::net::socket::wait(const wait_opts &opts)
}
void
ircd::net::socket::handle(const std::weak_ptr<socket> wp,
const ec_handler callback,
const error_code &ec)
ircd::net::socket::handle_ready(const std::weak_ptr<socket> wp,
const net::ready type,
const ec_handler callback,
error_code ec)
noexcept try
{
using namespace boost::system::errc;
@ -1438,8 +1447,11 @@ noexcept try
// After life_guard is constructed it is safe to use *this in this frame.
const life_guard<socket> s{wp};
log.debug("socket(%p): handle: %s (available: %zu)",
assert(ec == success || ec == operation_canceled);
log.debug("socket(%p)[%s]: ready %s: %s (available: %zu)",
this,
string(remote_ipport(*this)),
reflect(type),
string(ec),
available(*this));
@ -1448,6 +1460,20 @@ noexcept try
if(ec.category() == system_category()) switch(ec.value())
{
case success: if(type == ready::READ)
{
// The problem here is that the wait operation gives ec=success
// on both a socket error and when data is actually available. Ideally
// this should be giving the error, or at worst, something other than
// success with the expectation we also wait(ERROR) too. The workaround
// is to do a non-blocking peek here.
static char buf[1];
assert(!blocking(*this));
sd.receive(asio::mutable_buffers_1(buf, sizeof(buf)), sd.message_peek, ec);
break;
}
else break;
// We expose a timeout condition to the user, but hide
// other cancellations from invoking the callback.
case operation_canceled: