Skip to content

Fix busy loop in rxpacket and support baudrates over 230400 in MacOS - #694

Merged
GyuH13 merged 8 commits into
mainfrom
feature-add-pollin
Sep 14, 2026
Merged

GyuH13 merged 8 commits into
mainfrom
feature-add-pollin

Conversation

@GyuH13

@GyuH13 GyuH13 commented Aug 25, 2026

Copy link
Copy Markdown
Member

Summary

  • Support baud rates above 230400 bps on macOS.
  • Use poll() in readPort() to avoid busy-wait. Wait up to 1 ms for serial data before returning.

Problem code

  1. PacketHandler::rxPacket() receives a status packet by calling PortHandler::readPort() repeatedly in a while loop.
  2. PortHandler::readPort() calls read() on the serial fd and returns immediately. The fd is opened with O_NONBLOCK, and termios is set to VMIN = 0, VTIME = 0, so read() returns at once when the receive buffer is empty.
  3. While the motor status packet has not arrived yet, readPort() returns 0 bytes immediately, and the rxPacket() loop spins as fast as it can until the packet timeout.

Problems

  1. High CPU usage.
  2. A large number of read() system calls and extra scheduler load.

Solution

  1. In readPort(), call poll() on the serial fd before read(), and wait up to 1 ms for the fd to become readable. If data arrives, read it immediately. If not, wait 1 ms and return 0.
    • This stops the tight retry loop that burns CPU while the buffer is empty.
    • When data is available, it is still read and returned right away, so the existing receive behavior is unchanged.

GyuH13 added 6 commits June 26, 2026 10:06
…ing IOSSIOSPEED. Update documentation to reflect changes in baud rate handling.
… reads and improve error handling. Update Protocol1 and Protocol2 packet handlers to check for read errors.
@GyuH13 GyuH13 self-assigned this Aug 25, 2026
@GyuH13 GyuH13 added the enhancement New feature or request label Aug 25, 2026
@GyuH13 GyuH13 added this to Platform Aug 25, 2026
@GyuH13
GyuH13 requested review from cho0h5 and removed request for Woojin-Crive and sunghowoo August 26, 2026 02:10

@cho0h5 cho0h5 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The poll change does not affect AIS.
LGTM👍

@yun-goon
yun-goon self-requested a review September 1, 2026 09:06
…cket() and improve error handling in the method.
@yun-goon

yun-goon commented Sep 2, 2026

Copy link
Copy Markdown
Member

I tested this PR with a U2D2 and two DXLs at 1 Mbps.

  1. The CPU usage reduction was confirmed.
  2. Ping latency did not regress when the response arrived immediately.
  3. However, when no data was available, readPort() became blocking for approximately 1 ms.
  4. For a non-responding target, the existing packet timeout was extended by more than 1 ms.
  5. poll() events related to closed or disconnected ports, such as POLLERR, POLLHUP, and POLLNVAL, are not explicitly handled.

Would it be possible to add handling for these cases before merging?

@GyuH13

GyuH13 commented Sep 8, 2026 •

Copy link
Copy Markdown
Member Author

I tested this PR with a U2D2 and two DXLs at 1 Mbps.

  1. The CPU usage reduction was confirmed.
  2. Ping latency did not regress when the response arrived immediately.
  3. However, when no data was available, readPort() became blocking for approximately 1 ms.
  4. For a non-responding target, the existing packet timeout was extended by more than 1 ms.
  5. poll() events related to closed or disconnected ports, such as POLLERR, POLLHUP, and POLLNVAL, are not explicitly handled.

Would it be possible to add handling for these cases before merging?

  1. When no data is available, readPort() is continuously called in a while loop. Although read() itself is non-blocking, the loop effectively waits until data becomes available, which is logically similar to the blocking behavior introduced by poll(). The difference is that poll() avoids the busy-loop and significantly reduces CPU usage.

  2. The additional 1 ms in the timeout for a non-responding target should not be a significant issue. The current packet timeout is already configured with sufficient margin.

  3. To maintain the existing error-handling behavior of RxPacket, readPort() temporarily returns 0 when there is no data available and -1 when an error occurs. Even if POLLHUP and POLLERR are explicitly checked, they will be mapped to -1, so they will still appear as RX_FAIL at the upper-level SDK.

@yun-goon yun-goon left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for the clarification. The CPU usage improvement is clear, and I did not observe a ping latency regression when data was ready, so I’m approving this PR.
As a follow-up, please add explicit handling for POLLERR, POLLHUP, and POLLNVAL. When POLLIN | POLLHUP is returned, pending data should be drained first, and terminal errors should then be mapped to -1 so that the upper layer reports COMM_RX_FAIL instead of a timeout.

@GyuH13
GyuH13 merged commit f838bc9 into main Sep 14, 2026
3 checks passed
@GyuH13
GyuH13 deleted the feature-add-pollin branch September 14, 2026 03:17
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

Status: No status

Development

Successfully merging this pull request may close these issues.

3 participants