This is an automated email from the ASF dual-hosted git repository. linguini pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx-apps.git
commit 256d6685d57788ec47cf34957124d87f229e2571 Author: ouyangxiangzhen <[email protected]> AuthorDate: Wed Feb 25 09:48:19 2026 +0800 sched/hrtimer: Refactor the hrtimer_test. This commit refactored the hrtimer_test and provided significantly improved test-cases for both SMP and non-SMP. Signed-off-by: ouyangxiangzhen <[email protected]> --- testing/ostest/hrtimer.c | 546 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 453 insertions(+), 93 deletions(-) diff --git a/testing/ostest/hrtimer.c b/testing/ostest/hrtimer.c index 3cf847eb3..63040697c 100644 --- a/testing/ostest/hrtimer.c +++ b/testing/ostest/hrtimer.c @@ -41,9 +41,9 @@ /* Timer constants */ -#define HRTIMER_PERIOD_TEST_NR 15 -#define HRTIMER_THREAD_LOOP_NR 50 -#define HRTIMER_TEST_THREAD_NR (CONFIG_SMP_NCPUS * 5) +#define HRTIMER_TEST_RAND_ITER (1024 * 2) +#define HRTIMER_TEST_CSECTION 1024 +#define HRTIMER_TEST_THREAD_NR (CONFIG_SMP_NCPUS * 8) /* Set the tolerent latency to 10ms to allow hrtimer_test to pass * in QEMU. @@ -62,6 +62,8 @@ #define HRTIMER_TEST_TOLERENT_LATENCY (10 * NSEC_PER_MSEC) +#define hrtimer_test_ndelay(delay_ns) usleep(delay_ns / 1000 + 1) + /**************************************************************************** * Private Types ****************************************************************************/ @@ -71,99 +73,458 @@ typedef struct hrtimer_test_s { struct hrtimer_s timer; /* HRTimer instance */ + spinlock_t lock; /* Spinlock */ volatile uint64_t timestamp; /* Previous timestamp in nanoseconds */ volatile uint64_t count; /* Number of timer expirations */ uint64_t period; /* Expected period between expirations */ + volatile uint8_t state; /* Test state */ } hrtimer_test_t; /**************************************************************************** * Private Functions ****************************************************************************/ -/**************************************************************************** - * Name: test_hrtimer_callback - * - * Description: - * HRTimer callback function for test. - * - * - Verifies the timer interval is exactly 500ms (nanosecond precision) - * - Stops the test after 15 expirations - * - Re-arms the timer in absolute mode - * - * Input Parameters: - * hrtimer - Pointer to the expired HRTimer instance - * expired - The expired value of hrtimer - * - * Returned Value: - * Timer period in nanoseconds (NSEC_PER_50MS) - * - ****************************************************************************/ - -static uint64_t -test_hrtimer_callback(FAR const hrtimer_t *hrtimer, uint64_t expired) +static uint64_t hrtimer_test_callback_oneshot(FAR const hrtimer_t *timer, + uint64_t expired_ns) { - struct timespec ts; - int64_t diff; - uint64_t now; - int ret; + FAR hrtimer_test_t *param = (FAR hrtimer_test_t *)timer; - FAR struct hrtimer_test_s *test = - (FAR struct hrtimer_test_s *)hrtimer; + /* Save the timestamp when the callback was triggered */ - /* Increment expiration count */ + param->timestamp = clock_systime_nsec(); - test->count++; + /* Increment the callback count */ - /* Get current system time */ + param->count++; - clock_systime_timespec(&ts); - now = clock_time2nsec(&ts); - - /* Verify the timer interval is exactly - * 500ms with nsec resolution - */ + return 0; +} - diff = now - expired; +static void hrtimer_test_checkdelay(uint64_t timestamp, uint64_t expected) +{ + int64_t diff = timestamp - expected; - /* Ensure the time diff is valid. */ + /* Ensure the hrtimer trigger time is not earlier than expected. */ ASSERT(diff >= 0); + /* If the timer latency exceeds the tolerance, print a warning. */ + if (diff > HRTIMER_TEST_TOLERENT_LATENCY) { - printf("hrtimer_test: warning diff=%" PRIu64 " > %" PRIu64 "\n", - diff, HRTIMER_TEST_TOLERENT_LATENCY); + printf("hrtimer_test: [WARNING] hrtimer latency %" PRId64 + " is too late!!! (> %u)\n", diff, + (unsigned)HRTIMER_TEST_TOLERENT_LATENCY); } +} + +static void hrtimer_test_oneshot(FAR hrtimer_test_t *param, uint64_t delay) +{ + uint64_t count; + uint64_t now; + FAR hrtimer_t *timer = ¶m->timer; + + printf("hrtimer_test_oneshot %" PRIu64 " ns\n", delay); + + /* Save the current callback count. */ + + count = param->count; - test->timestamp = now; + /* Save the current system time. */ - /* Stop the test after HRTIMER_PERIOD_TEST_NR expirations */ + now = clock_systime_nsec(); - if (test->count < HRTIMER_PERIOD_TEST_NR) + ASSERT(hrtimer_start(timer, hrtimer_test_callback_oneshot, + delay + now, HRTIMER_MODE_ABS) == OK); + + /* Wait until the callback is triggered exactly once. */ + + while (count + 1 != param->count) { - return test->period; + hrtimer_test_ndelay(delay); } - else + + /* Check if the delay is within the acceptable tolerance. */ + + hrtimer_test_checkdelay(param->timestamp, now + delay); + + /* Cancel the timer. */ + + hrtimer_cancel_sync(timer); +} + +static void hrtimer_test_maximum(FAR hrtimer_test_t *param) +{ + uint64_t count; + uint64_t rest; + FAR hrtimer_t *timer = ¶m->timer; + + count = param->count; + + /* Start the hrtimer with maximum */ + + ASSERT(hrtimer_start(timer, hrtimer_test_callback_oneshot, UINT64_MAX, + HRTIMER_MODE_REL) == OK); + + /* Sleep for at least 1s */ + + hrtimer_test_ndelay(USEC_PER_SEC / 100); + + /* Ensure hrtimer is not alarmed */ + + ASSERT(count == param->count); + + rest = hrtimer_gettime(timer); + + ASSERT(rest < UINT64_MAX); + + ASSERT(hrtimer_cancel_sync(timer) == OK); + + printf("hrtimer_start with maximum delay, rest %" PRIu64 "\n", rest); +} + +static void hrtimer_test_rand(FAR hrtimer_test_t *param, uint64_t rand_ns) +{ + uint64_t count; + uint64_t now; + unsigned int idx; + uint64_t delay; + irqstate_t flags; + FAR hrtimer_t *timer = ¶m->timer; + + printf("hrtimer_test_rand %" PRIu64 " ns\n", rand_ns); + + /* Perform multiple iterations with random delays. */ + + for (idx = 0; idx < HRTIMER_TEST_RAND_ITER; idx++) { - test->active = false; - return 0; + /* Generate a random delay within the specified range. */ + + delay = rand() % rand_ns; + + ASSERT(timer->func == NULL); + + /* Enter critical section if the callback count is odd. */ + + count = param->count; + + if (count % 2u) + { + flags = up_irq_save(); + } + + now = clock_systime_nsec(); + ASSERT(hrtimer_start(timer, hrtimer_test_callback_oneshot, + delay, HRTIMER_MODE_REL) == 0); + if (count % 2u) + { + up_irq_restore(flags); + } + + /* Decide to wait for the callback or cancel the hrtimer. */ + + if (delay % 2u) + { + /* Wait for the callback. */ + + while (count + 1u != param->count) + { + hrtimer_test_ndelay(delay); + } + + /* Check the delay if the callback count is odd. */ + + if (count % 2u) + { + hrtimer_test_checkdelay(param->timestamp, now + delay); + } + } + + hrtimer_cancel_sync(timer); + ASSERT(timer->func == NULL); } + + hrtimer_cancel_sync(timer); } -/**************************************************************************** - * Name: hrtimer_test_callback - * - * Description: - * Simple HRTimer callback for threaded tests. - * - ****************************************************************************/ +static uint64_t hrtimer_test_cancel_callback(FAR const hrtimer_t *timer, + uint64_t expired_ns) +{ + FAR hrtimer_test_t *param = (FAR hrtimer_test_t *)timer; + FAR spinlock_t *lock = ¶m->lock; + uint64_t delay = 0; + irqstate_t flags = spin_lock_irqsave(lock); + + /* Random sleep */ + + delay = expired_ns % param->period; + + /* Check if the version is same. */ -static uint64_t -hrtimer_test_callback(FAR const hrtimer_t *hrtimer, uint64_t expired) + if (expired_ns == timer->expired) + { + param->timestamp = clock_systime_nsec(); + + /* Increment the callback count */ + + param->count++; + } + + spin_unlock_irqrestore(lock, flags); + + up_ndelay(delay); + + return 0; +} + +static void hrtimer_test_rand_cancel(FAR hrtimer_test_t *param, + uint64_t rand_ns) { + uint64_t now; + unsigned int idx; + uint64_t count; + uint64_t delay; + irqstate_t flags; + spinlock_t *lock = ¶m->lock; + + printf("hrtimer_test_rand cancel %" PRIu64 " ns\n", rand_ns); + + param->period = rand_ns; + + /* Perform multiple iterations with random delays. */ + + for (idx = 0; idx < HRTIMER_TEST_RAND_ITER; idx++) + { + /* Generate a random delay within the specified range. */ + + delay = rand() % rand_ns; + + flags = spin_lock_irqsave(lock); + + now = clock_systime_nsec(); + count = param->count; + ASSERT(hrtimer_start(¶m->timer, hrtimer_test_cancel_callback, + delay, HRTIMER_MODE_REL) == 0); + + spin_unlock_irqrestore(lock, flags); + + /* Decide to wait for the callback or cancel the hrtimer. */ + + if (delay % 2u) + { + /* Wait for the callback finished. */ + + while (param->count != count + 1u) + { + hrtimer_test_ndelay(delay); + } + + hrtimer_test_checkdelay(param->timestamp, now + delay); + } + + hrtimer_cancel(¶m->timer); + } + + hrtimer_cancel_sync(¶m->timer); +} + +static uint64_t hrtimer_test_callback_period(FAR const hrtimer_t *timer, + uint64_t expired_ns) +{ + FAR hrtimer_test_t *param = (FAR hrtimer_test_t *)timer; + uint64_t interval = param->period; + + param->count++; + param->timestamp = clock_systime_nsec(); + + return interval; +} + +static void hrtimer_test_period(FAR hrtimer_test_t *param, + uint64_t delay_ns, + unsigned int iters) +{ + uint64_t timestamp; + uint64_t count = param->count; + FAR hrtimer_t *timer = ¶m->timer; + + printf("hrtimer_test_period %" PRIu64 " ns\n", delay_ns); + + param->period = delay_ns; + ASSERT(param->period > 0); + + timestamp = clock_systime_nsec(); + + ASSERT(hrtimer_start(timer, hrtimer_test_callback_period, + delay_ns, HRTIMER_MODE_REL) == OK); + + hrtimer_test_ndelay(iters * delay_ns); + + hrtimer_cancel_sync(timer); + ASSERT(timer->func == NULL); + + printf("periodical hrtimer triggered %" PRIu64 " times, " + "elapsed nsec %" PRIu64 "\n", param->count - count, + param->timestamp - timestamp); + + if (param->count - count < iters) + { + printf("hrtimer_test: [WARNING] periodical hrtimer" + "triggered times < %u\n", iters); + } +} + +#ifdef CONFIG_SMP +static uint64_t hrtimer_test_callback_crita(FAR const hrtimer_t *timer, + uint64_t expired_ns) +{ + FAR hrtimer_test_t *param = (FAR hrtimer_test_t *)timer; + + /* change status */ + + if (param->state == 0) + { + param->state = 1; + param->count++; + } + + /* check whether parameter be changed by another critical section */ + + ASSERT(param->state == 1); + param->state = 0; + return 0; } +static uint64_t hrtimer_test_callback_critb(FAR const hrtimer_t *timer, + uint64_t expired_ns) +{ + FAR hrtimer_test_t *param = (FAR hrtimer_test_t *)timer; + + /* change status */ + + if (param->state == 1) + { + param->state = 0; + param->count++; + } + + /* check whether parameter be changed by another critical section */ + + ASSERT(param->state == 0); + param->state = 1; + + return 0; +} + +static uint64_t hrtimer_test_callback_critdelay(FAR const hrtimer_t *timer, + uint64_t expired_ns) +{ + FAR hrtimer_test_t *param = (FAR hrtimer_test_t *)timer; + FAR spinlock_t *lock = ¶m->lock; + irqstate_t flags; + + flags = spin_lock_irqsave(lock); + param->count++; + spin_unlock_irqrestore(lock, flags); + + up_ndelay(100 * NSEC_PER_USEC); + + return 300 * NSEC_PER_USEC; +} + +static void hrtimer_test_cancel_sync(FAR hrtimer_test_t *param) +{ + unsigned int idx = 0; + + ASSERT(!param->timer.func); + + param->count = 0; + + /* This test is to validate if the hrtimer can ensure the + * callback function be finished after the hrtimer_cancel_sync + * is called. + */ + + for (idx = 0; idx < HRTIMER_TEST_CSECTION; ) + { + param->state = 0; + hrtimer_start(¶m->timer, hrtimer_test_callback_crita, + 0, HRTIMER_MODE_REL); + + hrtimer_cancel_sync(¶m->timer); + param->state = 1; + hrtimer_start(¶m->timer, hrtimer_test_callback_critb, + 0, HRTIMER_MODE_REL); + + if (++idx % (HRTIMER_TEST_CSECTION / 4) == 0) + { + printf("hrtimer_test_cancel_sync passed %d times.\n", idx); + } + + hrtimer_cancel_sync(¶m->timer); + } +} + +static void hrtimer_test_cancel_periodic(FAR hrtimer_test_t *param) +{ + uint64_t count; + unsigned int idx = 0; + FAR spinlock_t *lock = ¶m->lock; + + ASSERT(!param->timer.func); + + param->count = 0; + + /* This test to check if the hrtimer can ensure the perodical callback + * can not restart the timer again after the hrtimer_cancel_sync is + * called. + */ + + for (idx = 0; idx < HRTIMER_TEST_CSECTION; idx++) + { + irqstate_t flags = spin_lock_irqsave(lock); + + hrtimer_start(¶m->timer, hrtimer_test_callback_critdelay, + 0, HRTIMER_MODE_REL); + + spin_unlock_irqrestore(lock, flags); + + up_ndelay(10000); + + flags = spin_lock_irqsave(lock); + + hrtimer_start(¶m->timer, hrtimer_test_callback_critdelay, + 0, HRTIMER_MODE_REL); + + spin_unlock_irqrestore(lock, flags); + + hrtimer_cancel(¶m->timer); + + up_ndelay(10000); + + /* The hrtimer should not be restarted again after the cancellation. */ + + ASSERT(!param->timer.func); + + hrtimer_cancel_sync(¶m->timer); + count = param->count; + + hrtimer_test_ndelay(10000); + + ASSERT(count == param->count); + + if (++idx % (HRTIMER_TEST_CSECTION / 4) == 0) + { + printf("hrtimer_test_cancel_periodic passed %d times. count %" + PRIu64 "\n", idx, param->count); + } + } + + hrtimer_cancel_sync(¶m->timer); +} +#endif + /**************************************************************************** * Name: hrtimer_test_thread * @@ -174,56 +535,55 @@ hrtimer_test_callback(FAR const hrtimer_t *hrtimer, uint64_t expired) static void * hrtimer_test_thread(void *arg) { - hrtimer_test_t test; - int ret; - uint64_t stamp; - int loop_cnt = 0; - hrtimer_t *timer = &test.timer; + hrtimer_test_t param = + { + 0 + }; - /* Initialize the high-resolution timer */ + hrtimer_init(¶m.timer); - hrtimer_init(timer); + /* Delay = 0 */ - /* Start the timer with 500ms relative timeout */ + hrtimer_test_oneshot(¶m, 0u); - stamp = test.timestamp; - ASSERT(hrtimer_start(timer, test_hrtimer_callback, - 50 * NSEC_PER_MSEC, HRTIMER_MODE_REL) == OK); + /* 0 < Delay < 10000 */ - /* Wait until the test completes */ + hrtimer_test_oneshot(¶m, 1u); + hrtimer_test_oneshot(¶m, 10u); + hrtimer_test_oneshot(¶m, 100u); + hrtimer_test_oneshot(¶m, 1000u); + hrtimer_test_oneshot(¶m, 10000u); - while (test.timestamp != stamp) - { - usleep(USEC_PER_MSEC); - } + /* 10000 < Delay < 10000000 */ - while (loop_cnt++ < HRTIMER_THREAD_LOOP_NR) - { - uint64_t delay = rand() % NSEC_PER_MSEC; + hrtimer_test_oneshot(¶m, 100000u); + hrtimer_test_oneshot(¶m, 1000000u); + hrtimer_test_oneshot(¶m, 10000000u); - /* Cancel timer */ +#ifdef CONFIG_SMP + /* Test hrtimer_cancel_sync */ - ret = hrtimer_cancel(&timer); - ASSERT(ret == OK); + hrtimer_test_cancel_sync(¶m); - /* Start timer with fixed period */ + /* Test hrtimer_cancel */ - ret = hrtimer_start(&timer, hrtimer_test_callback, - 10 * NSEC_PER_USEC, HRTIMER_MODE_REL); - ASSERT(ret == OK); + hrtimer_test_cancel_periodic(¶m); +#endif - /* Start timer with random delay */ + /* Maximum hrtimer delay test. */ - ret = hrtimer_start(&timer, hrtimer_test_callback, - delay, HRTIMER_MODE_REL); - ASSERT(ret == OK); - } + hrtimer_test_maximum(¶m); + + /* Period hrtimer delay 100000ns */ - /* Cancel the timer synchronously */ + hrtimer_test_period(¶m, 1000000u, 128u); - ASSERT(hrtimer_cancel_sync(&timer) == OK); + /* Random delay 12345ns and 67890ns */ - return NULL; + hrtimer_test_rand(¶m, 12345u); + hrtimer_test_rand_cancel(¶m, 67890u); + + return 0; } /****************************************************************************
