fix/esp32s3-tickless-timeout-overflow - #20024
Merged
xiaoxiang781216 merged 1 commit intoSep 1, 2026
Merged
Conversation
…C_2_CTICK CONFIG_ESP32S3_TICKLESS hangs forever the first time a task calls a sleep/timeout with a fractional-second component of roughly 134ms or more (e.g. usleep(500000)) while another task is also pending a timeout. Root cause: NSEC_2_CTICK() computes ((nsec) * CTICK_PER_USEC) / NSEC_PER_USEC. `nsec` (struct timespec's tv_nsec) is a 32-bit `long`, and CTICK_PER_USEC is 16 (the S3's systimer runs at 16MHz), so the multiplication overflows a 32-bit signed int for any tv_nsec at or above INT32_MAX / 16 (~134,217,728 ns). The overflowed (negative) result then gets added into up_timer_start()'s `uint64_t cpu_ticks`, wrapping around to a value near UINT64_MAX. tickless_setcounter() then programs the systimer alarm that many ticks in the future -- effectively never -- so nxsched_process_timer() is never called and the waiting task sleeps forever. Reproduced on real esp32s3-xiao hardware: apps/testing/ostest hung indefinitely right after starting user_main(), whose first statement is usleep(500000). Instrumented up_timer_start() to print its inputs and observed exactly the described overflow (cpu_ticks close to UINT64_MAX for tv_nsec=510000000). Confirmed root cause is the concurrent-timeout case specifically: user_main's usleep() alone works, and ostest_main's own usleep() alone works, but the two together (matching ostest's actual task_create() + concurrent usleep() pattern) reproduce the hang every time. Fix: cast to uint64_t before multiplying in all three *_2_CTICK macros, forcing 64-bit arithmetic throughout, matching how the CTICK_2_* (division) macros are already overflow-safe. Validated on esp32s3-xiao: with the fix, the full ostest suite (built with CONFIG_ESP32S3_TICKLESS=y) runs past the point it used to hang and completes end to end. Note: while testing, ostest's own round-robin test (rr_test) failed near the end of the run -- the two same-priority SCHED_RR threads did not appear to interleave under tickless. That looks like a separate, likely more architectural issue (time-slice preemption needs its own periodic re-arm, independent of one-shot sleep timeouts) and is not addressed by this fix; filing separately. Signed-off-by: Felipe Moura <moura.fmo@gmail.com>
xiaoxiang781216
approved these changes
Aug 31, 2026
fdcavalcanti
approved these changes
Aug 31, 2026
simbit18
approved these changes
Sep 1, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
CONFIG_ESP32S3_TICKLESShangs forever the first time a task sleepswith a fractional-second timeout of ~134ms or more (e.g.
usleep(500000)) while another task also has a pending timeout —e.g.
apps/testing/ostesthangs immediately, since that's exactlywhat
user_main()'s first statement does.Root cause
nsecis a 32-bitlongandCTICK_PER_USECis 16 (16MHz systimer),so
nsec * 16overflows a 32-bit signed int fortv_nsec >= ~134ms.The overflowed (negative) result gets added into
up_timer_start()'suint64_t cpu_ticks, wrapping to a value nearUINT64_MAX. Thesystimer alarm then gets programmed that many ticks in the future —
effectively never — so the sleeping task never wakes up.
SEC_2_CTICK/USEC_2_CTICKhave the same class of bug at higherthresholds.
Fix
Cast to
uint64_tbefore multiplying in all three macros, forcing64-bit arithmetic (matching the already-safe
CTICK_2_*macros).Also fixed a pre-existing nxstyle nit (missing blank line) in
tickless_isr(), next to the changed code.Testing
Board: Seeed XIAO ESP32-S3, built on current
master.up_timer_start()and confirmed the exactoverflow (
cpu_ticks=18446744073709121682for a ~500-700ms sleep).ostest(CONFIG_ESP32S3_TICKLESS=y) hangs right afterStarted user_main, reproducible every time from a clean flash.ostestsuite runs past that point and completes.sleep 3from NSH) keep correct timingbefore and after (3.06s measured).