Skip to content

Refactor TCPSocket::setup Timeout and Error Handling#511

Open
srvald wants to merge 6 commits into
UniversalRobots:masterfrom
srvald:tcp_socket_timeout
Open

Refactor TCPSocket::setup Timeout and Error Handling#511
srvald wants to merge 6 commits into
UniversalRobots:masterfrom
srvald:tcp_socket_timeout

Conversation

@srvald
Copy link
Copy Markdown
Contributor

@srvald srvald commented May 20, 2026

Refactor TCPSocket::setup

Description

This PR refactors the TCPSocket::setup() method to resolve the blocking behavior during network failures.

The implementation now introduces an asynchronous approach to enforce a connection timeout and capture socket errors.

Step-by-Step Modifications

1. Signature Update (Timeout Parameter)

  • Added a new timeout parameter (std::chrono::milliseconds) to the setup() function. This makes it possible to define exactly how long to wait for a connection before giving up. Otherwise, the code blocks indefinitely, keeping the thread hanging even if the number of max tries is set to 1. The default value has been set to 500 ms.

2. Error Tracking (std::error_code)

  • Introduced a std::error_code socket_error variable.
  • Now, getLastSocketErrorCode() is called immediately after any failure to store the exact system reason (e.g., ECONNREFUSED, EHOSTUNREACH) so it can be reported later.

3. Non-Blocking Connection Logic

  • State Change: The socket is temporarily set to non-blocking mode using fcntl() and O_NONBLOCK.
  • Async Connect: ::connect() is called. If it returns EINPROGRESS, the code now hands control over to ::select().
  • Timeout Enforcement: select() waits for the socket to become writable, strictly bounded by the newly provided timeout parameter.
  • Error Verification: If select() indicates readiness, ::getsockopt(..., SO_ERROR, ...) is used to verify if the connection actually succeeded or if it failed in the background.
  • State Restoration: The socket is restored to its original blocking state before proceeding.

4. Proper Resource Cleanup on Failure

  • If socket_fd_ is created but the connection attempt fails, it is now properly closed.
  • Added ::ur_close(socket_fd_) and reset to INVALID_SOCKET inside the failure branches within the for loop to properly release resources. Also added error capturing if ::socket() creation fails directly.

5. Improved Logging & Debugging

  • Replaced the generic and hardcoded log message ("Please check that the robot is booted and reachable...") with the exact system string translated by socket_error.message().
  • Logs will now report "Reason: Connection refused", "Reason: Operation not permitted", or "Reason: Connection timed out", improving troubleshooting capabilities for end users.

Testing Performed

  • Connection Refused: Pointed to an active IP with a closed port. Verified the code bypasses select() and fails immediately.
image
  • Connection Timed Out: Simulated a network blackhole (e.g., using a dead IP). Verified the code waits exactly for the timeout duration before logging a timeout error.
image
  • Host Unreachable: Pointed to an invalid subnet. Verified immediate routing error capture.
image

Note

Medium Risk
Changes core TCP connection establishment logic (non-blocking connect + select()/getsockopt()), which can affect connectivity behavior across platforms and edge cases like fd limits/timeouts.

Overview
TCPSocket::setup() now accepts an additional timeout (default 500ms) and uses a non-blocking connect with select() to enforce connection timeouts instead of potentially blocking indefinitely.

Connection attempts now capture and surface the underlying std::error_code reason in retry logs, add extra failure-path cleanup (close/reset fd), and include a non-Windows guard for FD_SETSIZE overflow before using select().

Reviewed by Cursor Bugbot for commit 6b46158. Bugbot is set up for automated code reviews on this repo. Configure here.

@codecov
Copy link
Copy Markdown

codecov Bot commented May 20, 2026

Codecov Report

❌ Patch coverage is 58.49057% with 22 lines in your changes missing coverage. Please review.
✅ Project coverage is 77.20%. Comparing base (de90b90) to head (6b46158).
⚠️ Report is 4 commits behind head on master.

Files with missing lines Patch % Lines
src/comm/tcp_socket.cpp 58.49% 14 Missing and 8 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #511      +/-   ##
==========================================
+ Coverage   77.14%   77.20%   +0.06%     
==========================================
  Files         116      116              
  Lines        6354     6415      +61     
  Branches     2764     2792      +28     
==========================================
+ Hits         4902     4953      +51     
- Misses       1101     1106       +5     
- Partials      351      356       +5     
Flag Coverage Δ
check_version_ur10-3.15.8 11.37% <41.50%> (-0.81%) ⬇️
check_version_ur10e-10.11.0 11.96% <41.50%> (+0.22%) ⬆️
check_version_ur10e-5.15.2 11.96% <41.50%> (+0.22%) ⬆️
check_version_ur12e-10.12.1 11.96% <41.50%> (+0.17%) ⬆️
check_version_ur12e-5.25.1 12.22% <52.83%> (-0.35%) ⬇️
check_version_ur15-10.12.1 11.96% <41.50%> (+0.22%) ⬆️
check_version_ur15-5.25.1 11.96% <41.50%> (+0.02%) ⬆️
check_version_ur16e-10.12.1 11.96% <41.50%> (+0.22%) ⬆️
check_version_ur16e-5.25.1 12.22% <52.83%> (+0.29%) ⬆️
check_version_ur18-10.12.1 11.96% <41.50%> (+0.22%) ⬆️
check_version_ur18-5.25.1 11.96% <41.50%> (+0.22%) ⬆️
check_version_ur20-10.12.1 11.96% <41.50%> (+0.22%) ⬆️
check_version_ur20-5.25.1 12.22% <52.83%> (+0.29%) ⬆️
check_version_ur3-3.14.3 11.96% <41.50%> (-0.03%) ⬇️
check_version_ur30-10.12.1 11.96% <41.50%> (+0.17%) ⬆️
check_version_ur30-5.25.1 11.96% <41.50%> (+0.17%) ⬆️
check_version_ur3e-10.11.0 11.96% <41.50%> (+0.22%) ⬆️
check_version_ur3e-5.9.4 12.01% <41.50%> (+0.07%) ⬆️
check_version_ur5-3.15.8 12.01% <41.50%> (+0.02%) ⬆️
check_version_ur5e-10.11.0 11.96% <41.50%> (+0.17%) ⬆️
check_version_ur5e-5.12.8 12.01% <41.50%> (+0.22%) ⬆️
check_version_ur7e-10.11.0 12.01% <41.50%> (+0.27%) ⬆️
check_version_ur7e-5.22.2 11.96% <41.50%> (+0.22%) ⬆️
check_version_ur8long-10.12.1 11.96% <41.50%> (+0.22%) ⬆️
check_version_ur8long-5.25.1 11.96% <41.50%> (+0.02%) ⬆️
python_scripts 75.90% <ø> (ø)
start_ursim 84.24% <ø> (-0.06%) ⬇️
ur20-latest 72.76% <58.49%> (-0.01%) ⬇️
ur5-3.14.3 72.53% <58.49%> (+0.05%) ⬆️
ur5e-10.11.0 66.67% <58.49%> (+0.07%) ⬆️
ur5e-10.12.0 67.99% <58.49%> (+0.14%) ⬆️
ur5e-10.7.0 66.26% <58.49%> (-0.11%) ⬇️
ur5e-5.9.4 73.34% <58.49%> (+0.02%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Comment thread src/comm/tcp_socket.cpp
Comment thread src/comm/tcp_socket.cpp Outdated
Comment thread src/comm/tcp_socket.cpp Outdated
Comment thread src/comm/tcp_socket.cpp Outdated
Comment thread include/ur_client_library/comm/tcp_socket.h
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default mode and found 1 potential issue.

Fix All in Cursor

Reviewed by Cursor Bugbot for commit 6618266. Configure here.

Comment thread src/comm/tcp_socket.cpp Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants