Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Potential Timing Issue in canShow with Counter Rollover Handling #415

Open
bajiuz opened this issue Dec 19, 2024 · 0 comments
Open

Potential Timing Issue in canShow with Counter Rollover Handling #415

bajiuz opened this issue Dec 19, 2024 · 0 comments

Comments

@bajiuz
Copy link

bajiuz commented Dec 19, 2024

Description:

Hello Adafruit NeoPixel Team,

I’ve noticed a potential issue in the implementation of the canShow function, particularly in the way it handles counter rollovers for the 32-bit micros() timer. While the current logic does attempt to handle rollover scenarios, resetting endTime to now when endTime > now may introduce an unintended delay of up to 300 microseconds, which could lead to timing inconsistencies in certain edge cases.


Steps to Reproduce:

  1. Use the Adafruit_NeoPixel library in an environment where micros() counter rollover is a possibility (approximately every 70 minutes).
  2. Allow a significant delay between show() calls, such that endTime and now end up on either side of the 32-bit counter rollover.
  3. Call canShow(). Observe that endTime > now triggers a reset of endTime to now, which effectively restarts the 300-microsecond timer.

Observed Behavior:

When the counter rolls over and endTime > now, the following line in the canShow function resets endTime to now:

if (endTime > now) {
    endTime = now;
}

This introduces an additional delay of up to 300 microseconds because the now - endTime difference is effectively reset to 0.

For example:

  • Before rollover:
    • endTime = 0xFFFFFFF0
    • now = 0x00000010
    • After rollover handling: endTime = now
    • The 300-microsecond interval starts over unnecessarily.

Expected Behavior:

The function should account for the rollover naturally by using proper time difference calculations with uint32_t, leveraging the fact that unsigned integer arithmetic can handle rollovers correctly.


Suggested Fix:

Instead of resetting endTime to now, modify the logic to calculate the time difference directly, even in rollover cases. Here's a proposed fix for the canShow function:

bool canShow(void) {
    uint32_t now = micros();

    // Calculate the time difference, accounting for rollover
    uint32_t delta = (now >= endTime) ? (now - endTime) : (UINT32_MAX - endTime + now + 1);

    return delta >= 300L;  // Check if the delay has been satisfied
}

Why This Works:

  • When now < endTime (rollover scenario), the difference is computed as:
    image
    This naturally handles rollover without resetting endTime.
  • It avoids introducing unintended delays caused by resetting endTime to now.

Impact:

This issue may not affect typical NeoPixel usage with frequent show() calls, but in scenarios where show() is invoked infrequently (e.g., longer than 70 minutes), this behavior could cause timing inconsistencies or unexpected stalls.


Thank you for maintaining this excellent library! Please let me know if you need further clarification or testing support.

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

No branches or pull requests

1 participant