Skip to content

RF24Mesh: replace rollover-unsafe millis() deadlines with elapsed-time checks#270

Merged
TMRh20 merged 2 commits into
masterfrom
copilot/patch-rollover-unsafe-millis-timeouts
Jun 6, 2026
Merged

RF24Mesh: replace rollover-unsafe millis() deadlines with elapsed-time checks#270
TMRh20 merged 2 commits into
masterfrom
copilot/patch-rollover-unsafe-millis-timeouts

Conversation

Copilot AI commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

This updates RF24Mesh timeout handling to eliminate millis() + N absolute-deadline comparisons that can misbehave across timer wraparound. All affected paths now use unsigned elapsed-time subtraction (millis() - start) while preserving existing timeout durations and control flow.

  • Patched timeout logic in RF24Mesh.cpp

    • write(const void*, uint8_t, size_t, uint8_t):
      • Replaced lookupTimeout = millis() + MESH_WRITE_TIMEOUT / millis() > lookupTimeout
      • With lookupStart + elapsed-time comparison
    • requestAddress() poll window:
      • Replaced timeout = millis() + 55 / while (millis() < timeout && ...)
      • With pollStart + while (millis() - pollStart < 55 && ...)
    • requestAddress() response wait:
      • Replaced timeout = millis() + 225 / while (millis() < timeout)
      • With responseStart + while (millis() - responseStart < 225)
  • Behavior preserved

    • Same timeout constants (MESH_WRITE_TIMEOUT, 55, 225)
    • No API changes
    • No unrelated refactors
const uint32_t start = millis();
while (millis() - start < 225) {
    // existing loop body
}
Original prompt

Patch all rollover-unsafe millis() timeout patterns across these repositories by replacing absolute-deadline logic like uint32_t timeout = millis() + N; while (millis() < timeout) {} and if (millis() > timeout) with rollover-safe elapsed-time comparisons using unsigned subtraction (for example const uint32_t start = millis(); while (millis() - start < N) {} and if (millis() - start > N)).

Create pull requests in the following repositories:

  • nRF24/RF24
  • nRF24/RF24Network
  • nRF24/RF24Mesh
  • nRF24/RF24Ethernet
  • nRF24/RF24Gateway

Confirmed unsafe sites already identified from repository code/search results include:

  1. nRF24/RF24
  • examples/scannerGraphic/scannerGraphic.ino: input timeout using unsigned long inputTimeout = millis() + 7000; and while (!Serial.available() && millis() < inputTimeout).
  1. nRF24/RF24Network
  • RF24Network.cpp: uint32_t timeout = millis() + 100; with if (millis() > timeout) in update().
  • RF24Network.cpp: fragmentation retry delay using uint32_t timer = millis() + 2; while (millis() < timer) {}.
  1. nRF24/RF24Mesh
  • RF24Mesh.cpp: uint32_t lookupTimeout = millis() + MESH_WRITE_TIMEOUT; with if (millis() > lookupTimeout || toNode == -2) in write(const void*, uint8_t, size_t, uint8_t).
  • RF24Mesh.cpp: uint32_t timeout = millis() + 55; while (millis() < timeout && pollCount < MESH_MAXPOLLS) in requestAddress().
  • RF24Mesh.cpp: timeout = millis() + 225; while (millis() < timeout) in requestAddress().
  1. nRF24/RF24Ethernet
  • RF24Client.cpp: uint32_t timeout = millis() + serverConnectionTimeout; while (len > tcp_sndbuf(fpcb)) { ... if (millis() > timeout) ... } in blocking_write().
  • RF24Client.cpp: volatile uint32_t timer = millis() + 5000; while (...) { if (millis() > timer) ... } in blocking_write().
  • examples/MQTT/mqtt_basic/mqtt_basic.ino: reconnect logic using clTimeout = millis() + 5001, if (millis() > clTimeout), and timer = millis() + 1000; while (millis() < timer).
  • examples/MQTT/mqtt_basic_2/mqtt_basic_2.ino: same reconnect patterns as above.
  1. nRF24/RF24Gateway
  • RF24Gateway.cpp: uint32_t txQueueTimer = millis() + 750; with if (millis() > txQueueTimer && queueSize >= 10) in handleRadioOut().

Requirements:

  • Create a separate PR for each repository.
  • Keep the fixes minimal and behavior-preserving except for rollover correctness.
  • Prefer introducing local start variables and elapsed-time comparisons rather than changing public APIs.
  • Preserve formatting/style as much as possible.
  • If additional rollover-unsafe millis() deadline comparisons are discovered during implementation in the targeted repos, patch them too if they are clearly the same class of bug.
  • Update only code that actually needs fixing; do not do unrelated cleanup.
  • In each PR description, summarize the unsafe pattern and why the subtraction-based form is rollover-safe.

The following is the prior conversation context from the user's chat exploration (may be truncated):

User: can you find all rollover unsafe millis() calls in RF24, RF24Network, RF24Mesh, RF24Ethernet & RF24Gateway?

Assistant: Yes — I found the clearly rollover-unsafe millis() usage patterns in those repos.

Unsafe pattern I looked for: storing an absolute deadline like timeout = millis() + N and later comparing with millis() < timeout or similar. That breaks across millis() wraparound.
Safe pattern: if (millis() - start >= interval) using unsigned subtraction.

Confirmed rollover-unsafe calls

RF24Mesh

  1. RF24Mesh.cpprequestAddress()
uint32_t timeout = millis() + 55;
#define MESH_MAXPOLLS 4
uint16_t contactNode[MESH_MAXPOLLS];
uint8_t pollCount = 0;

while (millis() < timeout && pollCount < MESH_MAXPOLLS) {

This is rollover-unsafe because it compares current time to an absolute future timestamp. If wrap happens before/around the loop, the condition can fail early or behave incorrectly.

RF24Ethernet

  1. RF24Client.hConnectState in-class initialization
volatile uint32_t connectTimestamp = millis();
volatile uint32_t sConnectionTimeout = serverConnectionTimeout;
volatile uint32_t serverTimer = millis();
volatile uint32_t cConnectionTimeout = clientConnectionTimeout;
volatile uint32_t clientTimer = millis();
volatile uint32_t closeTimer = millis();

These initializations are not inherently unsafe by themselves, but they are suspect state seeds because they store absolute times. Whether they’re unsafe depends on how they’re compared later.

  1. RF24Client.cpp — reopen-window timing
if (u->windowOpened == ...

</details>



<!-- START COPILOT CODING AGENT SUFFIX -->

*This pull request was created from Copilot chat.*
>

Copilot AI changed the title [WIP] Patch rollover-unsafe millis timeout patterns RF24Mesh: replace rollover-unsafe millis() deadlines with elapsed-time checks Jun 4, 2026
Copilot finished work on behalf of TMRh20 June 4, 2026 08:23
Copilot AI requested a review from TMRh20 June 4, 2026 08:23
Copilot finished work on behalf of TMRh20 June 4, 2026 08:25
Copilot finished work on behalf of TMRh20 June 4, 2026 08:27
@github-actions

github-actions Bot commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Memory usage change @ 31b59d1

Board Flash % RAM for global variables %
arduino:avr:nano 💚 -2 - 0 -0.01 - 0 0 - 0 0 - 0
arduino:samd:mkrzero 💚 -16 - -16 -0.01 - -0.01 0 - 0 0 - 0
Click for full report per board

arduino:avr:nano

Sketch Flash % RAM for global variables %
examples/RF24Mesh_Example 0 0 0 0
examples/RF24Mesh_Example_Master_Statics -2 -0.01 0 0
examples/RF24Mesh_Example_Master_To_Nodes -2 -0.01 0 0
examples/RF24Mesh_Example_Node2Node -2 -0.01 0 0
examples/RF24Mesh_Example_Node2NodeExtra -2 -0.01 0 0
examples/RF24Mesh_SerialConfig 0 0 0 0
examples/RF24Mesh_Example_Master -2 -0.01 0 0

arduino:samd:mkrzero

Sketch Flash % RAM for global variables %
examples/RF24Mesh_Example -16 -0.01 0 0
examples/RF24Mesh_Example_Master_Statics -16 -0.01 0 0
examples/RF24Mesh_Example_Master_To_Nodes -16 -0.01 0 0
examples/RF24Mesh_Example_Node2Node -16 -0.01 0 0
examples/RF24Mesh_Example_Node2NodeExtra -16 -0.01 0 0
examples/RF24Mesh_SerialConfig -16 -0.01 0 0
examples/RF24Mesh_Example_Master -16 -0.01 0 0

@TMRh20 TMRh20 marked this pull request as ready for review June 4, 2026 09:03
@TMRh20 TMRh20 requested a review from 2bndy5 June 4, 2026 09:03

@2bndy5 2bndy5 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.

again with the const in a func body: looks good on a size delta report. Has this been tested IRL?

@2bndy5

2bndy5 commented Jun 4, 2026

Copy link
Copy Markdown
Member

I think I'm confusing const with static const behavior in a func body.

@TMRh20

TMRh20 commented Jun 4, 2026

Copy link
Copy Markdown
Member

Nope, still gotta test out all these millis() changes to make it official.

And yeah, I barely know the difference between const and static const , had to google it to make sure. :laugh:

@TMRh20 TMRh20 merged commit 9e38b29 into master Jun 6, 2026
59 checks passed
@TMRh20 TMRh20 deleted the copilot/patch-rollover-unsafe-millis-timeouts branch June 6, 2026 11:09
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.

3 participants