Skip to content

Let PrimaryClient take a port and read package length safely - #582

Merged
urrsk merged 6 commits into
UniversalRobots:masterfrom
urrsk:primaryPort
Sep 30, 2026
Merged

urrsk merged 6 commits into
UniversalRobots:masterfrom
urrsk:primaryPort

Conversation

@urrsk

@urrsk urrsk commented Sep 29, 2026

Copy link
Copy Markdown
Member

Fake-server tests had to bind the real primary port because PrimaryClient always connected to UR_PRIMARY_PORT. Another listener made TCPServer retry the bind forever, hanging the tests. PrimaryClient now takes an optional port, and the fake-server fixtures use dedicated test ports.

getPackageLength() also copies the length bytes before the endian swap, so an unaligned position in the byte stream is no longer read through a cast.

Fake-server tests had to bind the real primary port because PrimaryClient
always connected to UR_PRIMARY_PORT. Another listener made TCPServer retry
the bind forever, hanging the tests. PrimaryClient now takes an optional
port, and the fake-server fixtures use dedicated test ports.

getPackageLength() also copies the length bytes before the endian swap, so
an unaligned position in the byte stream is no longer read through a cast.
@urrsk urrsk added the enhancement New feature or request label Sep 29, 2026
@codecov

codecov Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 88.43%. Comparing base (00893ff) to head (d6e3b54).
⚠️ Report is 2 commits behind head on master.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##           master     #582      +/-   ##
==========================================
- Coverage   88.63%   88.43%   -0.21%     
==========================================
  Files           2        2              
  Lines         440      441       +1     
==========================================
  Hits          390      390              
- Misses         50       51       +1     
Flag Coverage Δ
python_scripts 75.90% <ø> (ø)
start_ursim 91.34% <ø> (-0.26%) ⬇️

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

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

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

🟡 Changes recommended

Fixed ports retain the test-hang risk, and the constructor change breaks shared-library ABI compatibility.

Review effort: Balanced
Findings: 1 High severity · 2 Medium severity

Open (3)
What changed in this PR

Adds configurable PrimaryClient ports for isolated fake-server tests and safely reads potentially unaligned package lengths.

Changes:

  • Adds an optional PrimaryClient port.
  • Moves fake-server tests to dedicated ports.
  • Replaces an unaligned cast with memcpy.
File Description
primary_client.h Exposes configurable port API.
primary_client.cpp Connects using the supplied port.
package_header.h Safely reads package lengths.
test_primary_client.cpp Uses a fake-server test port.
test_primary_client_reconnect.cpp Uses a test port for reconnect tests.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread include/ur_client_library/primary/primary_client.h
Comment thread tests/test_primary_client.cpp Outdated
Comment thread tests/test_primary_client_reconnect.cpp Outdated
A fixed test port can already be in use, and TCPServer retries that bind
forever. The fake-server fixtures now pass port 0 so bind() selects an
available port, and the reconnect test reuses that assigned port.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

🟡 Changes recommended

The unaligned package-length behavior needs a focused regression test.

Review effort: Balanced
Findings: 1 High severity · 1 Medium severity

Open (2)
Resolved since last review (2)

Comment thread include/ur_client_library/primary/package_header.h
Adding a defaulted port parameter replaced the exported constructor, so
already-built callers of the shared library would fail to load. The
original constructor now delegates to a separate overload that takes the
port.
Parser tests pass buffers from their aligned start, so they never call
getPackageLength() one byte into a stream. That is the read the old cast
could mishandle.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

🔵 Needs a closer look

The replacement reconnect server can still retry indefinitely when its released port cannot be rebound.

Review effort: Balanced
Findings: None

Resolved since last review (2)
Previously missed (1)

In code that hasn't changed since last review

Medium severity Prevent replacement server from hanging on fixed-port rebind

tests/​test_primary_client_reconnect.cpp:182

The replacement server still binds a previously released fixed port with TCPServer's default unlimited retries. This port remains unreserved for the entire reconnect/stop interval, so another concurrent test or process can claim it; on platforms where immediate rebinding is delayed, the same bind can also fail temporarily. In either case this constructor can hang forever, preserving the failure mode the PR is intended to remove. Give FakePrimaryServer a finite bind-attempt option and use it here (or otherwise keep/reserve the port) so a rebind failure terminates the test instead of blocking indefinitely.

The replacement server bound a port that had been free for the whole stop
interval, and TCPServer retries that bind forever. FakePrimaryServer now
takes a bind retry limit, and the test uses a finite one so a collision
fails the test.
@urrsk

urrsk commented Sep 29, 2026

Copy link
Copy Markdown
Member Author

Addressed the previously missed rebind hang in 4590227.

The replacement server in PrimaryClientReconnectTest.stop_not_blocked_by_stuck_reconnect_thread was binding a port that had been free for the whole stop interval, and TCPServer retries that bind forever. FakePrimaryServer now takes a bind retry limit, and that replacement server uses 5 attempts, 100 ms apart, so a collision fails the test instead of hanging.

#582 (review)

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

🟡 Changes recommended

The same-port restart test remains unreliable on Windows because the port can outlive the brief retry period in TCP TIME_WAIT.

Review effort: Balanced
Findings: 1 Medium severity

Open (1)

Comment thread tests/test_primary_client_reconnect.cpp
@urrsk
urrsk marked this pull request as ready for review September 29, 2026 11:23
@urrsk
urrsk requested a review from a team September 29, 2026 11:23
urrsk added a commit to urrsk/Universal_Robots_Client_Library that referenced this pull request Sep 29, 2026
…l request.

Those changes now live in UniversalRobots#582, including the review follow-ups. The RTDE package-length read stays, because coalesced fake-server frames still need it.
Comment thread include/ur_client_library/primary/primary_client.h Outdated
This reverts the separate two-argument overload. ABI stability is not
guaranteed (see UniversalRobots#562), so a defaulted port parameter is preferred.
Copilot AI review requested due to automatic review settings September 29, 2026 14:04

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

🔵 Needs a closer look

The constructor change removes the existing shared-library symbol, breaking binary compatibility.

Review effort: Balanced
Findings: None

Resolved since last review (1)
Previously missed (1)

In code that hasn't changed since last review

Medium severity Use the supplied notifier or document it as unused

include/​ur_client_library/​primary/​primary_client.h:73

This new parameter documentation promises that the supplied notifier receives lifecycle events, but the constructor marks the parameter unused and passes the separate default-constructed notifier_ member to the pipeline. Either initialize/use the supplied notifier or document that this parameter is retained as an unused compatibility argument.

This issue also appears on line 76 of the same file.

@urrsk
urrsk requested a review from urfeex September 30, 2026 06:53
@mergify

mergify Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

This pull request does not currently match the merge queue conditions, so it cannot be queued from here. The box comes back if it matches again.

@urrsk
urrsk merged commit 758467c into UniversalRobots:master Sep 30, 2026
79 of 100 checks passed
@urrsk
urrsk deleted the primaryPort branch September 30, 2026 10:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants