From d5d7bb61e59949871feb732d2e8117dd4b321d6f Mon Sep 17 00:00:00 2001 From: Stephen Noonan Date: Fri, 3 Jan 2025 16:47:19 -0500 Subject: [PATCH] fix(newlib): usleep returning early This commit updates usleep() to always sleep for the required sleep period or more. This fixes a bug where the usleep() could sleep for less than the request sleep period. Closes https://github.com/espressif/esp-idf/pull/15132 --- components/newlib/src/time.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/components/newlib/src/time.c b/components/newlib/src/time.c index 1338803af0..3dfbc99115 100644 --- a/components/newlib/src/time.c +++ b/components/newlib/src/time.c @@ -207,10 +207,25 @@ int usleep(useconds_t us) if (us < us_per_tick) { esp_rom_delay_us((uint32_t) us); } else { - /* since vTaskDelay(1) blocks for anywhere between 0 and portTICK_PERIOD_MS, - * round up to compensate. - */ - vTaskDelay((us + us_per_tick - 1) / us_per_tick); + /* vTaskDelay may return up to (n-1) tick periods due to the tick ISR + being asynchronous to the call. We must sleep at least the specified + time, or longer. Checking the monotonic clock allows making an + additional call to vTaskDelay when needed to ensure minimal time is + actually slept. Adding `us_per_tick - 1` prevents ever passing 0 to + vTaskDelay(). + */ + uint64_t now_us = esp_time_impl_get_time(); + uint64_t target_us = now_us + us; + do { + vTaskDelay((((target_us - now_us) + us_per_tick - 1) / us_per_tick)); + now_us = esp_time_impl_get_time(); + /* If the time left until the target is less than 1 tick, then we use ROM delay to fill the gap */ + uint64_t time_left = target_us - now_us; + if (time_left != 0 && time_left < us_per_tick) { + esp_rom_delay_us(time_left); + break; + } + } while (now_us < target_us); } return 0; }