Enhance UdpSocket error handling and usability

- Introduced a new `quarantine_socket` function to centralize error handling for socket operations, improving code clarity and maintainability.
- Updated error handling in `UdpSocket` methods to utilize `quarantine_socket`, ensuring consistent logging and socket management across setup, send, and receive operations.
- Added a `cancel` method to `UdpSocket` to safely cancel socket operations when the socket is not usable, enhancing robustness in error scenarios.
- Introduced a `m_socket_usable` flag to track socket state, preventing operations on unusable sockets and improving overall stability.
This commit is contained in:
Rad
2026-03-03 01:26:37 +01:00
parent e13af34188
commit 682e84e5f4
2 changed files with 44 additions and 5 deletions

View File

@@ -19,6 +19,26 @@ namespace endian = boost::endian;
namespace asio = boost::asio;
using boost::asio::ip::udp;
namespace {
void quarantine_socket(udp::socket& socket, bool& socket_usable, const char* action, const std::exception& e)
{
#ifdef __APPLE__
socket_usable = false;
boost::system::error_code ec;
socket.cancel(ec);
socket.close(ec);
BOOST_LOG_TRIVIAL(error) << action << ": " << e.what();
#else
(void) socket;
(void) socket_usable;
(void) action;
BOOST_LOG_TRIVIAL(error) << e.what();
#endif
}
}
namespace Slic3r {
@@ -649,7 +669,7 @@ UdpSocket::UdpSocket( Bonjour::ReplyFn replyfn, const asio::ip::address& multica
BOOST_LOG_TRIVIAL(info) << "Socket created. Multicast: " << multicast_address << ". Interface: " << interface_address;
}
catch (std::exception& e) {
BOOST_LOG_TRIVIAL(error) << e.what();
quarantine_socket(socket, m_socket_usable, "UdpSocket setup failed", e);
}
}
@@ -673,7 +693,7 @@ UdpSocket::UdpSocket( Bonjour::ReplyFn replyfn, const asio::ip::address& multica
BOOST_LOG_TRIVIAL(info) << "Socket created. Multicast: " << multicast_address;
}
catch (std::exception& e) {
BOOST_LOG_TRIVIAL(error) << e.what();
quarantine_socket(socket, m_socket_usable, "UdpSocket setup failed", e);
}
}
@@ -695,8 +715,20 @@ UdpSocket::~UdpSocket()
}
}
void UdpSocket::cancel()
{
if (!m_socket_usable)
return;
boost::system::error_code ec;
socket.cancel(ec);
}
void UdpSocket::send()
{
if (!m_socket_usable)
return;
try {
for (const auto& request : requests)
socket.send_to(asio::buffer(request.m_data), mcast_endpoint);
@@ -705,12 +737,15 @@ void UdpSocket::send()
async_receive();
}
catch (std::exception& e) {
BOOST_LOG_TRIVIAL(error) << e.what();
quarantine_socket(socket, m_socket_usable, "UdpSocket send failed", e);
}
}
void UdpSocket::async_receive()
{
if (!m_socket_usable)
return;
try {
// our session to hold the buffer + endpoint
auto session = create_session();
@@ -719,12 +754,15 @@ void UdpSocket::async_receive()
, boost::bind(&UdpSocket::receive_handler, this, session, asio::placeholders::error, asio::placeholders::bytes_transferred));
}
catch (std::exception& e) {
BOOST_LOG_TRIVIAL(error) << e.what();
quarantine_socket(socket, m_socket_usable, "UdpSocket receive setup failed", e);
}
}
void UdpSocket::receive_handler(SharedSession session, const boost::system::error_code& error, size_t bytes)
{
if (!m_socket_usable)
return;
// let io_service to handle the datagram on session
// from boost documentation io_service::post:
// The io_service guarantees that the handler will only be called in a thread in which the run(), run_one(), poll() or poll_one() member functions is currently being invoked.

View File

@@ -161,7 +161,7 @@ public:
void send();
void async_receive();
void cancel() { socket.cancel(); }
void cancel();
protected:
void receive_handler(SharedSession session, const boost::system::error_code& error, size_t bytes);
virtual SharedSession create_session() const = 0;
@@ -172,6 +172,7 @@ protected:
boost::asio::ip::udp::endpoint mcast_endpoint;
std::shared_ptr< boost::asio::io_service > io_service;
std::vector<BonjourRequest> requests;
bool m_socket_usable { true };
};
class LookupSocket : public UdpSocket