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 = &param->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 = &param->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 = &param->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 = &param->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 = &param->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(&param->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(&param->timer);
+    }
+
+  hrtimer_cancel_sync(&param->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 = &param->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      = &param->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(&param->timer, hrtimer_test_callback_crita,
+                    0, HRTIMER_MODE_REL);
+
+      hrtimer_cancel_sync(&param->timer);
+      param->state = 1;
+      hrtimer_start(&param->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(&param->timer);
+    }
+}
+
+static void hrtimer_test_cancel_periodic(FAR hrtimer_test_t *param)
+{
+  uint64_t       count;
+  unsigned int     idx = 0;
+  FAR spinlock_t *lock = &param->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(&param->timer, hrtimer_test_callback_critdelay,
+                    0, HRTIMER_MODE_REL);
+
+      spin_unlock_irqrestore(lock, flags);
+
+      up_ndelay(10000);
+
+      flags = spin_lock_irqsave(lock);
+
+      hrtimer_start(&param->timer, hrtimer_test_callback_critdelay,
+                    0, HRTIMER_MODE_REL);
+
+      spin_unlock_irqrestore(lock, flags);
+
+      hrtimer_cancel(&param->timer);
+
+      up_ndelay(10000);
+
+      /* The hrtimer should not be restarted again after the cancellation. */
+
+      ASSERT(!param->timer.func);
+
+      hrtimer_cancel_sync(&param->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(&param->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(&param.timer);
 
-  hrtimer_init(timer);
+  /* Delay = 0 */
 
-  /* Start the timer with 500ms relative timeout */
+  hrtimer_test_oneshot(&param, 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(&param, 1u);
+  hrtimer_test_oneshot(&param, 10u);
+  hrtimer_test_oneshot(&param, 100u);
+  hrtimer_test_oneshot(&param, 1000u);
+  hrtimer_test_oneshot(&param, 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(&param, 100000u);
+  hrtimer_test_oneshot(&param, 1000000u);
+  hrtimer_test_oneshot(&param, 10000000u);
 
-      /* Cancel timer */
+#ifdef CONFIG_SMP
+  /* Test hrtimer_cancel_sync */
 
-      ret = hrtimer_cancel(&timer);
-      ASSERT(ret == OK);
+  hrtimer_test_cancel_sync(&param);
 
-      /* 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(&param);
+#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(&param);
+
+  /* Period hrtimer delay 100000ns */
 
-  /* Cancel the timer synchronously */
+  hrtimer_test_period(&param, 1000000u, 128u);
 
-  ASSERT(hrtimer_cancel_sync(&timer) == OK);
+  /* Random delay 12345ns and 67890ns */
 
-  return NULL;
+  hrtimer_test_rand(&param, 12345u);
+  hrtimer_test_rand_cancel(&param, 67890u);
+
+  return 0;
 }
 
 /****************************************************************************

Reply via email to