Repository: apache/nuttx
Pull request #16231: sched/wqueue: Refactor delayed and periodic workqueue.
Author: login: Fix-Point
Labels: name: State: Needs more testing
name: Area: OS Components
description: OS Components issues
name: Size: L
description: The size of the change in this PR is large
Created: 2025-04-17T06:59:08Z | Merged: 2025-05-06T18:02:10Z

## Summary

This commit refactors the delayed and the periodic workqueue, and changes the timer to be set in `work_thread`. The improvements of this change are as follows:
- Fix the memory reuse problem of the original periodic workqueue implementation.  https://github.com/apache/nuttx/pull/15937#issuecomment-2808973151
- By removing the `wdog_s` structure in the `work_s` structure, the memory overhead of each `work_s` structure is reduced by about 30 bytes.
- Set the timer for each workqueue instead of each work, which improves system performance.
- Simplified the workqueue cancel processing.

## Impact

This modification mainly affects modules that use workqueue, and may affect the timing of workqueue execution and workqueue cancel semantics.

## Testing

`wqueue_test` tested on QEMU/x86_64. Periodical workqueue can work now.





Diff:
diff --git a/include/nuttx/wdog.h b/include/nuttx/wdog.h
index 90ba3fc85f3ce..02f18b1b30847 100644
--- a/include/nuttx/wdog.h
+++ b/include/nuttx/wdog.h
@@ -72,12 +72,7 @@ struct wdlist_node
   FAR struct wdlist_node *next;
 };
 
-/* This is the internal representation of the watchdog timer structure.
- * Notice !!!
- * Carefully with the struct wdog_s order, you may not directly modify
- * this. This struct will combine in struct work_s in union type, and,
- * wqueue will modify/check this struct in kwork work_qcancel().
- */
+/* Support a doubly linked list of watchdog timers */
 
 struct wdog_s
 {
@@ -87,7 +82,7 @@ struct wdog_s
 #ifdef CONFIG_PIC
   FAR void          *picbase;    /* PIC base address */
 #endif
-  clock_t            expired;    /* Timer associated with the absoulute time */
+  clock_t            expired;    /* Timer associated with the absolute time */
 };
 
 struct wdog_period_s
@@ -168,7 +163,7 @@ int wd_start(FAR struct wdog_s *wdog, sclock_t delay,
  *
  * Input Parameters:
  *   wdog     - Watchdog ID
- *   ticks    - Absoulute time in clock ticks
+ *   ticks    - Absolute time in clock ticks
  *   wdentry  - Function to call on timeout
  *   arg      - Parameter to pass to wdentry.
  *
diff --git a/include/nuttx/wqueue.h b/include/nuttx/wqueue.h
index 6155015540828..6eaefff5976c9 100644
--- a/include/nuttx/wqueue.h
+++ b/include/nuttx/wqueue.h
@@ -33,7 +33,7 @@
 #include <stdint.h>
 
 #include <nuttx/clock.h>
-#include <nuttx/queue.h>
+#include <nuttx/list.h>
 #include <nuttx/wdog.h>
 
 /****************************************************************************
@@ -249,19 +249,11 @@ typedef CODE void (*worker_t)(FAR void *arg);
 
 struct work_s
 {
-  union
-  {
-    struct
-    {
-      struct dq_entry_s dq;      /* Implements a double linked list */
-      clock_t qtime;             /* Time work queued */
-    } s;
-    struct wdog_s timer;         /* Delay expiry timer */
-    struct wdog_period_s ptimer; /* Period expiry timer */
-  } u;
-  worker_t  worker;              /* Work callback */
-  FAR void *arg;                 /* Callback argument */
-  FAR struct kwork_wqueue_s *wq; /* Work queue */
+  struct list_node node;   /* Implements a double linked list */
+  clock_t          qtime;  /* Time work queued */
+  clock_t          period; /* Periodical delay ticks */
+  worker_t         worker; /* Work callback */
+  FAR void        *arg;    /* Callback argument */
 };
 
 /* This is an enumeration of the various events that may be
@@ -560,11 +552,7 @@ int work_cancel_sync_wq(FAR struct kwork_wqueue_s *wqueue,
  *
  ****************************************************************************/
 
-#ifdef __KERNEL__
-#  define work_timeleft(work) wd_gettime(&((work)->u.timer))
-#else
-#  define work_timeleft(work) ((sclock_t)((work)->u.s.qtime - clock()))
-#endif
+#define work_timeleft(work) ((sclock_t)((work)->qtime - clock()))
 
 /****************************************************************************
  * Name: lpwork_boostpriority
diff --git a/libs/libc/wqueue/work_cancel.c b/libs/libc/wqueue/work_cancel.c
index 4a86335c2e8a1..7d4188e974fcb 100644
--- a/libs/libc/wqueue/work_cancel.c
+++ b/libs/libc/wqueue/work_cancel.c
@@ -65,8 +65,6 @@
 static int work_qcancel(FAR struct usr_wqueue_s *wqueue,
                         FAR struct work_s *work)
 {
-  FAR dq_entry_t *prev = NULL;
-  FAR dq_entry_t *curr;
   int ret = -ENOENT;
   int semcount;
 
@@ -83,37 +81,16 @@ static int work_qcancel(FAR struct usr_wqueue_s *wqueue,
 
   if (work->worker != NULL)
     {
-      /* Search the work activelist for the target work. We can't
-       * use dq_rem to do this because there are additional operations that
-       * need to be done.
-       */
-
-      curr = wqueue->q.head;
-      while (curr && curr != &work->u.s.dq)
-        {
-          prev = curr;
-          curr = curr->flink;
-        }
-
-      /* Check if the work was found in the list.  If not, then an OS
-       * error has occurred because the work is marked active!
-       */
-
-      DEBUGASSERT(curr);
+      bool is_head = list_is_head(&wqueue->q, &work->node);
 
       /* Now, remove the work from the work queue */
 
-      if (prev)
-        {
-          /* Remove the work from mid- or end-of-queue */
+      list_delete(&work->node);
 
-          dq_remafter(prev, &wqueue->q);
-        }
-      else
+      if (is_head)
         {
           /* Remove the work at the head of the queue */
 
-          dq_remfirst(&wqueue->q);
           nxsem_get_value(&wqueue->wake, &semcount);
           if (semcount < 1)
             {
diff --git a/libs/libc/wqueue/work_queue.c b/libs/libc/wqueue/work_queue.c
index 0a00bf6f2e010..c80e02e677e0a 100644
--- a/libs/libc/wqueue/work_queue.c
+++ b/libs/libc/wqueue/work_queue.c
@@ -32,7 +32,7 @@
 #include <errno.h>
 
 #include <nuttx/clock.h>
-#include <nuttx/queue.h>
+#include <nuttx/list.h>
 #include <nuttx/wqueue.h>
 
 #include "wqueue/wqueue.h"
@@ -76,9 +76,8 @@ static int work_qqueue(FAR struct usr_wqueue_s *wqueue,
                        FAR struct work_s *work, worker_t worker,
                        FAR void *arg, clock_t delay)
 {
-  FAR dq_entry_t *prev = NULL;
-  FAR dq_entry_t *curr;
-  sclock_t delta;
+  FAR struct work_s *curr;
+  FAR struct work_s *head;
   int semcount;
 
   /* Get exclusive access to the work queue */
@@ -87,59 +86,51 @@ static int work_qqueue(FAR struct usr_wqueue_s *wqueue,
 
   /* Initialize the work structure */
 
-  work->worker = worker;             /* Work callback. non-NULL means queued */
-  work->arg    = arg;                /* Callback argument */
-  work->u.s.qtime = clock() + delay; /* Delay until work performed */
+  work->worker = worker;          /* Work callback. non-NULL means queued */
+  work->arg    = arg;             /* Callback argument */
+  work->qtime  = clock() + delay; /* Delay until work performed */
 
-  /* Do the easy case first -- when the work queue is empty. */
+  /* delay+1 is to prevent the insufficient sleep time if we are
+   * currently near the boundary to the next tick.
+   * | current_tick | current_tick + 1 | current_tick + 2 | .... |
+   * |           ^ Here we get the current tick
+   * In this case we delay 1 tick, timer will be triggered at
+   * current_tick + 1, which is not enough for at least 1 tick.
+   */
 
-  if (wqueue->q.head == NULL)
-    {
-      /* Add the watchdog to the head == tail of the queue. */
+  work->qtime += 1;
 
-      dq_addfirst(&work->u.s.dq, &wqueue->q);
-      nxsem_post(&wqueue->wake);
-    }
+  /* Insert the work into the wait queue sorted by the expired time. */
 
-  /* There are other active watchdogs in the timer queue */
+  head = list_first_entry(&wqueue->q, struct work_s, node);
 
-  else
+  list_for_every_entry(&wqueue->q, curr, struct work_s, node)
     {
-      curr = wqueue->q.head;
-
-      /* Check if the new work must be inserted before the curr. */
-
-      do
+      if (!clock_compare(curr->qtime, work->qtime))
         {
-          delta = work->u.s.qtime - ((FAR struct work_s *)curr)->u.s.qtime;
-          if (delta < 0)
-            {
-              break;
-            }
-
-          prev = curr;
-          curr = curr->flink;
+          break;
         }
-      while (curr != NULL);
+    }
 
-      /* Insert the new watchdog in the list */
+  /* After the insertion, we do not violate the invariant that
+   * the wait queue is sorted by the expired time. Because
+   * curr->qtime > work->qtime.
+   * In the case of the wqueue is empty, we insert
+   * the work at the head of the wait queue.
+   */
 
-      if (prev == NULL)
-        {
-          /* Insert the watchdog at the head of the list */
-
-          dq_addfirst(&work->u.s.dq, &wqueue->q);
-          nxsem_get_value(&wqueue->wake, &semcount);
-          if (semcount < 1)
-            {
-              nxsem_post(&wqueue->wake);
-            }
-        }
-      else
-        {
-          /* Insert the watchdog in mid- or end-of-queue */
+  list_add_before(&curr->node, &work->node);
 
-          dq_addafter(prev, &work->u.s.dq, &wqueue->q);
+  /* If the current work is the head of the wait queue.
+   * We should wake up the worker thread.
+   */
+
+  if (curr == head)
+    {
+      nxsem_get_value(&wqueue->wake, &semcount);
+      if (semcount < 1)
+        {
+          nxsem_post(&wqueue->wake);
         }
     }
 
diff --git a/libs/libc/wqueue/work_usrthread.c b/libs/libc/wqueue/work_usrthread.c
index 8e4c679217d37..e52529c2452f1 100644
--- a/libs/libc/wqueue/work_usrthread.c
+++ b/libs/libc/wqueue/work_usrthread.c
@@ -34,7 +34,7 @@
 #include <assert.h>
 
 #include <nuttx/clock.h>
-#include <nuttx/queue.h>
+#include <nuttx/list.h>
 #include <nuttx/wqueue.h>
 
 #include "wqueue/wqueue.h"
@@ -63,7 +63,7 @@
 
 struct usr_wqueue_s g_usrwork =
 {
-  {NULL, NULL},
+  LIST_INITIAL_VALUE(g_usrwork.q),
   NXMUTEX_INITIALIZER,
   SEM_INITIALIZER(0),
 };
@@ -91,10 +91,10 @@ struct usr_wqueue_s g_usrwork =
 
 static void work_process(FAR struct usr_wqueue_s *wqueue)
 {
-  volatile FAR struct work_s *work;
+  FAR struct work_s *work;
   worker_t worker;
   FAR void *arg;
-  sclock_t elapsed;
+  clock_t tick;
   clock_t next;
   int ret;
 
@@ -116,27 +116,28 @@ static void work_process(FAR struct usr_wqueue_s *wqueue)
    * so ourselves, and (2) there will be no changes to the work queue
    */
 
-  work = (FAR struct work_s *)wqueue->q.head;
-  while (work)
+  while (!list_is_empty(&wqueue->q))
     {
+      work = list_first_entry(&wqueue->q, struct work_s, node);
+
       /* Is this work ready? It is ready if there is no delay or if
        * the delay has elapsed.  is the time that the work was added
        * to the work queue. Therefore a delay of equal or less than
        * zero will always execute immediately.
        */
 
-      elapsed = clock() - work->u.s.qtime;
+      tick = clock();
 
       /* Is this delay work ready? */
 
-      if (elapsed >= 0)
+      if (clock_compare(work->qtime, tick))
         {
           /* Remove the ready-to-execute work from the list */
 
-          dq_remfirst(&wqueue->q);
+          list_delete(&work->node);
 
           /* Extract the work description from the entry (in case the work
-           * instance by the re-used after it has been de-queued).
+           * instance by the reused after it has been de-queued).
            */
 
           worker = work->worker;
@@ -175,12 +176,10 @@ static void work_process(FAR struct usr_wqueue_s *wqueue)
                   return;
                 }
             }
-
-          work = (FAR struct work_s *)wqueue->q.head;
         }
       else
         {
-          next = work->u.s.qtime - clock();
+          next = work->qtime - clock();
           break;
         }
     }
@@ -287,7 +286,7 @@ int work_usrstart(void)
 
   /* Initialize the work queue */
 
-  dq_init(&g_usrwork.q);
+  list_initialize(&g_usrwork.q);
 
 #ifdef CONFIG_BUILD_PROTECTED
 
diff --git a/libs/libc/wqueue/wqueue.h b/libs/libc/wqueue/wqueue.h
index c262693b1f0f7..eab017c6125b9 100644
--- a/libs/libc/wqueue/wqueue.h
+++ b/libs/libc/wqueue/wqueue.h
@@ -33,6 +33,7 @@
 
 #include <nuttx/mutex.h>
 #include <nuttx/semaphore.h>
+#include <nuttx/list.h>
 #include <nuttx/wqueue.h>
 
 #if defined(CONFIG_LIBC_USRWORK) && !defined(__KERNEL__)
@@ -49,9 +50,9 @@
 
 struct usr_wqueue_s
 {
-  struct dq_queue_s q;      /* The queue of pending work */
-  mutex_t           lock;   /* exclusive access to user-mode work queue */
-  sem_t             wake;   /* The wake-up semaphore of the  usrthread */
+  struct list_node q;    /* The queue of pending work */
+  mutex_t          lock; /* exclusive access to user-mode work queue */
+  sem_t            wake; /* The wake-up semaphore of the  usrthread */
 };
 
 /****************************************************************************
diff --git a/sched/wdog/wd_start.c b/sched/wdog/wd_start.c
index c03c7b06fe158..3d84dd2d3edcc 100644
--- a/sched/wdog/wd_start.c
+++ b/sched/wdog/wd_start.c
@@ -226,18 +226,21 @@ static inline_function void wd_expiration(clock_t ticks)
  *   wdog and wdentry is not NULL.
  *
  * Returned Value:
- *   None.
+ *   Whether the head of the watchdog list has changed.
  *
  ****************************************************************************/
 
 static inline_function
-void wd_insert(FAR struct wdog_s *wdog, clock_t expired,
+bool wd_insert(FAR struct wdog_s *wdog, clock_t expired,
                wdentry_t wdentry, wdparm_t arg)
 {
   FAR struct wdog_s *curr;
+  FAR struct wdog_s *head;
 
   /* Traverse the watchdog list */
 
+  head = list_first_entry(&g_wdactivelist, struct wdog_s, node);
+
   list_for_every_entry(&g_wdactivelist, curr, struct wdog_s, node)
     {
       /* Until curr->expired has not timed out relative to expired */
@@ -261,6 +264,10 @@ void wd_insert(FAR struct wdog_s *wdog, clock_t expired,
   up_getpicbase(&wdog->picbase);
   wdog->arg = arg;
   wdog->expired = expired;
+
+  /* Return whether the head of the watchdog list has changed. */
+
+  return head == curr;
 }
 
 /****************************************************************************
@@ -287,7 +294,7 @@ void wd_insert(FAR struct wdog_s *wdog, clock_t expired,
  *
  * Input Parameters:
  *   wdog     - Watchdog ID
- *   ticks    - Absoulute time in clock ticks
+ *   ticks    - Absolute time in clock ticks
  *   wdentry  - Function to call on timeout
  *   arg      - Parameter to pass to wdentry.
  *
@@ -349,10 +356,9 @@ int wd_start_abstick(FAR struct wdog_s *wdog, clock_t ticks,
       wdog->func = NULL;
     }
 
-  wd_insert(wdog, ticks, wdentry, arg);
+  reassess |= wd_insert(wdog, ticks, wdentry, arg);
 
-  if (!g_wdtimernested &&
-      (reassess || list_is_head(&g_wdactivelist, &wdog->node)))
+  if (!g_wdtimernested && reassess)
     {
       /* Resume the interval timer that will generate the next
        * interval event. If the timer at the head of the list changed,
diff --git a/sched/wqueue/kwork_cancel.c b/sched/wqueue/kwork_cancel.c
index 89bec581bdaea..369730d6a0796 100644
--- a/sched/wqueue/kwork_cancel.c
+++ b/sched/wqueue/kwork_cancel.c
@@ -31,7 +31,7 @@
 
 #include <nuttx/irq.h>
 #include <nuttx/arch.h>
-#include <nuttx/queue.h>
+#include <nuttx/list.h>
 #include <nuttx/wqueue.h>
 
 #include "wqueue/wqueue.h"
@@ -46,7 +46,7 @@ static int work_qcancel(FAR struct kwork_wqueue_s *wqueue, bool sync,
                         FAR struct work_s *work)
 {
   irqstate_t flags;
-  int ret = -ENOENT;
+  int        ret = OK;
 
   if (wqueue == NULL || work == NULL)
     {
@@ -59,20 +59,37 @@ static int work_qcancel(FAR struct kwork_wqueue_s *wqueue, bool sync,
    */
 
   flags = spin_lock_irqsave(&wqueue->lock);
-  if (work->worker != NULL)
+
+  /* Check whether we own the work structure. */
+
+  if (!work_available(work))
     {
-      /* Remove the entry from the work queue and make sure that it is
-       * marked as available (i.e., the worker field is nullified).
-       */
+      bool is_head = list_is_head(&wqueue->pending, &work->node);
+
+      /* Seize the ownership from the work thread. */
 
       work->worker = NULL;
-      wd_cancel(&work->u.timer);
-      if (dq_inqueue((FAR dq_entry_t *)work, &wqueue->q))
+
+      list_delete(&work->node);
+
+      /* If the head of the pending queue has changed, we should reset
+       * the wqueue timer.
+       */
+
+      if (is_head)
         {
-          dq_rem((FAR dq_entry_t *)work, &wqueue->q);
-        }
+          if (!list_is_empty(&wqueue->pending))
+            {
+              work = list_first_entry(&wqueue->pending, struct work_s, node);
 
-      ret = OK;
+              ret = wd_start_abstick(&wqueue->timer, work->qtime,
+                                     work_timer_expired, (wdparm_t)wqueue);
+            }
+          else
+            {
+              wd_cancel(&wqueue->timer);
+            }
+        }
     }
   else if (!up_interrupt_context() && !sched_idletask() && sync)
     {
diff --git a/sched/wqueue/kwork_queue.c b/sched/wqueue/kwork_queue.c
index 7af8e9800bae6..57f893cb672e8 100644
--- a/sched/wqueue/kwork_queue.c
+++ b/sched/wqueue/kwork_queue.c
@@ -33,74 +33,13 @@
 #include <nuttx/irq.h>
 #include <nuttx/arch.h>
 #include <nuttx/clock.h>
-#include <nuttx/queue.h>
+#include <nuttx/list.h>
 #include <nuttx/wqueue.h>
 
 #include "wqueue/wqueue.h"
 
 #ifdef CONFIG_SCHED_WORKQUEUE
 
-/****************************************************************************
- * Pre-processor Definitions
- ****************************************************************************/
-
-#define queue_work(wqueue, work) \
-  do \
-    { \
-      dq_addlast((FAR dq_entry_t *)(work), &(wqueue)->q); \
-      if ((wqueue)->wait_count > 0) /* There are threads waiting for sem. */ \
-        { \
-          (wqueue)->wait_count--; \
-          nxsem_post(&(wqueue)->sem); \
-        } \
-    } \
-  while (0)
-
-/****************************************************************************
- * Private Functions
- ****************************************************************************/
-
-/****************************************************************************
- * Name: work_timer_expiry
- ****************************************************************************/
-
-static void work_timer_expiry(wdparm_t arg)
-{
-  FAR struct work_s *work = (FAR struct work_s *)arg;
-
-  irqstate_t flags = spin_lock_irqsave(&work->wq->lock);
-  sched_lock();
-
-  /* We have being canceled */
-
-  if (work->worker != NULL)
-    {
-      queue_work(work->wq, work);
-    }
-
-  spin_unlock_irqrestore(&work->wq->lock, flags);
-  sched_unlock();
-}
-
-static bool work_is_canceling(FAR struct kworker_s *kworkers, int nthreads,
-                              FAR struct work_s *work)
-{
-  int wndx;
-
-  for (wndx = 0; wndx < nthreads; wndx++)
-    {
-      if (kworkers[wndx].work == work)
-        {
-          if (kworkers[wndx].wait_count > 0)
-            {
-              return true;
-            }
-        }
-    }
-
-  return false;
-}
-
 /****************************************************************************
  * Public Functions
  ****************************************************************************/
@@ -141,67 +80,67 @@ int work_queue_period_wq(FAR struct kwork_wqueue_s *wqueue,
                          FAR void *arg, clock_t delay, clock_t period)
 {
   irqstate_t flags;
-  int ret = OK;
+  clock_t    expected;
+  bool wake = false;
+  int  ret  = OK;
 
   if (wqueue == NULL || work == NULL || worker == NULL)
     {
       return -EINVAL;
     }
 
+  /* Ensure the work has been canceled. */
+
+  work_cancel_wq(wqueue, work);
+
+  /* delay+1 is to prevent the insufficient sleep time if we are
+   * currently near the boundary to the next tick.
+   * | current_tick | current_tick + 1 | current_tick + 2 | .... |
+   * |           ^ Here we get the current tick
+   * In this case we delay 1 tick, timer will be triggered at
+   * current_tick + 1, which is not enough for at least 1 tick.
+   */
+
+  expected = clock_systime_ticks() + delay + 1;
+
   /* Interrupts are disabled so that this logic can be called from with
    * task logic or from interrupt handling logic.
    */
 
   flags = spin_lock_irqsave(&wqueue->lock);
-  sched_lock();
 
-  /* Remove the entry from the timer and work queue. */
+  /* Initialize the work structure. */
 
-  if (work->worker != NULL)
-    {
-      /* Remove the entry from the work queue and make sure that it is
-       * marked as available (i.e., the worker field is nullified).
-       */
+  work->worker = worker;   /* Work callback. non-NULL means queued */
+  work->arg    = arg;      /* Callback argument */
+  work->qtime  = expected; /* Expected time */
+  work->period = period;   /* Periodical delay */
 
-      work->worker = NULL;
-      wd_cancel(&work->u.timer);
-      if (dq_inqueue((FAR dq_entry_t *)work, &wqueue->q))
+  /* Insert to the pending list of the wqueue. */
+
+  if (delay)
+    {
+      if (work_insert_pending(wqueue, work))
         {
-          dq_rem((FAR dq_entry_t *)work, &wqueue->q);
+          /* Start the timer if the work is the earliest expired work. */
+
+          ret = wd_start_abstick(&wqueue->timer, work->qtime,
+                                 work_timer_expired, (wdparm_t)wqueue);
         }
     }
-
-  if (work_is_canceling(wqueue->worker, wqueue->nthreads, work))
+  else
     {
-      goto out;
+      list_add_tail(&wqueue->expired, &work->node);
+      wake = true;
     }
 
-  /* Initialize the work structure. */
-
-  work->worker = worker;           /* Work callback. non-NULL means queued */
-  work->arg    = arg;              /* Callback argument */
-  work->wq     = wqueue;           /* Work queue */
-
-  /* Queue the new work */
+  spin_unlock_irqrestore(&wqueue->lock, flags);
 
-  if (!delay)
+  if (wake)
     {
-      queue_work(wqueue, work);
-    }
-  else if (period == 0)
-    {
-      ret = wd_start(&work->u.timer, delay,
-                     work_timer_expiry, (wdparm_t)work);
-    }
-  else
-    {
-      ret = wd_start_period(&work->u.ptimer, delay, period,
-                            work_timer_expiry, (wdparm_t)work);
+      nxsem_post(&wqueue->sem);
     }
 
-out:
-  spin_unlock_irqrestore(&wqueue->lock, flags);
-  sched_unlock();
   return ret;
 }
 
diff --git a/sched/wqueue/kwork_thread.c b/sched/wqueue/kwork_thread.c
index 19585f0b28d5d..be1a77185ea12 100644
--- a/sched/wqueue/kwork_thread.c
+++ b/sched/wqueue/kwork_thread.c
@@ -35,7 +35,7 @@
 #include <assert.h>
 #include <debug.h>
 
-#include <nuttx/queue.h>
+#include <nuttx/list.h>
 #include <nuttx/wqueue.h>
 #include <nuttx/kthread.h>
 #include <nuttx/semaphore.h>
@@ -83,11 +83,14 @@
 
 struct hp_wqueue_s g_hpwork =
 {
-  {NULL, NULL},
-  SEM_INITIALIZER(0),
-  SEM_INITIALIZER(0),
-  SP_UNLOCKED,
-  CONFIG_SCHED_HPNTHREADS,
+  {
+    LIST_INITIAL_VALUE(g_hpwork.wq.expired),
+    LIST_INITIAL_VALUE(g_hpwork.wq.pending),
+    SEM_INITIALIZER(0),
+    SEM_INITIALIZER(0),
+    SP_UNLOCKED,
+    CONFIG_SCHED_HPNTHREADS,
+  }
 };
 
 #endif /* CONFIG_SCHED_HPWORK */
@@ -97,11 +100,14 @@ struct hp_wqueue_s g_hpwork =
 
 struct lp_wqueue_s g_lpwork =
 {
-  {NULL, NULL},
-  SEM_INITIALIZER(0),
-  SEM_INITIALIZER(0),
-  SP_UNLOCKED,
-  CONFIG_SCHED_LPNTHREADS,
+  {
+    LIST_INITIAL_VALUE(g_lpwork.wq.expired),
+    LIST_INITIAL_VALUE(g_lpwork.wq.pending),
+    SEM_INITIALIZER(0),
+    SEM_INITIALIZER(0),
+    SP_UNLOCKED,
+    CONFIG_SCHED_LPNTHREADS,
+  }
 };
 
 #endif /* CONFIG_SCHED_LPWORK */
@@ -110,6 +116,51 @@ struct lp_wqueue_s g_lpwork =
  * Private Functions
  ****************************************************************************/
 
+static inline_function
+void work_dispatch(FAR struct kwork_wqueue_s *wq)
+{
+  FAR struct work_s *work;
+  FAR struct work_s *next;
+  unsigned int count = 0;
+  clock_t      ticks = clock_systime_ticks();
+
+  /* Wake up the worker thread once there is expired work.
+   * If some worker threads are busy, here the callback will
+   * wake up another waiting work thread.
+   *
+   * Becareful of the special case that the pending work
+   * has been canceled but the timer is expired.
+   * In this case we should not wake up any worker thread.
+   */
+
+  list_for_every_entry_safe(&wq->pending, work, next, struct work_s, node)
+    {
+      /* Check whether the work has expired. */
+
+      if (!clock_compare(work->qtime, ticks))
+        {
+          wd_start_abstick(&wq->timer, work->qtime,
+                           work_timer_expired, (wdparm_t)wq);
+          break;
+        }
+
+      /* Expired work will be moved to tail of the expired queue. */
+
+      list_delete(&work->node);
+      list_add_tail(&wq->expired, &work->node);
+
+      /* Note that the thread execution this function is also
+       * a worker thread, which has already been woken up by the timer.
+       * So only `count - 1` semaphore will be posted.
+       */
+
+      if (count++ > 0)
+        {
+          nxsem_post(&wq->sem);
+        }
+    }
+}
+
 /****************************************************************************
  * Name: work_thread
  *
@@ -135,11 +186,11 @@ struct lp_wqueue_s g_lpwork =
 static int work_thread(int argc, FAR char *argv[])
 {
   FAR struct kwork_wqueue_s *wqueue;
-  FAR struct kworker_s *kworker;
-  FAR struct work_s *work;
-  worker_t worker;
-  irqstate_t flags;
-  FAR void *arg;
+  FAR struct kworker_s      *kworker;
+  FAR struct work_s         *work;
+  worker_t      worker;
+  irqstate_t    flags;
+  FAR void     *arg;
 
   /* Get the handle from argv */
 
@@ -148,29 +199,38 @@ static int work_thread(int argc, FAR char *argv[])
   kworker = (FAR struct kworker_s *)
             ((uintptr_t)strtoul(argv[2], NULL, 16));
 
-  flags = spin_lock_irqsave(&wqueue->lock);
-  sched_lock();
-
-  /* Loop forever */
+  /* Loop until wqueue->exit != 0.
+   * Since the only way to set wqueue->exit is to call work_queue_free(),
+   * there is no need for entering the critical section.
+   */
 
   while (!wqueue->exit)
     {
-      /* And check each entry in the work queue.  Since we have disabled
+      /* And check first entry in the work queue. Since we have disabled
        * interrupts we know:  (1) we will not be suspended unless we do
        * so ourselves, and (2) there will be no changes to the work queue
        */
 
-      /* Remove the ready-to-execute work from the list */
+      flags = spin_lock_irqsave(&wqueue->lock);
+      sched_lock();
+
+      /* If the wqueue timer is expired and non-active, it indicates that
+       * there might be expired work in the pending queue.
+       */
 
-      while ((work = (FAR struct work_s *)dq_remfirst(&wqueue->q)) != NULL)
+      if (!WDOG_ISACTIVE(&wqueue->timer))
         {
-          if (work->worker == NULL)
-            {
-              continue;
-            }
+          work_dispatch(wqueue);
+        }
+
+      if (!list_is_empty(&wqueue->expired))
+        {
+          work = list_first_entry(&wqueue->expired, struct work_s, node);
+
+          list_delete(&work->node);
 
-          /* Extract the work description from the entry (in case the work
-           * instance will be re-used after it has been de-queued).
+          /* Extract the work description from the entry (in case the
+           * work instance will be reused after it has been de-queued).
            */
 
           worker = work->worker;
@@ -179,21 +239,42 @@ static int work_thread(int argc, FAR char *argv[])
 
           arg = work->arg;
 
-          /* Mark the work as no longer being queued */
+          /* Check whether the work is periodic. */
+
+          if (work->period != 0)
+            {
+              /* Calculate next expiration qtime. */
+
+              work->qtime += work->period;
+
+              /* Enqueue to the waiting queue */
+
+              if (work_insert_pending(wqueue, work))
+                {
+                  /* We should reset timer if the work is the earliest. */
 
-          work->worker = NULL;
+                  wd_start_abstick(&wqueue->timer, work->qtime,
+                                   work_timer_expired, (wdparm_t)wqueue);
+                }
+            }
+          else
+            {
+              /* Return the work structure ownership to the work owner. */
+
+              work->worker = NULL;
+            }
 
           /* Mark the thread busy */
 
           kworker->work = work;
 
+          spin_unlock_irqrestore(&wqueue->lock, flags);
+          sched_unlock();
+
           /* Do the work.  Re-enable interrupts while the work is being
            * performed... we don't have any idea how long this will take!
            */
 
-          spin_unlock_irqrestore(&wqueue->lock, flags);
-          sched_unlock();
-
           CALL_WORKER(worker, arg);
           flags = spin_lock_irqsave(&wqueue->lock);
           sched_lock();
@@ -211,23 +292,14 @@ static int work_thread(int argc, FAR char *argv[])
             }
         }
 
-      /* Then process queued work.  work_process will not return until: (1)
-       * there is no further work in the work queue, and (2) semaphore is
-       * posted.
-       */
-
-      wqueue->wait_count++;
       spin_unlock_irqrestore(&wqueue->lock, flags);
       sched_unlock();
 
+      /* Wait for the semaphore to be posted by the wqueue timer. */
+
       nxsem_wait_uninterruptible(&wqueue->sem);
-      flags = spin_lock_irqsave(&wqueue->lock);
-      sched_lock();
     }
 
-  spin_unlock_irqrestore(&wqueue->lock, flags);
-  sched_unlock();
-
   nxsem_post(&wqueue->exsem);
   return OK;
 }
@@ -299,6 +371,27 @@ static int work_thread_create(FAR const char *name, int priority,
  * Public Functions
  ****************************************************************************/
 
+/****************************************************************************
+ * Name: work_timer_expired
+ *
+ * Description:
+ *   The wqueue timer callback.
+ *
+ * Input Parameters:
+ *   arg  - The work queue.
+ *
+ ****************************************************************************/
+
+void work_timer_expired(wdparm_t arg)
+{
+  /* The work time expired callback will wake up at least one worker thread
+   * to dispatch the expired work.
+   */
+
+  FAR struct kwork_wqueue_s *wq = (FAR struct kwork_wqueue_s *)arg;
+  nxsem_post(&wq->sem);
+}
+
 /****************************************************************************
  * Name: work_queue_create
  *
@@ -345,7 +438,9 @@ FAR struct kwork_wqueue_s *work_queue_create(FAR const char *name,
 
   /* Initialize the work queue structure */
 
-  dq_init(&wqueue->q);
+  list_initialize(&wqueue->expired);
+  list_initialize(&wqueue->pending);
+  wqueue->timer.func = NULL;
   nxsem_init(&wqueue->sem, 0, 0);
   nxsem_init(&wqueue->exsem, 0, 0);
   wqueue->nthreads = nthreads;
@@ -388,6 +483,8 @@ int work_queue_free(FAR struct kwork_wqueue_s *wqueue)
       return -EINVAL;
     }
 
+  wd_cancel(&wqueue->timer);
+
   /* Mark the work queue as exiting */
 
   wqueue->exit = true;
diff --git a/sched/wqueue/wqueue.h b/sched/wqueue/wqueue.h
index 7d36cf0d9bbf5..c6ecb95883f6a 100644
--- a/sched/wqueue/wqueue.h
+++ b/sched/wqueue/wqueue.h
@@ -33,7 +33,7 @@
 #include <stdbool.h>
 
 #include <nuttx/clock.h>
-#include <nuttx/queue.h>
+#include <nuttx/list.h>
 #include <nuttx/wqueue.h>
 #include <nuttx/spinlock.h>
 
@@ -66,14 +66,15 @@ struct kworker_s
 
 struct kwork_wqueue_s
 {
-  struct dq_queue_s q;         /* The queue of pending work */
-  sem_t             sem;       /* The counting semaphore of the wqueue */
-  sem_t             exsem;     /* Sync waiting for thread exit */
-  spinlock_t        lock;      /* Spinlock */
-  uint8_t           nthreads;  /* Number of worker threads */
-  bool              exit;      /* A flag to request the thread to exit */
-  int16_t           wait_count;
-  struct kworker_s  worker[0]; /* Describes a worker thread */
+  struct list_node expired;   /* The queue of expired work. */
+  struct list_node pending;   /* The queue of pending work. */
+  sem_t            sem;       /* The counting semaphore of the wqueue */
+  sem_t            exsem;     /* Sync waiting for thread exit */
+  spinlock_t       lock;      /* Spinlock */
+  uint8_t          nthreads;  /* Number of worker threads */
+  bool             exit;      /* A flag to request the thread to exit */
+  struct wdog_s    timer;     /* Timer to pending. */
+  struct kworker_s worker[0]; /* Describes a worker thread */
 };
 
 /* This structure defines the state of one high-priority work queue.  This
@@ -83,17 +84,11 @@ struct kwork_wqueue_s
 #ifdef CONFIG_SCHED_HPWORK
 struct hp_wqueue_s
 {
-  struct dq_queue_s q;         /* The queue of pending work */
-  sem_t             sem;       /* The counting semaphore of the wqueue */
-  sem_t             exsem;     /* Sync waiting for thread exit */
-  spinlock_t        lock;      /* Spinlock */
-  uint8_t           nthreads;  /* Number of worker threads */
-  bool              exit;      /* A flag to request the thread to exit */
-  int16_t           wait_count;
+  struct kwork_wqueue_s wq;
 
   /* Describes each thread in the high priority queue's thread pool */
 
-  struct kworker_s  worker[CONFIG_SCHED_HPNTHREADS];
+  struct kworker_s      worker[CONFIG_SCHED_HPNTHREADS];
 };
 #endif
 
@@ -104,17 +99,11 @@ struct hp_wqueue_s
 #ifdef CONFIG_SCHED_LPWORK
 struct lp_wqueue_s
 {
-  struct dq_queue_s q;         /* The queue of pending work */
-  sem_t             sem;       /* The counting semaphore of the wqueue */
-  sem_t             exsem;     /* Sync waiting for thread exit */
-  spinlock_t        lock;      /* Spinlock */
-  uint8_t           nthreads;  /* Number of worker threads */
-  bool              exit;      /* A flag to request the thread to exit */
-  int16_t           wait_count;
+  struct kwork_wqueue_s wq;
 
   /* Describes each thread in the low priority queue's thread pool */
 
-  struct kworker_s  worker[CONFIG_SCHED_LPNTHREADS];
+  struct kworker_s      worker[CONFIG_SCHED_LPNTHREADS];
 };
 #endif
 
@@ -159,6 +148,74 @@ static inline_function FAR struct kwork_wqueue_s *work_qid2wq(int qid)
     }
 }
 
+/****************************************************************************
+ * Name: work_insert_pending
+ *
+ * Description:
+ *   Internal public function to insert the work to the workqueue.
+ *   Require wqueue != NULL and work != NULL.
+ *
+ * Input Parameters:
+ *   wqueue - The work queue.
+ *   work   - The work to be inserted.
+ *
+ * Returned Value:
+ *   Return whether the work is inserted at the head of the pending queue.
+ *
+ ****************************************************************************/
+
+static inline_function
+bool work_insert_pending(FAR struct kwork_wqueue_s *wqueue,
+                         FAR struct work_s         *work)
+{
+  FAR struct work_s *curr;
+  FAR struct work_s *head;
+
+  DEBUGASSERT(wqueue != NULL && work != NULL);
+
+  /* Insert the work into the wait queue sorted by the expired time. */
+
+  head = list_first_entry(&wqueue->pending, struct work_s, node);
+
+  list_for_every_entry(&wqueue->pending, curr, struct work_s, node)
+    {
+      if (!clock_compare(curr->qtime, work->qtime))
+        {
+          break;
+        }
+    }
+
+  /* After the insertion, we do not violate the invariant that
+   * the wait queue is sorted by the expired time. Because
+   * curr->qtime > work->qtime.
+   * In the case of the wqueue is empty, we insert
+   * the work at the head of the wait queue.
+   */
+
+  list_add_before(&curr->node, &work->node);
+
+  /* Return list_is_head(&wqueue->pending, &work->node)
+   * However, there is fast path that we can check if `curr`
+   * is the `head` we cached before, which is cache-friendly and
+   * can reduce one memory access.
+   */
+
+  return curr == head;
+}
+
+/****************************************************************************
+ * Name: work_timer_expired
+ *
+ * Description:
+ *   The wqueue timer callback.
+ *
+ * Input Parameters:
+ *   arg  - The work queue.
+ *
+ ****************************************************************************/
+
+void work_timer_expired(wdparm_t arg);
+
 /****************************************************************************
  * Name: work_start_highpri
  *


Reviews:
user: login: jerpelea
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-17T12:15:56Z
user: login: anchao
body: new work queue implementation may be affected by the number of works and the CET of the work callback. I think we may need to do more testing in the worker thread, especially with high CPU loading.
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-17T15:20:26Z
user: login: xiaoxiang781216
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-18T02:01:28Z
user: login: anchao
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-18T02:35:59Z
user: login: anchao
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-18T02:41:12Z
user: login: Fix-Point
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-18T03:02:04Z
user: login: Fix-Point
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-18T03:02:09Z
user: login: Fix-Point
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-18T03:02:18Z
user: login: Fix-Point
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-18T03:04:22Z
user: login: Fix-Point
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-18T03:04:27Z
user: login: Fix-Point
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-18T03:04:42Z
user: login: Fix-Point
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-18T03:04:58Z
user: login: Fix-Point
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-18T03:05:03Z
user: login: Fix-Point
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-18T03:17:41Z
user: login: Fix-Point
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-18T03:18:13Z
user: login: Fix-Point
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-18T03:18:18Z
user: login: Fix-Point
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-18T03:18:22Z
user: login: Fix-Point
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-18T03:19:02Z
user: login: Fix-Point
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-18T03:20:24Z
user: login: Fix-Point
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-18T03:20:36Z
user: login: Fix-Point
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-18T03:26:42Z
user: login: Fix-Point
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-18T03:32:04Z
user: login: xiaoxiang781216
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-18T04:23:16Z
user: login: xiaoxiang781216
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-18T04:24:58Z
user: login: Fix-Point
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-18T06:08:51Z
user: login: Fix-Point
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-18T06:08:53Z
user: login: xiaoxiang781216
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-18T08:31:41Z
user: login: xiaoxiang781216
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-18T08:32:41Z
user: login: xiaoxiang781216
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-18T08:35:32Z
user: login: Fix-Point
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-18T08:44:11Z
user: login: Fix-Point
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-18T08:44:19Z
user: login: Fix-Point
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-18T08:45:33Z
user: login: Fix-Point
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-18T08:45:40Z
user: login: Fix-Point
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-18T08:46:52Z
user: login: xiaoxiang781216
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-18T13:09:39Z
user: login: Fix-Point
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-19T01:53:05Z
user: login: Fix-Point
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-19T01:53:08Z
user: login: Fix-Point
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-19T01:54:36Z
user: login: Fix-Point
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-19T01:55:10Z
user: login: Fix-Point
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-19T02:20:07Z
user: login: Fix-Point
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-19T02:21:12Z
user: login: Fix-Point
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-19T02:21:13Z
user: login: xiaoxiang781216
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-19T08:12:13Z
user: login: anchao
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-20T08:52:15Z
user: login: Fix-Point
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-21T02:14:07Z
user: login: Fix-Point
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-21T02:14:14Z
user: login: Fix-Point
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-21T02:15:21Z
user: login: GUIDINGLI
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T09:20:26Z
user: login: Fix-Point
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-24T08:06:34Z
user: login: GUIDINGLI
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-27T08:42:14Z
user: login: GUIDINGLI
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-28T08:55:26Z
user: login: Fix-Point
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-29T03:29:48Z
user: login: jerpelea
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-02T12:04:07Z
user: login: acassis
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-02T12:29:47Z
user: login: xiaoxiang781216
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-05T08:24:10Z
user: login: Fix-Point
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-06T02:17:47Z
user: login: Fix-Point
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-06T02:17:51Z
user: login: Fix-Point
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-06T02:17:58Z
user: login: Fix-Point
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-06T02:18:01Z
user: login: Fix-Point
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-06T02:19:36Z
user: login: Fix-Point
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-06T02:19:43Z
user: login: Fix-Point
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-06T02:19:48Z
user: login: Fix-Point
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-06T02:27:07Z
user: login: Fix-Point
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-06T02:27:19Z
user: login: Fix-Point
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-06T02:27:25Z
user: login: xiaoxiang781216
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-06T03:13:43Z
user: login: Fix-Point
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-06T03:19:25Z
user: login: Fix-Point
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-06T03:25:38Z
user: login: Fix-Point
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-06T03:28:49Z
user: login: xiaoxiang781216
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-06T03:36:44Z
user: login: xiaoxiang781216
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-06T03:37:12Z
user: login: xiaoxiang781216
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-06T03:42:52Z
user: login: xiaoxiang781216
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-06T06:17:13Z
user: login: jerpelea
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-06T07:33:32Z
user: login: Fix-Point
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-06T07:51:54Z
user: login: Fix-Point
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-06T07:51:57Z
user: login: Fix-Point
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-06T07:52:00Z
user: login: xiaoxiang781216
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-06T09:07:41Z
user: login: xiaoxiang781216
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-06T09:34:24Z
user: login: xiaoxiang781216
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-06T11:40:27Z
user: login: jerpelea
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-06T12:34:05Z

Review comments:
diff_hunk: @@ -59,38 +59,20 @@ static int work_qcancel(FAR struct kwork_wqueue_s *wqueue, bool sync,
    */
 
   flags = spin_lock_irqsave(&wqueue->lock);
+
+  /* Check whether we own the work structure. */
+
   if (work->worker != NULL)
     {
-      /* Remove the entry from the work queue and make sure that it is
-       * marked as available (i.e., the worker field is nullified).
-       */
+      /* Seize the ownership from the work thread. */
 
       work->worker = NULL;
-      wd_cancel(&work->u.timer);
-      if (dq_inqueue((FAR dq_entry_t *)work, &wqueue->q))
-        {
-          dq_rem((FAR dq_entry_t *)work, &wqueue->q);
-        }
 
-      ret = OK;
-    }
-  else if (!up_interrupt_context() && !sched_idletask() && sync)
-    {
-      int wndx;
-
-      for (wndx = 0; wndx < wqueue->nthreads; wndx++)
-        {
-          if (wqueue->worker[wndx].work == work &&
-              wqueue->worker[wndx].pid != nxsched_gettid())
-            {
-              wqueue->worker[wndx].wait_count++;
-              spin_unlock_irqrestore(&wqueue->lock, flags);
-              nxsem_wait_uninterruptible(&wqueue->worker[wndx].wait);
-              return 1;
-            }
-        }
+      list_delete(&work->node);
     }
 
+  ret = OK;
path: sched/wqueue/kwork_cancel.c
user: login: anchao
body: ```suggestion
```
remove ret

and `work_cancel_sync() ` should be also removed
created_at: 2025-04-17T14:53:48Z
author_association: CONTRIBUTOR
diff_hunk: @@ -155,51 +94,48 @@ int work_queue_period_wq(FAR struct kwork_wqueue_s *wqueue,
   flags = spin_lock_irqsave(&wqueue->lock);
   sched_lock();
 
-  /* Remove the entry from the timer and work queue. */
+  /* Check whether we own the work structure. */
 
   if (work->worker != NULL)
path: sched/wqueue/kwork_queue.c
user: login: anchao
body: work_available
created_at: 2025-04-17T14:56:30Z
author_association: CONTRIBUTOR
diff_hunk: @@ -155,51 +94,48 @@ int work_queue_period_wq(FAR struct kwork_wqueue_s *wqueue,
   flags = spin_lock_irqsave(&wqueue->lock);
   sched_lock();
 
-  /* Remove the entry from the timer and work queue. */
+  /* Check whether we own the work structure. */
 
   if (work->worker != NULL)
     {
-      /* Remove the entry from the work queue and make sure that it is
-       * marked as available (i.e., the worker field is nullified).
-       */
+      /* Seize the ownership from the work thread. */
 
       work->worker = NULL;
-      wd_cancel(&work->u.timer);
-      if (dq_inqueue((FAR dq_entry_t *)work, &wqueue->q))
-        {
-          dq_rem((FAR dq_entry_t *)work, &wqueue->q);
-        }
-    }
 
-  if (work_is_canceling(wqueue->worker, wqueue->nthreads, work))
-    {
-      goto out;
+      list_delete(&work->node);
     }
 
   /* Initialize the work structure. */
 
   work->worker = worker;           /* Work callback. non-NULL means queued */
   work->arg    = arg;              /* Callback argument */
-  work->wq     = wqueue;           /* Work queue */
+  work->period = period;           /* Periodical delay */
 
   /* Queue the new work */
 
   if (!delay)
     {
-      queue_work(wqueue, work);
+      work->qtime = clock_systime_ticks();
+
+      list_add_tail(&wqueue->q, &work->node);
     }
-  else if (period == 0)
+  else
     {
-      ret = wd_start(&work->u.timer, delay,
-                     work_timer_expiry, (wdparm_t)work);
+      /* Insert the work to the workqueue's waiting list. */
+
+      work->qtime = clock_systime_ticks() + delay + 1;
+
+      work_insert_waitq(wqueue, work);
     }
-  else
+
+  /* Wakeup the wqueue thread. */
+
+  if (wqueue->wait_count > 0) /* There are threads waiting for sem. */
     {
-      ret = wd_start_period(&work->u.ptimer, delay, period,
-                            work_timer_expiry, (wdparm_t)work);
+      wqueue->wait_count--;
+      nxsem_post(&wqueue->sem);
     }
 
-out:
   spin_unlock_irqrestore(&wqueue->lock, flags);
   sched_unlock();
   return ret;
path: sched/wqueue/kwork_queue.c
user: login: anchao
body: ```suggestion
  return 0;
```
created_at: 2025-04-17T14:59:25Z
author_association: CONTRIBUTOR
diff_hunk: @@ -159,6 +149,24 @@ static inline_function FAR struct kwork_wqueue_s *work_qid2wq(int qid)
     }
 }
 
+static inline_function
+void work_insert_waitq(FAR struct kwork_wqueue_s *wqueue,
+                       struct work_s *work)
+{
+  struct list_node *curr;
+
+  list_for_every(&wqueue->wait_q, curr)
path: sched/wqueue/wqueue.h
user: login: anchao
body: list_for_every_entry
created_at: 2025-04-17T15:00:58Z
author_association: CONTRIBUTOR
diff_hunk: @@ -110,6 +118,44 @@ struct lp_wqueue_s g_lpwork =
  * Private Functions
  ****************************************************************************/
 
+static inline void work_timer_expired(wdparm_t arg)
path: sched/wqueue/kwork_thread.c
user: login: anchao
body: ```suggestion
static inline_function void work_timer_expired(wdparm_t arg)
```
created_at: 2025-04-17T15:03:03Z
author_association: CONTRIBUTOR
diff_hunk: @@ -110,6 +118,44 @@ struct lp_wqueue_s g_lpwork =
  * Private Functions
  ****************************************************************************/
 
+static inline void work_timer_expired(wdparm_t arg)
+{
+  FAR struct kwork_wqueue_s *wq = (FAR struct kwork_wqueue_s *)arg;
+  irqstate_t flags = spin_lock_irqsave(&wq->lock);
+  sched_lock();
+
+  if (wq->wait_count > 0)
+    {
+      wq->wait_count--;
+      nxsem_post(&wq->sem);
+    }
+
+  spin_unlock_irqrestore(&wq->lock, flags);
+  sched_unlock();
+}
+
+static inline
+void work_expiration(clock_t ticks, FAR struct kwork_wqueue_s *wq)
+{
+  struct work_s *work;
+
+  while (!list_is_empty(&wq->wait_q))
path: sched/wqueue/kwork_thread.c
user: login: anchao
body: list_for_every_entry
created_at: 2025-04-17T15:06:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -181,7 +234,31 @@ static int work_thread(int argc, FAR char *argv[])
 
           /* Mark the work as no longer being queued */
 
-          work->worker = NULL;
+          if (work->period != 0)
+            {
+              /* Insert qtime */
+
+              work->qtime += work->period;
+
+              if (clock_compare(work->qtime, ticks))
path: sched/wqueue/kwork_thread.c
user: login: anchao
body: ticks should be updated again after each polling
created_at: 2025-04-17T15:09:52Z
author_association: CONTRIBUTOR
diff_hunk: @@ -211,6 +288,17 @@ static int work_thread(int argc, FAR char *argv[])
             }
         }
 
+      if (!list_is_empty(&wqueue->wait_q))
path: sched/wqueue/kwork_thread.c
user: login: anchao
body: maybe wait_q have ready worker now
created_at: 2025-04-17T15:11:07Z
author_association: CONTRIBUTOR
diff_hunk: @@ -160,14 +208,19 @@ static int work_thread(int argc, FAR char *argv[])
        * so ourselves, and (2) there will be no changes to the work queue
        */
 
+      ticks = clock_systime_ticks();
+
+      /* Check if there is expired delayed work */
+
+      work_expiration(ticks, wqueue);
+
       /* Remove the ready-to-execute work from the list */
 
-      while ((work = (FAR struct work_s *)dq_remfirst(&wqueue->q)) != NULL)
+      while (!list_is_empty(&wqueue->q))
path: sched/wqueue/kwork_thread.c
user: login: anchao
body: move readytorun queue into stack
created_at: 2025-04-17T15:12:25Z
author_association: CONTRIBUTOR
diff_hunk: @@ -159,6 +149,24 @@ static inline_function FAR struct kwork_wqueue_s *work_qid2wq(int qid)
     }
 }
 
+static inline_function
+void work_insert_waitq(FAR struct kwork_wqueue_s *wqueue,
+                       struct work_s *work)
+{
+  struct list_node *curr;
+
+  list_for_every(&wqueue->wait_q, curr)
+    {
+      struct work_s *curr_work = list_entry(curr, struct work_s, node);
path: sched/wqueue/wqueue.h
user: login: anchao
body: ```suggestion
      FAR struct work_s *curr_work = list_entry(curr, struct work_s, node);
```
created_at: 2025-04-17T15:17:12Z
author_association: CONTRIBUTOR
diff_hunk: @@ -160,14 +208,19 @@ static int work_thread(int argc, FAR char *argv[])
        * so ourselves, and (2) there will be no changes to the work queue
        */
 
+      ticks = clock_systime_ticks();
+
+      /* Check if there is expired delayed work */
+
+      work_expiration(ticks, wqueue);
+
       /* Remove the ready-to-execute work from the list */
 
-      while ((work = (FAR struct work_s *)dq_remfirst(&wqueue->q)) != NULL)
+      while (!list_is_empty(&wqueue->q))
path: sched/wqueue/kwork_thread.c
user: login: xiaoxiang781216
body: what's benefit? this suggestion require the change to add another loop to handle the work item maybe add after line 198
created_at: 2025-04-18T01:37:01Z
author_association: CONTRIBUTOR
diff_hunk: @@ -249,19 +249,11 @@ typedef CODE void (*worker_t)(FAR void *arg);
 
 struct work_s
 {
-  union
-  {
-    struct
-    {
-      struct list_node node;     /* Implements a double linked list */
-      clock_t qtime;             /* Time work queued */
-    } s;
-    struct wdog_s timer;         /* Delay expiry timer */
-    struct wdog_period_s ptimer; /* Period expiry timer */
-  } u;
+  struct list_node  node;        /* Implements a double linked list */
path: include/nuttx/wqueue.h
user: login: xiaoxiang781216
body: remove one space before node
created_at: 2025-04-18T01:38:19Z
author_association: CONTRIBUTOR
diff_hunk: @@ -249,19 +249,11 @@ typedef CODE void (*worker_t)(FAR void *arg);
 
 struct work_s
 {
-  union
-  {
-    struct
-    {
-      struct list_node node;     /* Implements a double linked list */
-      clock_t qtime;             /* Time work queued */
-    } s;
-    struct wdog_s timer;         /* Delay expiry timer */
-    struct wdog_period_s ptimer; /* Period expiry timer */
-  } u;
+  struct list_node  node;        /* Implements a double linked list */
+  clock_t   qtime;               /* Time work queued */
+  clock_t   period;              /* Periodical delay ticks */
path: include/nuttx/wqueue.h
user: login: xiaoxiang781216
body: remove the extra spaces before comment
created_at: 2025-04-18T01:41:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -89,7 +89,7 @@ static int work_qqueue(FAR struct usr_wqueue_s *wqueue,
 
   work->worker = worker;             /* Work callback. non-NULL means queued */
   work->arg    = arg;                /* Callback argument */
-  work->u.s.qtime = clock() + delay; /* Delay until work performed */
+  work->qtime  = clock() + delay;    /* Delay until work performed */
path: libs/libc/wqueue/work_queue.c
user: login: xiaoxiang781216
body: remove two spaces before comment
created_at: 2025-04-18T01:42:05Z
author_association: CONTRIBUTOR
diff_hunk: @@ -83,13 +85,7 @@ struct kwork_wqueue_s
 #ifdef CONFIG_SCHED_HPWORK
 struct hp_wqueue_s
 {
-  struct list_node  q;         /* The queue of pending work */
-  sem_t             sem;       /* The counting semaphore of the wqueue */
-  sem_t             exsem;     /* Sync waiting for thread exit */
-  spinlock_t        lock;      /* Spinlock */
-  uint8_t           nthreads;  /* Number of worker threads */
-  bool              exit;      /* A flag to request the thread to exit */
-  int16_t           wait_count;
+  struct kwork_wqueue_s wq;
path: sched/wqueue/wqueue.h
user: login: xiaoxiang781216
body: remove or align worker at line 92
created_at: 2025-04-18T01:47:29Z
author_association: CONTRIBUTOR
diff_hunk: @@ -104,13 +100,7 @@ struct hp_wqueue_s
 #ifdef CONFIG_SCHED_LPWORK
 struct lp_wqueue_s
 {
-  struct list_node  q;         /* The queue of pending work */
-  sem_t             sem;       /* The counting semaphore of the wqueue */
-  sem_t             exsem;     /* Sync waiting for thread exit */
-  spinlock_t        lock;      /* Spinlock */
-  uint8_t           nthreads;  /* Number of worker threads */
-  bool              exit;      /* A flag to request the thread to exit */
-  int16_t           wait_count;
+  struct kwork_wqueue_s wq;
path: sched/wqueue/wqueue.h
user: login: xiaoxiang781216
body: ditto
created_at: 2025-04-18T01:47:34Z
author_association: CONTRIBUTOR
diff_hunk: @@ -59,38 +59,20 @@ static int work_qcancel(FAR struct kwork_wqueue_s *wqueue, bool sync,
    */
 
   flags = spin_lock_irqsave(&wqueue->lock);
+
+  /* Check whether we own the work structure. */
+
   if (work->worker != NULL)
     {
-      /* Remove the entry from the work queue and make sure that it is
-       * marked as available (i.e., the worker field is nullified).
-       */
+      /* Seize the ownership from the work thread. */
 
       work->worker = NULL;
-      wd_cancel(&work->u.timer);
-      if (dq_inqueue((FAR dq_entry_t *)work, &wqueue->q))
-        {
-          dq_rem((FAR dq_entry_t *)work, &wqueue->q);
-        }
 
-      ret = OK;
-    }
-  else if (!up_interrupt_context() && !sched_idletask() && sync)
-    {
-      int wndx;
-
-      for (wndx = 0; wndx < wqueue->nthreads; wndx++)
-        {
-          if (wqueue->worker[wndx].work == work &&
-              wqueue->worker[wndx].pid != nxsched_gettid())
-            {
-              wqueue->worker[wndx].wait_count++;
-              spin_unlock_irqrestore(&wqueue->lock, flags);
-              nxsem_wait_uninterruptible(&wqueue->worker[wndx].wait);
-              return 1;
-            }
-        }
+      list_delete(&work->node);
     }
 
+  ret = OK;
path: sched/wqueue/kwork_cancel.c
user: login: xiaoxiang781216
body: how to handle the caller want to wait until the callback finish?
created_at: 2025-04-18T01:50:30Z
author_association: CONTRIBUTOR
diff_hunk: @@ -83,11 +83,15 @@
 
 struct hp_wqueue_s g_hpwork =
 {
-  LIST_INITIAL_VALUE(g_hpwork.q),
-  SEM_INITIALIZER(0),
-  SEM_INITIALIZER(0),
-  SP_UNLOCKED,
-  CONFIG_SCHED_HPNTHREADS,
+  .wq =
path: sched/wqueue/kwork_thread.c
user: login: xiaoxiang781216
body: don't use .xxx = yyy, which some old compiler doesn't support c99 feature.
created_at: 2025-04-18T01:53:18Z
author_association: CONTRIBUTOR
diff_hunk: @@ -155,49 +94,48 @@ int work_queue_period_wq(FAR struct kwork_wqueue_s *wqueue,
   flags = spin_lock_irqsave(&wqueue->lock);
   sched_lock();
 
-  /* Remove the entry from the timer and work queue. */
+  /* Check whether we own the work structure. */
 
   if (work->worker != NULL)
     {
-      /* Remove the entry from the work queue and make sure that it is
-       * marked as available (i.e., the worker field is nullified).
-       */
+      /* Seize the ownership from the work thread. */
 
       work->worker = NULL;
-      wd_cancel(&work->u.timer);
 
-      list_delete(&work->u.s.node);
-    }
-
-  if (work_is_canceling(wqueue->worker, wqueue->nthreads, work))
-    {
-      goto out;
+      list_delete(&work->node);
     }
 
   /* Initialize the work structure. */
 
   work->worker = worker;           /* Work callback. non-NULL means queued */
   work->arg    = arg;              /* Callback argument */
-  work->wq     = wqueue;           /* Work queue */
+  work->period = period;           /* Periodical delay */
 
   /* Queue the new work */
 
   if (!delay)
     {
-      queue_work(wqueue, work);
+      work->qtime = clock_systime_ticks();
+
+      list_add_tail(&wqueue->q, &work->node);
     }
-  else if (period == 0)
+  else
     {
-      ret = wd_start(&work->u.timer, delay,
-                     work_timer_expiry, (wdparm_t)work);
+      /* Insert the work to the workqueue's waiting list. */
+
+      work->qtime = clock_systime_ticks() + delay + 1;
+
+      work_insert_waitq(wqueue, work);
     }
-  else
+
+  /* Wakeup the wqueue thread. */
+
+  if (wqueue->wait_count > 0) /* There are threads waiting for sem. */
     {
-      ret = wd_start_period(&work->u.ptimer, delay, period,
-                            work_timer_expiry, (wdparm_t)work);
+      wqueue->wait_count--;
+      nxsem_post(&wqueue->sem);
     }
 
-out:
   spin_unlock_irqrestore(&wqueue->lock, flags);
path: sched/wqueue/kwork_queue.c
user: login: xiaoxiang781216
body: could we move spin_unlock_irqrestore before line 136? so, we can remove sched_lock/sched_unlock
created_at: 2025-04-18T01:54:03Z
author_association: CONTRIBUTOR
diff_hunk: @@ -110,6 +118,44 @@ struct lp_wqueue_s g_lpwork =
  * Private Functions
  ****************************************************************************/
 
+static inline void work_timer_expired(wdparm_t arg)
path: sched/wqueue/kwork_thread.c
user: login: xiaoxiang781216
body: why add inline which is always called through pointer
created_at: 2025-04-18T01:55:02Z
author_association: CONTRIBUTOR
diff_hunk: @@ -110,6 +118,44 @@ struct lp_wqueue_s g_lpwork =
  * Private Functions
  ****************************************************************************/
 
+static inline void work_timer_expired(wdparm_t arg)
+{
+  FAR struct kwork_wqueue_s *wq = (FAR struct kwork_wqueue_s *)arg;
+  irqstate_t flags = spin_lock_irqsave(&wq->lock);
+  sched_lock();
+
+  if (wq->wait_count > 0)
+    {
+      wq->wait_count--;
+      nxsem_post(&wq->sem);
+    }
+
+  spin_unlock_irqrestore(&wq->lock, flags);
path: sched/wqueue/kwork_thread.c
user: login: xiaoxiang781216
body: move before line 130 to remove sched_lock/sched_unlock
created_at: 2025-04-18T01:55:37Z
author_association: CONTRIBUTOR
diff_hunk: @@ -110,6 +118,44 @@ struct lp_wqueue_s g_lpwork =
  * Private Functions
  ****************************************************************************/
 
+static inline void work_timer_expired(wdparm_t arg)
+{
+  FAR struct kwork_wqueue_s *wq = (FAR struct kwork_wqueue_s *)arg;
+  irqstate_t flags = spin_lock_irqsave(&wq->lock);
+  sched_lock();
+
+  if (wq->wait_count > 0)
+    {
+      wq->wait_count--;
+      nxsem_post(&wq->sem);
+    }
+
+  spin_unlock_irqrestore(&wq->lock, flags);
+  sched_unlock();
+}
+
+static inline
+void work_expiration(clock_t ticks, FAR struct kwork_wqueue_s *wq)
path: sched/wqueue/kwork_thread.c
user: login: xiaoxiang781216
body: void work_expiration(FAR struct kwork_wqueue_s *wq, clock_t ticks)
created_at: 2025-04-18T01:56:49Z
author_association: CONTRIBUTOR
diff_hunk: @@ -211,6 +288,17 @@ static int work_thread(int argc, FAR char *argv[])
             }
         }
 
+      if (!list_is_empty(&wqueue->wait_q))
path: sched/wqueue/kwork_thread.c
user: login: xiaoxiang781216
body: don't need call wd_start_abstick again if the head of wait_q isn't changed
created_at: 2025-04-18T02:00:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -59,38 +59,20 @@ static int work_qcancel(FAR struct kwork_wqueue_s *wqueue, bool sync,
    */
 
   flags = spin_lock_irqsave(&wqueue->lock);
+
+  /* Check whether we own the work structure. */
+
   if (work->worker != NULL)
     {
-      /* Remove the entry from the work queue and make sure that it is
-       * marked as available (i.e., the worker field is nullified).
-       */
+      /* Seize the ownership from the work thread. */
 
       work->worker = NULL;
-      wd_cancel(&work->u.timer);
-      if (dq_inqueue((FAR dq_entry_t *)work, &wqueue->q))
-        {
-          dq_rem((FAR dq_entry_t *)work, &wqueue->q);
-        }
 
-      ret = OK;
-    }
-  else if (!up_interrupt_context() && !sched_idletask() && sync)
-    {
-      int wndx;
-
-      for (wndx = 0; wndx < wqueue->nthreads; wndx++)
-        {
-          if (wqueue->worker[wndx].work == work &&
-              wqueue->worker[wndx].pid != nxsched_gettid())
-            {
-              wqueue->worker[wndx].wait_count++;
-              spin_unlock_irqrestore(&wqueue->lock, flags);
-              nxsem_wait_uninterruptible(&wqueue->worker[wndx].wait);
-              return 1;
-            }
-        }
+      list_delete(&work->node);
     }
 
+  ret = OK;
path: sched/wqueue/kwork_cancel.c
user: login: anchao
body: The current version does not implement this feature. so has this commit not been reviewed by you? I thought the intention was to remove the sync semantics.
created_at: 2025-04-18T02:35:58Z
author_association: CONTRIBUTOR
diff_hunk: @@ -160,14 +208,19 @@ static int work_thread(int argc, FAR char *argv[])
        * so ourselves, and (2) there will be no changes to the work queue
        */
 
+      ticks = clock_systime_ticks();
+
+      /* Check if there is expired delayed work */
+
+      work_expiration(ticks, wqueue);
+
       /* Remove the ready-to-execute work from the list */
 
-      while ((work = (FAR struct work_s *)dq_remfirst(&wqueue->q)) != NULL)
+      while (!list_is_empty(&wqueue->q))
path: sched/wqueue/kwork_thread.c
user: login: anchao
body: The readytorun queue is now only used in this function. If the member is not accessed by the external API in the design, it needs to be privatized in this function.
created_at: 2025-04-18T02:41:12Z
author_association: CONTRIBUTOR
diff_hunk: @@ -59,38 +59,20 @@ static int work_qcancel(FAR struct kwork_wqueue_s *wqueue, bool sync,
    */
 
   flags = spin_lock_irqsave(&wqueue->lock);
+
+  /* Check whether we own the work structure. */
+
   if (work->worker != NULL)
     {
-      /* Remove the entry from the work queue and make sure that it is
-       * marked as available (i.e., the worker field is nullified).
-       */
+      /* Seize the ownership from the work thread. */
 
       work->worker = NULL;
-      wd_cancel(&work->u.timer);
-      if (dq_inqueue((FAR dq_entry_t *)work, &wqueue->q))
-        {
-          dq_rem((FAR dq_entry_t *)work, &wqueue->q);
-        }
 
-      ret = OK;
-    }
-  else if (!up_interrupt_context() && !sched_idletask() && sync)
-    {
-      int wndx;
-
-      for (wndx = 0; wndx < wqueue->nthreads; wndx++)
-        {
-          if (wqueue->worker[wndx].work == work &&
-              wqueue->worker[wndx].pid != nxsched_gettid())
-            {
-              wqueue->worker[wndx].wait_count++;
-              spin_unlock_irqrestore(&wqueue->lock, flags);
-              nxsem_wait_uninterruptible(&wqueue->worker[wndx].wait);
-              return 1;
-            }
-        }
+      list_delete(&work->node);
     }
 
+  ret = OK;
path: sched/wqueue/kwork_cancel.c
user: login: Fix-Point
body: Some modules call the `work_cancel_sync()`. Remove it lead to compilation error. I am going to remove the API in the next patch.
created_at: 2025-04-18T03:02:04Z
author_association: CONTRIBUTOR
diff_hunk: @@ -155,51 +94,48 @@ int work_queue_period_wq(FAR struct kwork_wqueue_s *wqueue,
   flags = spin_lock_irqsave(&wqueue->lock);
   sched_lock();
 
-  /* Remove the entry from the timer and work queue. */
+  /* Check whether we own the work structure. */
 
   if (work->worker != NULL)
path: sched/wqueue/kwork_queue.c
user: login: Fix-Point
body: Done.
created_at: 2025-04-18T03:02:09Z
author_association: CONTRIBUTOR
diff_hunk: @@ -155,51 +94,48 @@ int work_queue_period_wq(FAR struct kwork_wqueue_s *wqueue,
   flags = spin_lock_irqsave(&wqueue->lock);
   sched_lock();
 
-  /* Remove the entry from the timer and work queue. */
+  /* Check whether we own the work structure. */
 
   if (work->worker != NULL)
     {
-      /* Remove the entry from the work queue and make sure that it is
-       * marked as available (i.e., the worker field is nullified).
-       */
+      /* Seize the ownership from the work thread. */
 
       work->worker = NULL;
-      wd_cancel(&work->u.timer);
-      if (dq_inqueue((FAR dq_entry_t *)work, &wqueue->q))
-        {
-          dq_rem((FAR dq_entry_t *)work, &wqueue->q);
-        }
-    }
 
-  if (work_is_canceling(wqueue->worker, wqueue->nthreads, work))
-    {
-      goto out;
+      list_delete(&work->node);
     }
 
   /* Initialize the work structure. */
 
   work->worker = worker;           /* Work callback. non-NULL means queued */
   work->arg    = arg;              /* Callback argument */
-  work->wq     = wqueue;           /* Work queue */
+  work->period = period;           /* Periodical delay */
 
   /* Queue the new work */
 
   if (!delay)
     {
-      queue_work(wqueue, work);
+      work->qtime = clock_systime_ticks();
+
+      list_add_tail(&wqueue->q, &work->node);
     }
-  else if (period == 0)
+  else
     {
-      ret = wd_start(&work->u.timer, delay,
-                     work_timer_expiry, (wdparm_t)work);
+      /* Insert the work to the workqueue's waiting list. */
+
+      work->qtime = clock_systime_ticks() + delay + 1;
+
+      work_insert_waitq(wqueue, work);
     }
-  else
+
+  /* Wakeup the wqueue thread. */
+
+  if (wqueue->wait_count > 0) /* There are threads waiting for sem. */
     {
-      ret = wd_start_period(&work->u.ptimer, delay, period,
-                            work_timer_expiry, (wdparm_t)work);
+      wqueue->wait_count--;
+      nxsem_post(&wqueue->sem);
     }
 
-out:
   spin_unlock_irqrestore(&wqueue->lock, flags);
   sched_unlock();
   return ret;
path: sched/wqueue/kwork_queue.c
user: login: Fix-Point
body: Done.
created_at: 2025-04-18T03:02:18Z
author_association: CONTRIBUTOR
diff_hunk: @@ -110,6 +118,44 @@ struct lp_wqueue_s g_lpwork =
  * Private Functions
  ****************************************************************************/
 
+static inline void work_timer_expired(wdparm_t arg)
+{
+  FAR struct kwork_wqueue_s *wq = (FAR struct kwork_wqueue_s *)arg;
+  irqstate_t flags = spin_lock_irqsave(&wq->lock);
+  sched_lock();
+
+  if (wq->wait_count > 0)
+    {
+      wq->wait_count--;
+      nxsem_post(&wq->sem);
+    }
+
+  spin_unlock_irqrestore(&wq->lock, flags);
+  sched_unlock();
+}
+
+static inline
+void work_expiration(clock_t ticks, FAR struct kwork_wqueue_s *wq)
+{
+  struct work_s *work;
+
+  while (!list_is_empty(&wq->wait_q))
path: sched/wqueue/kwork_thread.c
user: login: Fix-Point
body: Done.
created_at: 2025-04-18T03:04:22Z
author_association: CONTRIBUTOR
diff_hunk: @@ -181,7 +234,31 @@ static int work_thread(int argc, FAR char *argv[])
 
           /* Mark the work as no longer being queued */
 
-          work->worker = NULL;
+          if (work->period != 0)
+            {
+              /* Insert qtime */
+
+              work->qtime += work->period;
+
+              if (clock_compare(work->qtime, ticks))
path: sched/wqueue/kwork_thread.c
user: login: Fix-Point
body: Done.
created_at: 2025-04-18T03:04:26Z
author_association: CONTRIBUTOR
diff_hunk: @@ -211,6 +288,17 @@ static int work_thread(int argc, FAR char *argv[])
             }
         }
 
+      if (!list_is_empty(&wqueue->wait_q))
path: sched/wqueue/kwork_thread.c
user: login: Fix-Point
body: Fixed.
created_at: 2025-04-18T03:04:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -160,14 +208,19 @@ static int work_thread(int argc, FAR char *argv[])
        * so ourselves, and (2) there will be no changes to the work queue
        */
 
+      ticks = clock_systime_ticks();
+
+      /* Check if there is expired delayed work */
+
+      work_expiration(ticks, wqueue);
+
       /* Remove the ready-to-execute work from the list */
 
-      while ((work = (FAR struct work_s *)dq_remfirst(&wqueue->q)) != NULL)
+      while (!list_is_empty(&wqueue->q))
path: sched/wqueue/kwork_thread.c
user: login: Fix-Point
body: Done.
created_at: 2025-04-18T03:04:58Z
author_association: CONTRIBUTOR
diff_hunk: @@ -159,6 +149,24 @@ static inline_function FAR struct kwork_wqueue_s *work_qid2wq(int qid)
     }
 }
 
+static inline_function
+void work_insert_waitq(FAR struct kwork_wqueue_s *wqueue,
+                       struct work_s *work)
+{
+  struct list_node *curr;
+
+  list_for_every(&wqueue->wait_q, curr)
+    {
+      struct work_s *curr_work = list_entry(curr, struct work_s, node);
path: sched/wqueue/wqueue.h
user: login: Fix-Point
body: Done.
created_at: 2025-04-18T03:05:03Z
author_association: CONTRIBUTOR
diff_hunk: @@ -249,19 +249,11 @@ typedef CODE void (*worker_t)(FAR void *arg);
 
 struct work_s
 {
-  union
-  {
-    struct
-    {
-      struct list_node node;     /* Implements a double linked list */
-      clock_t qtime;             /* Time work queued */
-    } s;
-    struct wdog_s timer;         /* Delay expiry timer */
-    struct wdog_period_s ptimer; /* Period expiry timer */
-  } u;
+  struct list_node  node;        /* Implements a double linked list */
path: include/nuttx/wqueue.h
user: login: Fix-Point
body: Done.
created_at: 2025-04-18T03:17:41Z
author_association: CONTRIBUTOR
diff_hunk: @@ -249,19 +249,11 @@ typedef CODE void (*worker_t)(FAR void *arg);
 
 struct work_s
 {
-  union
-  {
-    struct
-    {
-      struct list_node node;     /* Implements a double linked list */
-      clock_t qtime;             /* Time work queued */
-    } s;
-    struct wdog_s timer;         /* Delay expiry timer */
-    struct wdog_period_s ptimer; /* Period expiry timer */
-  } u;
+  struct list_node  node;        /* Implements a double linked list */
+  clock_t   qtime;               /* Time work queued */
+  clock_t   period;              /* Periodical delay ticks */
path: include/nuttx/wqueue.h
user: login: Fix-Point
body: Done.
created_at: 2025-04-18T03:18:13Z
author_association: CONTRIBUTOR
diff_hunk: @@ -89,7 +89,7 @@ static int work_qqueue(FAR struct usr_wqueue_s *wqueue,
 
   work->worker = worker;             /* Work callback. non-NULL means queued */
   work->arg    = arg;                /* Callback argument */
-  work->u.s.qtime = clock() + delay; /* Delay until work performed */
+  work->qtime  = clock() + delay;    /* Delay until work performed */
path: libs/libc/wqueue/work_queue.c
user: login: Fix-Point
body: Done.
created_at: 2025-04-18T03:18:18Z
author_association: CONTRIBUTOR
diff_hunk: @@ -83,13 +85,7 @@ struct kwork_wqueue_s
 #ifdef CONFIG_SCHED_HPWORK
 struct hp_wqueue_s
 {
-  struct list_node  q;         /* The queue of pending work */
-  sem_t             sem;       /* The counting semaphore of the wqueue */
-  sem_t             exsem;     /* Sync waiting for thread exit */
-  spinlock_t        lock;      /* Spinlock */
-  uint8_t           nthreads;  /* Number of worker threads */
-  bool              exit;      /* A flag to request the thread to exit */
-  int16_t           wait_count;
+  struct kwork_wqueue_s wq;
path: sched/wqueue/wqueue.h
user: login: Fix-Point
body: Done.
created_at: 2025-04-18T03:18:22Z
author_association: CONTRIBUTOR
diff_hunk: @@ -155,49 +94,48 @@ int work_queue_period_wq(FAR struct kwork_wqueue_s *wqueue,
   flags = spin_lock_irqsave(&wqueue->lock);
   sched_lock();
 
-  /* Remove the entry from the timer and work queue. */
+  /* Check whether we own the work structure. */
 
   if (work->worker != NULL)
     {
-      /* Remove the entry from the work queue and make sure that it is
-       * marked as available (i.e., the worker field is nullified).
-       */
+      /* Seize the ownership from the work thread. */
 
       work->worker = NULL;
-      wd_cancel(&work->u.timer);
 
-      list_delete(&work->u.s.node);
-    }
-
-  if (work_is_canceling(wqueue->worker, wqueue->nthreads, work))
-    {
-      goto out;
+      list_delete(&work->node);
     }
 
   /* Initialize the work structure. */
 
   work->worker = worker;           /* Work callback. non-NULL means queued */
   work->arg    = arg;              /* Callback argument */
-  work->wq     = wqueue;           /* Work queue */
+  work->period = period;           /* Periodical delay */
 
   /* Queue the new work */
 
   if (!delay)
     {
-      queue_work(wqueue, work);
+      work->qtime = clock_systime_ticks();
+
+      list_add_tail(&wqueue->q, &work->node);
     }
-  else if (period == 0)
+  else
     {
-      ret = wd_start(&work->u.timer, delay,
-                     work_timer_expiry, (wdparm_t)work);
+      /* Insert the work to the workqueue's waiting list. */
+
+      work->qtime = clock_systime_ticks() + delay + 1;
+
+      work_insert_waitq(wqueue, work);
     }
-  else
+
+  /* Wakeup the wqueue thread. */
+
+  if (wqueue->wait_count > 0) /* There are threads waiting for sem. */
     {
-      ret = wd_start_period(&work->u.ptimer, delay, period,
-                            work_timer_expiry, (wdparm_t)work);
+      wqueue->wait_count--;
+      nxsem_post(&wqueue->sem);
     }
 
-out:
   spin_unlock_irqrestore(&wqueue->lock, flags);
path: sched/wqueue/kwork_queue.c
user: login: Fix-Point
body: I will try this in the next patch.
created_at: 2025-04-18T03:19:01Z
author_association: CONTRIBUTOR
diff_hunk: @@ -110,6 +118,44 @@ struct lp_wqueue_s g_lpwork =
  * Private Functions
  ****************************************************************************/
 
+static inline void work_timer_expired(wdparm_t arg)
+{
+  FAR struct kwork_wqueue_s *wq = (FAR struct kwork_wqueue_s *)arg;
+  irqstate_t flags = spin_lock_irqsave(&wq->lock);
+  sched_lock();
+
+  if (wq->wait_count > 0)
+    {
+      wq->wait_count--;
+      nxsem_post(&wq->sem);
+    }
+
+  spin_unlock_irqrestore(&wq->lock, flags);
path: sched/wqueue/kwork_thread.c
user: login: Fix-Point
body: Inline callback is removed.
created_at: 2025-04-18T03:20:24Z
author_association: CONTRIBUTOR
diff_hunk: @@ -110,6 +118,44 @@ struct lp_wqueue_s g_lpwork =
  * Private Functions
  ****************************************************************************/
 
+static inline void work_timer_expired(wdparm_t arg)
+{
+  FAR struct kwork_wqueue_s *wq = (FAR struct kwork_wqueue_s *)arg;
+  irqstate_t flags = spin_lock_irqsave(&wq->lock);
+  sched_lock();
+
+  if (wq->wait_count > 0)
+    {
+      wq->wait_count--;
+      nxsem_post(&wq->sem);
+    }
+
+  spin_unlock_irqrestore(&wq->lock, flags);
+  sched_unlock();
+}
+
+static inline
+void work_expiration(clock_t ticks, FAR struct kwork_wqueue_s *wq)
path: sched/wqueue/kwork_thread.c
user: login: Fix-Point
body: Done.
created_at: 2025-04-18T03:20:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -83,11 +83,15 @@
 
 struct hp_wqueue_s g_hpwork =
 {
-  LIST_INITIAL_VALUE(g_hpwork.q),
-  SEM_INITIALIZER(0),
-  SEM_INITIALIZER(0),
-  SP_UNLOCKED,
-  CONFIG_SCHED_HPNTHREADS,
+  .wq =
path: sched/wqueue/kwork_thread.c
user: login: Fix-Point
body: fixed
created_at: 2025-04-18T03:26:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -83,11 +83,15 @@
 
 struct hp_wqueue_s g_hpwork =
 {
-  LIST_INITIAL_VALUE(g_hpwork.q),
-  SEM_INITIALIZER(0),
-  SEM_INITIALIZER(0),
-  SP_UNLOCKED,
-  CONFIG_SCHED_HPNTHREADS,
+  .wq =
path: sched/wqueue/kwork_thread.c
user: login: Fix-Point
body: Fixed the cancel.
created_at: 2025-04-18T03:32:04Z
author_association: CONTRIBUTOR
diff_hunk: @@ -59,38 +59,20 @@ static int work_qcancel(FAR struct kwork_wqueue_s *wqueue, bool sync,
    */
 
   flags = spin_lock_irqsave(&wqueue->lock);
+
+  /* Check whether we own the work structure. */
+
   if (work->worker != NULL)
     {
-      /* Remove the entry from the work queue and make sure that it is
-       * marked as available (i.e., the worker field is nullified).
-       */
+      /* Seize the ownership from the work thread. */
 
       work->worker = NULL;
-      wd_cancel(&work->u.timer);
-      if (dq_inqueue((FAR dq_entry_t *)work, &wqueue->q))
-        {
-          dq_rem((FAR dq_entry_t *)work, &wqueue->q);
-        }
 
-      ret = OK;
-    }
-  else if (!up_interrupt_context() && !sched_idletask() && sync)
-    {
-      int wndx;
-
-      for (wndx = 0; wndx < wqueue->nthreads; wndx++)
-        {
-          if (wqueue->worker[wndx].work == work &&
-              wqueue->worker[wndx].pid != nxsched_gettid())
-            {
-              wqueue->worker[wndx].wait_count++;
-              spin_unlock_irqrestore(&wqueue->lock, flags);
-              nxsem_wait_uninterruptible(&wqueue->worker[wndx].wait);
-              return 1;
-            }
-        }
+      list_delete(&work->node);
     }
 
+  ret = OK;
path: sched/wqueue/kwork_cancel.c
user: login: xiaoxiang781216
body: no, you can't remove the sync version, since the caller need sync to avoid the race condition.
created_at: 2025-04-18T04:23:16Z
author_association: CONTRIBUTOR
diff_hunk: @@ -59,38 +59,20 @@ static int work_qcancel(FAR struct kwork_wqueue_s *wqueue, bool sync,
    */
 
   flags = spin_lock_irqsave(&wqueue->lock);
+
+  /* Check whether we own the work structure. */
+
   if (work->worker != NULL)
     {
-      /* Remove the entry from the work queue and make sure that it is
-       * marked as available (i.e., the worker field is nullified).
-       */
+      /* Seize the ownership from the work thread. */
 
       work->worker = NULL;
-      wd_cancel(&work->u.timer);
-      if (dq_inqueue((FAR dq_entry_t *)work, &wqueue->q))
-        {
-          dq_rem((FAR dq_entry_t *)work, &wqueue->q);
-        }
 
-      ret = OK;
-    }
-  else if (!up_interrupt_context() && !sched_idletask() && sync)
-    {
-      int wndx;
-
-      for (wndx = 0; wndx < wqueue->nthreads; wndx++)
-        {
-          if (wqueue->worker[wndx].work == work &&
-              wqueue->worker[wndx].pid != nxsched_gettid())
-            {
-              wqueue->worker[wndx].wait_count++;
-              spin_unlock_irqrestore(&wqueue->lock, flags);
-              nxsem_wait_uninterruptible(&wqueue->worker[wndx].wait);
-              return 1;
-            }
-        }
+      list_delete(&work->node);
     }
 
+  ret = OK;
path: sched/wqueue/kwork_cancel.c
user: login: xiaoxiang781216
body: > The current version does not implement this feature. so has this commit not been reviewed by you? I thought the intention was to remove the sync semantics.

it's a hot fix, not go through the internal review first.
created_at: 2025-04-18T04:24:58Z
author_association: CONTRIBUTOR
diff_hunk: @@ -110,6 +118,44 @@ struct lp_wqueue_s g_lpwork =
  * Private Functions
  ****************************************************************************/
 
+static inline void work_timer_expired(wdparm_t arg)
path: sched/wqueue/kwork_thread.c
user: login: Fix-Point
body: Fixed.
created_at: 2025-04-18T06:08:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -159,6 +149,24 @@ static inline_function FAR struct kwork_wqueue_s *work_qid2wq(int qid)
     }
 }
 
+static inline_function
+void work_insert_waitq(FAR struct kwork_wqueue_s *wqueue,
+                       struct work_s *work)
+{
+  struct list_node *curr;
+
+  list_for_every(&wqueue->wait_q, curr)
path: sched/wqueue/wqueue.h
user: login: Fix-Point
body: Done.
created_at: 2025-04-18T06:08:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -249,19 +249,11 @@ typedef CODE void (*worker_t)(FAR void *arg);
 
 struct work_s
 {
-  union
-  {
-    struct
-    {
-      struct list_node node;     /* Implements a double linked list */
-      clock_t qtime;             /* Time work queued */
-    } s;
-    struct wdog_s timer;         /* Delay expiry timer */
-    struct wdog_period_s ptimer; /* Period expiry timer */
-  } u;
+  struct list_node  node;        /* Implements a double linked list */
+  clock_t   qtime;               /* Time work queued */
+  clock_t   period;              /* Periodical delay ticks */
path: include/nuttx/wqueue.h
user: login: xiaoxiang781216
body: but no change
created_at: 2025-04-18T08:31:41Z
author_association: CONTRIBUTOR
diff_hunk: @@ -155,49 +94,48 @@ int work_queue_period_wq(FAR struct kwork_wqueue_s *wqueue,
   flags = spin_lock_irqsave(&wqueue->lock);
   sched_lock();
 
-  /* Remove the entry from the timer and work queue. */
+  /* Check whether we own the work structure. */
 
   if (work->worker != NULL)
     {
-      /* Remove the entry from the work queue and make sure that it is
-       * marked as available (i.e., the worker field is nullified).
-       */
+      /* Seize the ownership from the work thread. */
 
       work->worker = NULL;
-      wd_cancel(&work->u.timer);
 
-      list_delete(&work->u.s.node);
-    }
-
-  if (work_is_canceling(wqueue->worker, wqueue->nthreads, work))
-    {
-      goto out;
+      list_delete(&work->node);
     }
 
   /* Initialize the work structure. */
 
   work->worker = worker;           /* Work callback. non-NULL means queued */
   work->arg    = arg;              /* Callback argument */
-  work->wq     = wqueue;           /* Work queue */
+  work->period = period;           /* Periodical delay */
 
   /* Queue the new work */
 
   if (!delay)
     {
-      queue_work(wqueue, work);
+      work->qtime = clock_systime_ticks();
+
+      list_add_tail(&wqueue->q, &work->node);
     }
-  else if (period == 0)
+  else
     {
-      ret = wd_start(&work->u.timer, delay,
-                     work_timer_expiry, (wdparm_t)work);
+      /* Insert the work to the workqueue's waiting list. */
+
+      work->qtime = clock_systime_ticks() + delay + 1;
+
+      work_insert_waitq(wqueue, work);
     }
-  else
+
+  /* Wakeup the wqueue thread. */
+
+  if (wqueue->wait_count > 0) /* There are threads waiting for sem. */
     {
-      ret = wd_start_period(&work->u.ptimer, delay, period,
-                            work_timer_expiry, (wdparm_t)work);
+      wqueue->wait_count--;
+      nxsem_post(&wqueue->sem);
     }
 
-out:
   spin_unlock_irqrestore(&wqueue->lock, flags);
path: sched/wqueue/kwork_queue.c
user: login: xiaoxiang781216
body: Ok
created_at: 2025-04-18T08:32:41Z
author_association: CONTRIBUTOR
diff_hunk: @@ -159,6 +148,32 @@ static inline_function FAR struct kwork_wqueue_s *work_qid2wq(int qid)
     }
 }
 
+static inline_function
+void work_insert_waitq(FAR struct kwork_wqueue_s *wqueue,
+                       struct work_s *work)
path: sched/wqueue/wqueue.h
user: login: xiaoxiang781216
body: add FAR
created_at: 2025-04-18T08:33:20Z
author_association: CONTRIBUTOR
diff_hunk: @@ -159,6 +148,32 @@ static inline_function FAR struct kwork_wqueue_s *work_qid2wq(int qid)
     }
 }
 
+static inline_function
+void work_insert_waitq(FAR struct kwork_wqueue_s *wqueue,
+                       struct work_s *work)
+{
+  struct work_s *curr;
path: sched/wqueue/wqueue.h
user: login: xiaoxiang781216
body: ditto
created_at: 2025-04-18T08:33:26Z
author_association: CONTRIBUTOR
diff_hunk: @@ -135,11 +181,14 @@ struct lp_wqueue_s g_lpwork =
 static int work_thread(int argc, FAR char *argv[])
 {
   FAR struct kwork_wqueue_s *wqueue;
-  FAR struct kworker_s *kworker;
-  FAR struct work_s *work;
-  worker_t worker;
+  FAR struct kworker_s      *kworker;
path: sched/wqueue/kwork_thread.c
user: login: xiaoxiang781216
body: remove the extra spaces
created_at: 2025-04-18T08:34:37Z
author_association: CONTRIBUTOR
diff_hunk: @@ -159,6 +148,32 @@ static inline_function FAR struct kwork_wqueue_s *work_qid2wq(int qid)
     }
 }
 
+static inline_function
+void work_insert_waitq(FAR struct kwork_wqueue_s *wqueue,
+                       struct work_s *work)
+{
+  struct work_s *curr;
path: sched/wqueue/wqueue.h
user: login: Fix-Point
body: Done.
created_at: 2025-04-18T08:44:11Z
author_association: CONTRIBUTOR
diff_hunk: @@ -135,11 +181,14 @@ struct lp_wqueue_s g_lpwork =
 static int work_thread(int argc, FAR char *argv[])
 {
   FAR struct kwork_wqueue_s *wqueue;
-  FAR struct kworker_s *kworker;
-  FAR struct work_s *work;
-  worker_t worker;
+  FAR struct kworker_s      *kworker;
path: sched/wqueue/kwork_thread.c
user: login: Fix-Point
body: Done.
created_at: 2025-04-18T08:44:19Z
author_association: CONTRIBUTOR
diff_hunk: @@ -249,19 +249,11 @@ typedef CODE void (*worker_t)(FAR void *arg);
 
 struct work_s
 {
-  union
-  {
-    struct
-    {
-      struct list_node node;     /* Implements a double linked list */
-      clock_t qtime;             /* Time work queued */
-    } s;
-    struct wdog_s timer;         /* Delay expiry timer */
-    struct wdog_period_s ptimer; /* Period expiry timer */
-  } u;
+  struct list_node  node;        /* Implements a double linked list */
+  clock_t   qtime;               /* Time work queued */
+  clock_t   period;              /* Periodical delay ticks */
path: include/nuttx/wqueue.h
user: login: Fix-Point
body: Done
created_at: 2025-04-18T08:45:33Z
author_association: CONTRIBUTOR
diff_hunk: @@ -159,6 +148,32 @@ static inline_function FAR struct kwork_wqueue_s *work_qid2wq(int qid)
     }
 }
 
+static inline_function
+void work_insert_waitq(FAR struct kwork_wqueue_s *wqueue,
+                       struct work_s *work)
path: sched/wqueue/wqueue.h
user: login: Fix-Point
body: Done.
created_at: 2025-04-18T08:45:40Z
author_association: CONTRIBUTOR
diff_hunk: @@ -104,13 +100,7 @@ struct hp_wqueue_s
 #ifdef CONFIG_SCHED_LPWORK
 struct lp_wqueue_s
 {
-  struct list_node  q;         /* The queue of pending work */
-  sem_t             sem;       /* The counting semaphore of the wqueue */
-  sem_t             exsem;     /* Sync waiting for thread exit */
-  spinlock_t        lock;      /* Spinlock */
-  uint8_t           nthreads;  /* Number of worker threads */
-  bool              exit;      /* A flag to request the thread to exit */
-  int16_t           wait_count;
+  struct kwork_wqueue_s wq;
path: sched/wqueue/wqueue.h
user: login: Fix-Point
body: Done
created_at: 2025-04-18T08:46:52Z
author_association: CONTRIBUTOR
diff_hunk: @@ -66,14 +66,15 @@ struct kworker_s
 
 struct kwork_wqueue_s
 {
-  struct list_node  q;         /* The queue of pending work */
-  sem_t             sem;       /* The counting semaphore of the wqueue */
-  sem_t             exsem;     /* Sync waiting for thread exit */
-  spinlock_t        lock;      /* Spinlock */
-  uint8_t           nthreads;  /* Number of worker threads */
-  bool              exit;      /* A flag to request the thread to exit */
-  int16_t           wait_count;
-  struct kworker_s  worker[0]; /* Describes a worker thread */
+  struct list_node wait_q;    /* The queue of pending periodical work. */
path: sched/wqueue/wqueue.h
user: login: xiaoxiang781216
body: let's keep original name(`q`)
created_at: 2025-04-18T12:47:12Z
author_association: CONTRIBUTOR
diff_hunk: @@ -394,6 +493,8 @@ int work_queue_free(FAR struct kwork_wqueue_s *wqueue)
 
   /* Mark the work queue as exiting */
 
+  wd_cancel(&wqueue->timer);
+
path: sched/wqueue/kwork_thread.c
user: login: xiaoxiang781216
body: remove the blank line 
created_at: 2025-04-18T12:48:05Z
author_association: CONTRIBUTOR
diff_hunk: @@ -136,10 +182,12 @@ static int work_thread(int argc, FAR char *argv[])
 {
   FAR struct kwork_wqueue_s *wqueue;
   FAR struct kworker_s *kworker;
-  FAR struct work_s *work;
-  worker_t worker;
-  irqstate_t flags;
-  FAR void *arg;
+  FAR struct work_s    *work;
path: sched/wqueue/kwork_thread.c
user: login: xiaoxiang781216
body: remove extra spaces
created_at: 2025-04-18T12:48:31Z
author_association: CONTRIBUTOR
diff_hunk: @@ -148,30 +196,35 @@ static int work_thread(int argc, FAR char *argv[])
   kworker = (FAR struct kworker_s *)
             ((uintptr_t)strtoul(argv[2], NULL, 16));
 
-  flags = spin_lock_irqsave(&wqueue->lock);
-  sched_lock();
+  list_initialize(&q);
 
-  /* Loop forever */
+  /* Loop until wqueue->exit != 0.
+   * We do not need to enter the critical-section to access wqueue->exit.
+   * Because the only way to set wqueue->exit is to call work_queue_free().
+   */
 
   while (!wqueue->exit)
     {
+      irqstate_t flags = spin_lock_irqsave(&wqueue->lock);
+      sched_lock();
path: sched/wqueue/kwork_thread.c
user: login: xiaoxiang781216
body: remove, don't need sched_lock
created_at: 2025-04-18T12:52:52Z
author_association: CONTRIBUTOR
diff_hunk: @@ -110,6 +114,48 @@ struct lp_wqueue_s g_lpwork =
  * Private Functions
  ****************************************************************************/
 
+/* The work time expired callback will wake up the worker thread. */
+
+static void work_timer_expired(wdparm_t arg)
+{
+  FAR struct kwork_wqueue_s *wq = (FAR struct kwork_wqueue_s *)arg;
+  irqstate_t flags = spin_lock_irqsave(&wq->lock);
+  sched_lock();
+
+  if (wq->wait_count > 0)
path: sched/wqueue/kwork_thread.c
user: login: xiaoxiang781216
body: let's create a new patch to change wait_count to atomic and remove the spinlock around it
created_at: 2025-04-18T12:58:18Z
author_association: CONTRIBUTOR
diff_hunk: @@ -59,37 +61,36 @@ static int work_qcancel(FAR struct kwork_wqueue_s *wqueue, bool sync,
    */
 
   flags = spin_lock_irqsave(&wqueue->lock);
-  if (work->worker != NULL)
+
+  /* Check whether we own the work structure. */
+
+  if (!work_available(work))
     {
-      /* Remove the entry from the work queue and make sure that it is
-       * marked as available (i.e., the worker field is nullified).
-       */
+      /* Seize the ownership from the work thread. */
 
+      worker = work->worker;
+      arg    = work->arg;
+
+      run_myself   = sync;
       work->worker = NULL;
-      wd_cancel(&work->u.timer);
-      list_delete(&work->u.s.node);
 
-      ret = OK;
+      list_delete(&work->node);
     }
-  else if (!up_interrupt_context() && !sched_idletask() && sync)
+
+  spin_unlock_irqrestore(&wqueue->lock, flags);
+
+  if (run_myself)
     {
-      int wndx;
-
-      for (wndx = 0; wndx < wqueue->nthreads; wndx++)
-        {
-          if (wqueue->worker[wndx].work == work &&
-              wqueue->worker[wndx].pid != nxsched_gettid())
path: sched/wqueue/kwork_cancel.c
user: login: xiaoxiang781216
body: why remove this block
created_at: 2025-04-18T13:01:58Z
author_association: CONTRIBUTOR
diff_hunk: @@ -148,30 +196,35 @@ static int work_thread(int argc, FAR char *argv[])
   kworker = (FAR struct kworker_s *)
             ((uintptr_t)strtoul(argv[2], NULL, 16));
 
-  flags = spin_lock_irqsave(&wqueue->lock);
-  sched_lock();
+  list_initialize(&q);
 
-  /* Loop forever */
+  /* Loop until wqueue->exit != 0.
+   * We do not need to enter the critical-section to access wqueue->exit.
+   * Because the only way to set wqueue->exit is to call work_queue_free().
+   */
 
   while (!wqueue->exit)
     {
+      irqstate_t flags = spin_lock_irqsave(&wqueue->lock);
+      sched_lock();
+
       /* And check each entry in the work queue.  Since we have disabled
        * interrupts we know:  (1) we will not be suspended unless we do
        * so ourselves, and (2) there will be no changes to the work queue
        */
 
+      /* Check if there is expired delayed work */
+
+      ticks = clock_systime_ticks();
+      work_expiration(wqueue, &q, ticks);
path: sched/wqueue/kwork_thread.c
user: login: xiaoxiang781216
body: work_cancel can't find the work in the local list(`q`)
created_at: 2025-04-18T13:09:24Z
author_association: CONTRIBUTOR
diff_hunk: @@ -66,14 +66,15 @@ struct kworker_s
 
 struct kwork_wqueue_s
 {
-  struct list_node  q;         /* The queue of pending work */
-  sem_t             sem;       /* The counting semaphore of the wqueue */
-  sem_t             exsem;     /* Sync waiting for thread exit */
-  spinlock_t        lock;      /* Spinlock */
-  uint8_t           nthreads;  /* Number of worker threads */
-  bool              exit;      /* A flag to request the thread to exit */
-  int16_t           wait_count;
-  struct kworker_s  worker[0]; /* Describes a worker thread */
+  struct list_node wait_q;    /* The queue of pending periodical work. */
path: sched/wqueue/wqueue.h
user: login: Fix-Point
body: Done.
created_at: 2025-04-19T01:53:05Z
author_association: CONTRIBUTOR
diff_hunk: @@ -394,6 +493,8 @@ int work_queue_free(FAR struct kwork_wqueue_s *wqueue)
 
   /* Mark the work queue as exiting */
 
+  wd_cancel(&wqueue->timer);
+
path: sched/wqueue/kwork_thread.c
user: login: Fix-Point
body: Done.
created_at: 2025-04-19T01:53:08Z
author_association: CONTRIBUTOR
diff_hunk: @@ -148,30 +196,35 @@ static int work_thread(int argc, FAR char *argv[])
   kworker = (FAR struct kworker_s *)
             ((uintptr_t)strtoul(argv[2], NULL, 16));
 
-  flags = spin_lock_irqsave(&wqueue->lock);
-  sched_lock();
+  list_initialize(&q);
 
-  /* Loop forever */
+  /* Loop until wqueue->exit != 0.
+   * We do not need to enter the critical-section to access wqueue->exit.
+   * Because the only way to set wqueue->exit is to call work_queue_free().
+   */
 
   while (!wqueue->exit)
     {
+      irqstate_t flags = spin_lock_irqsave(&wqueue->lock);
+      sched_lock();
+
       /* And check each entry in the work queue.  Since we have disabled
        * interrupts we know:  (1) we will not be suspended unless we do
        * so ourselves, and (2) there will be no changes to the work queue
        */
 
+      /* Check if there is expired delayed work */
+
+      ticks = clock_systime_ticks();
+      work_expiration(wqueue, &q, ticks);
path: sched/wqueue/kwork_thread.c
user: login: Fix-Point
body: In both local list or shared list, we can use the `list_delete` in critical-section to remove the work safely. 
created_at: 2025-04-19T01:54:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -136,10 +182,12 @@ static int work_thread(int argc, FAR char *argv[])
 {
   FAR struct kwork_wqueue_s *wqueue;
   FAR struct kworker_s *kworker;
-  FAR struct work_s *work;
-  worker_t worker;
-  irqstate_t flags;
-  FAR void *arg;
+  FAR struct work_s    *work;
path: sched/wqueue/kwork_thread.c
user: login: Fix-Point
body: Done.
created_at: 2025-04-19T01:55:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -59,37 +61,36 @@ static int work_qcancel(FAR struct kwork_wqueue_s *wqueue, bool sync,
    */
 
   flags = spin_lock_irqsave(&wqueue->lock);
-  if (work->worker != NULL)
+
+  /* Check whether we own the work structure. */
+
+  if (!work_available(work))
     {
-      /* Remove the entry from the work queue and make sure that it is
-       * marked as available (i.e., the worker field is nullified).
-       */
+      /* Seize the ownership from the work thread. */
 
+      worker = work->worker;
+      arg    = work->arg;
+
+      run_myself   = sync;
       work->worker = NULL;
-      wd_cancel(&work->u.timer);
-      list_delete(&work->u.s.node);
 
-      ret = OK;
+      list_delete(&work->node);
     }
-  else if (!up_interrupt_context() && !sched_idletask() && sync)
+
+  spin_unlock_irqrestore(&wqueue->lock, flags);
+
+  if (run_myself)
     {
-      int wndx;
-
-      for (wndx = 0; wndx < wqueue->nthreads; wndx++)
-        {
-          if (wqueue->worker[wndx].work == work &&
-              wqueue->worker[wndx].pid != nxsched_gettid())
path: sched/wqueue/kwork_cancel.c
user: login: Fix-Point
body: This block is not required anymore since the cancel thread will complete the unfinished work by itself.
created_at: 2025-04-19T02:20:07Z
author_association: CONTRIBUTOR
diff_hunk: @@ -148,30 +196,35 @@ static int work_thread(int argc, FAR char *argv[])
   kworker = (FAR struct kworker_s *)
             ((uintptr_t)strtoul(argv[2], NULL, 16));
 
-  flags = spin_lock_irqsave(&wqueue->lock);
-  sched_lock();
+  list_initialize(&q);
 
-  /* Loop forever */
+  /* Loop until wqueue->exit != 0.
+   * We do not need to enter the critical-section to access wqueue->exit.
+   * Because the only way to set wqueue->exit is to call work_queue_free().
+   */
 
   while (!wqueue->exit)
     {
+      irqstate_t flags = spin_lock_irqsave(&wqueue->lock);
+      sched_lock();
path: sched/wqueue/kwork_thread.c
user: login: Fix-Point
body: In next patch I'll try to optimize the synchronization.
created_at: 2025-04-19T02:21:12Z
author_association: CONTRIBUTOR
diff_hunk: @@ -110,6 +114,48 @@ struct lp_wqueue_s g_lpwork =
  * Private Functions
  ****************************************************************************/
 
+/* The work time expired callback will wake up the worker thread. */
+
+static void work_timer_expired(wdparm_t arg)
+{
+  FAR struct kwork_wqueue_s *wq = (FAR struct kwork_wqueue_s *)arg;
+  irqstate_t flags = spin_lock_irqsave(&wq->lock);
+  sched_lock();
+
+  if (wq->wait_count > 0)
path: sched/wqueue/kwork_thread.c
user: login: Fix-Point
body: OK. I'll create a new patch after this.
created_at: 2025-04-19T02:21:13Z
author_association: CONTRIBUTOR
diff_hunk: @@ -148,30 +196,35 @@ static int work_thread(int argc, FAR char *argv[])
   kworker = (FAR struct kworker_s *)
             ((uintptr_t)strtoul(argv[2], NULL, 16));
 
-  flags = spin_lock_irqsave(&wqueue->lock);
-  sched_lock();
+  list_initialize(&q);
 
-  /* Loop forever */
+  /* Loop until wqueue->exit != 0.
+   * We do not need to enter the critical-section to access wqueue->exit.
+   * Because the only way to set wqueue->exit is to call work_queue_free().
+   */
 
   while (!wqueue->exit)
     {
+      irqstate_t flags = spin_lock_irqsave(&wqueue->lock);
+      sched_lock();
+
       /* And check each entry in the work queue.  Since we have disabled
        * interrupts we know:  (1) we will not be suspended unless we do
        * so ourselves, and (2) there will be no changes to the work queue
        */
 
+      /* Check if there is expired delayed work */
+
+      ticks = clock_systime_ticks();
+      work_expiration(wqueue, &q, ticks);
path: sched/wqueue/kwork_thread.c
user: login: xiaoxiang781216
body: but how to wait until the callback finish in work_cancel_sync?
created_at: 2025-04-19T08:12:13Z
author_association: CONTRIBUTOR
diff_hunk: @@ -59,40 +61,36 @@ static int work_qcancel(FAR struct kwork_wqueue_s *wqueue, bool sync,
    */
 
   flags = spin_lock_irqsave(&wqueue->lock);
-  if (work->worker != NULL)
+
+  /* Check whether we own the work structure. */
+
+  if (!work_available(work))
     {
-      /* Remove the entry from the work queue and make sure that it is
-       * marked as available (i.e., the worker field is nullified).
-       */
+      /* Seize the ownership from the work thread. */
 
+      worker = work->worker;
+      arg    = work->arg;
+
+      run_myself   = sync;
       work->worker = NULL;
-      wd_cancel(&work->u.timer);
-      if (dq_inqueue((FAR dq_entry_t *)work, &wqueue->q))
-        {
-          dq_rem((FAR dq_entry_t *)work, &wqueue->q);
-        }
 
-      ret = OK;
+      list_delete(&work->node);
     }
-  else if (!up_interrupt_context() && !sched_idletask() && sync)
+
+  spin_unlock_irqrestore(&wqueue->lock, flags);
+
+  if (run_myself)
path: sched/wqueue/kwork_cancel.c
user: login: anchao
body: This is wrong. **cancel_sync** means waiting for the task being executed to end, rather than executing the call in the current task context. Such an implementation could easily cause deadlock.
created_at: 2025-04-20T08:27:39Z
author_association: CONTRIBUTOR
diff_hunk: @@ -155,51 +94,46 @@ int work_queue_period_wq(FAR struct kwork_wqueue_s *wqueue,
   flags = spin_lock_irqsave(&wqueue->lock);
   sched_lock();
 
-  /* Remove the entry from the timer and work queue. */
+  /* Check whether we own the work structure. */
 
-  if (work->worker != NULL)
+  if (!work_available(work))
     {
-      /* Remove the entry from the work queue and make sure that it is
-       * marked as available (i.e., the worker field is nullified).
-       */
+      /* Seize the ownership from the work thread. */
 
       work->worker = NULL;
-      wd_cancel(&work->u.timer);
-      if (dq_inqueue((FAR dq_entry_t *)work, &wqueue->q))
-        {
-          dq_rem((FAR dq_entry_t *)work, &wqueue->q);
-        }
-    }
 
-  if (work_is_canceling(wqueue->worker, wqueue->nthreads, work))
-    {
-      goto out;
+      list_delete(&work->node);
     }
 
   /* Initialize the work structure. */
 
   work->worker = worker;           /* Work callback. non-NULL means queued */
   work->arg    = arg;              /* Callback argument */
-  work->wq     = wqueue;           /* Work queue */
+  work->period = period;           /* Periodical delay */
 
   /* Queue the new work */
 
   if (!delay)
     {
-      queue_work(wqueue, work);
+      work->qtime = clock_systime_ticks();
     }
-  else if (period == 0)
+  else
     {
-      ret = wd_start(&work->u.timer, delay,
-                     work_timer_expiry, (wdparm_t)work);
+      work->qtime = clock_systime_ticks() + delay + 1;
     }
-  else
+
+  /* Insert to the workqueue's waiting list. */
+
+  work_insert_queue(wqueue, work);
+
+  /* Wakeup the wqueue thread. */
+
+  if (wqueue->wait_count > 0) /* There are threads waiting for sem. */
path: sched/wqueue/kwork_queue.c
user: login: anchao
body: no need to wake up the worker thread if the work does not expiration
created_at: 2025-04-20T08:32:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -216,18 +288,49 @@ static int work_thread(int argc, FAR char *argv[])
        * posted.
        */
 
+      /* Check the waiting queue */
+
+      has_next = false;
+
+      if (!list_is_empty(&wqueue->q))
+        {
+          work  = list_first_entry(&wqueue->q, struct work_s, node);
+
+          /* The work thread will go sleep until work->qtime */
+
+          ticks = work->qtime;
+          has_next = true;
+        }
+
       wqueue->wait_count++;
       spin_unlock_irqrestore(&wqueue->lock, flags);
       sched_unlock();
 
+      /* Set the earliest expired work delay */
+
+      if (has_next)
+        {
+          /* If the earliest work has already expired. */
+
+          if (clock_compare(ticks, clock_systime_ticks()))
+            {
+              /* Continue the work thread loop. */
+
+              continue;
+            }
+
+          /* Else we start a time to wake up the work thread. */
+
+          wd_start_abstick(&wqueue->timer, ticks, work_timer_expired,
path: sched/wqueue/kwork_thread.c
user: login: anchao
body: We should optimize the current wait strategy and wait only when there are no enqueue works or the timer is started, so that `wait_count` and `sem_post` in this function should be removed
created_at: 2025-04-20T08:52:12Z
author_association: CONTRIBUTOR
diff_hunk: @@ -59,40 +61,36 @@ static int work_qcancel(FAR struct kwork_wqueue_s *wqueue, bool sync,
    */
 
   flags = spin_lock_irqsave(&wqueue->lock);
-  if (work->worker != NULL)
+
+  /* Check whether we own the work structure. */
+
+  if (!work_available(work))
     {
-      /* Remove the entry from the work queue and make sure that it is
-       * marked as available (i.e., the worker field is nullified).
-       */
+      /* Seize the ownership from the work thread. */
 
+      worker = work->worker;
+      arg    = work->arg;
+
+      run_myself   = sync;
       work->worker = NULL;
-      wd_cancel(&work->u.timer);
-      if (dq_inqueue((FAR dq_entry_t *)work, &wqueue->q))
-        {
-          dq_rem((FAR dq_entry_t *)work, &wqueue->q);
-        }
 
-      ret = OK;
+      list_delete(&work->node);
     }
-  else if (!up_interrupt_context() && !sched_idletask() && sync)
+
+  spin_unlock_irqrestore(&wqueue->lock, flags);
+
+  if (run_myself)
path: sched/wqueue/kwork_cancel.c
user: login: Fix-Point
body: Fixed.
created_at: 2025-04-21T02:14:07Z
author_association: CONTRIBUTOR
diff_hunk: @@ -155,51 +94,46 @@ int work_queue_period_wq(FAR struct kwork_wqueue_s *wqueue,
   flags = spin_lock_irqsave(&wqueue->lock);
   sched_lock();
 
-  /* Remove the entry from the timer and work queue. */
+  /* Check whether we own the work structure. */
 
-  if (work->worker != NULL)
+  if (!work_available(work))
     {
-      /* Remove the entry from the work queue and make sure that it is
-       * marked as available (i.e., the worker field is nullified).
-       */
+      /* Seize the ownership from the work thread. */
 
       work->worker = NULL;
-      wd_cancel(&work->u.timer);
-      if (dq_inqueue((FAR dq_entry_t *)work, &wqueue->q))
-        {
-          dq_rem((FAR dq_entry_t *)work, &wqueue->q);
-        }
-    }
 
-  if (work_is_canceling(wqueue->worker, wqueue->nthreads, work))
-    {
-      goto out;
+      list_delete(&work->node);
     }
 
   /* Initialize the work structure. */
 
   work->worker = worker;           /* Work callback. non-NULL means queued */
   work->arg    = arg;              /* Callback argument */
-  work->wq     = wqueue;           /* Work queue */
+  work->period = period;           /* Periodical delay */
 
   /* Queue the new work */
 
   if (!delay)
     {
-      queue_work(wqueue, work);
+      work->qtime = clock_systime_ticks();
     }
-  else if (period == 0)
+  else
     {
-      ret = wd_start(&work->u.timer, delay,
-                     work_timer_expiry, (wdparm_t)work);
+      work->qtime = clock_systime_ticks() + delay + 1;
     }
-  else
+
+  /* Insert to the workqueue's waiting list. */
+
+  work_insert_queue(wqueue, work);
+
+  /* Wakeup the wqueue thread. */
+
+  if (wqueue->wait_count > 0) /* There are threads waiting for sem. */
path: sched/wqueue/kwork_queue.c
user: login: Fix-Point
body: Done.
created_at: 2025-04-21T02:14:14Z
author_association: CONTRIBUTOR
diff_hunk: @@ -216,18 +288,49 @@ static int work_thread(int argc, FAR char *argv[])
        * posted.
        */
 
+      /* Check the waiting queue */
+
+      has_next = false;
+
+      if (!list_is_empty(&wqueue->q))
+        {
+          work  = list_first_entry(&wqueue->q, struct work_s, node);
+
+          /* The work thread will go sleep until work->qtime */
+
+          ticks = work->qtime;
+          has_next = true;
+        }
+
       wqueue->wait_count++;
       spin_unlock_irqrestore(&wqueue->lock, flags);
       sched_unlock();
 
+      /* Set the earliest expired work delay */
+
+      if (has_next)
+        {
+          /* If the earliest work has already expired. */
+
+          if (clock_compare(ticks, clock_systime_ticks()))
+            {
+              /* Continue the work thread loop. */
+
+              continue;
+            }
+
+          /* Else we start a time to wake up the work thread. */
+
+          wd_start_abstick(&wqueue->timer, ticks, work_timer_expired,
path: sched/wqueue/kwork_thread.c
user: login: Fix-Point
body: I will try to optimize the synchronization in the next patch.
created_at: 2025-04-21T02:15:21Z
author_association: CONTRIBUTOR
diff_hunk: @@ -216,17 +278,54 @@ static int work_thread(int argc, FAR char *argv[])
        * posted.
        */
 
-      wqueue->wait_count++;
+      /* Check the waiting queue */
+
+      has_next = false;
+
+      if (!list_is_empty(&wqueue->pending))
+        {
+          work  = list_first_entry(&wqueue->pending, struct work_s, node);
+
+          /* The work thread will go sleep until work->qtime */
+
+          ticks = work->qtime;
+          has_next = true;
+        }
+
       spin_unlock_irqrestore(&wqueue->lock, flags);
       sched_unlock();
 
+      /* Set the earliest expired work delay */
+
+      if (has_next)
+        {
+          /* If the earliest work has already expired. */
+
+          if (clock_compare(ticks, clock_systime_ticks()))
path: sched/wqueue/kwork_thread.c
user: login: GUIDINGLI
body: the ticks base time is clock_systime_ticks() in line 208, and here must be has a continue ?
created_at: 2025-04-23T09:16:29Z
author_association: CONTRIBUTOR
diff_hunk: @@ -216,18 +288,49 @@ static int work_thread(int argc, FAR char *argv[])
        * posted.
        */
 
+      /* Check the waiting queue */
+
+      has_next = false;
+
+      if (!list_is_empty(&wqueue->q))
+        {
+          work  = list_first_entry(&wqueue->q, struct work_s, node);
+
+          /* The work thread will go sleep until work->qtime */
+
+          ticks = work->qtime;
+          has_next = true;
+        }
+
       wqueue->wait_count++;
       spin_unlock_irqrestore(&wqueue->lock, flags);
       sched_unlock();
 
+      /* Set the earliest expired work delay */
+
+      if (has_next)
+        {
+          /* If the earliest work has already expired. */
+
+          if (clock_compare(ticks, clock_systime_ticks()))
+            {
+              /* Continue the work thread loop. */
+
+              continue;
+            }
+
+          /* Else we start a time to wake up the work thread. */
+
+          wd_start_abstick(&wqueue->timer, ticks, work_timer_expired,
path: sched/wqueue/kwork_thread.c
user: login: GUIDINGLI
body: how about the NTHREDS > 1, and the list only has one, so the each thread will setup their own wdog(but the same value) to wake thread
created_at: 2025-04-23T09:20:21Z
author_association: CONTRIBUTOR
diff_hunk: @@ -216,17 +278,54 @@ static int work_thread(int argc, FAR char *argv[])
        * posted.
        */
 
-      wqueue->wait_count++;
+      /* Check the waiting queue */
+
+      has_next = false;
+
+      if (!list_is_empty(&wqueue->pending))
+        {
+          work  = list_first_entry(&wqueue->pending, struct work_s, node);
+
+          /* The work thread will go sleep until work->qtime */
+
+          ticks = work->qtime;
+          has_next = true;
+        }
+
       spin_unlock_irqrestore(&wqueue->lock, flags);
       sched_unlock();
 
+      /* Set the earliest expired work delay */
+
+      if (has_next)
+        {
+          /* If the earliest work has already expired. */
+
+          if (clock_compare(ticks, clock_systime_ticks()))
path: sched/wqueue/kwork_thread.c
user: login: Fix-Point
body: The semantic of `ticks` has changed if `has_next == true`. It represents the next expired time.
created_at: 2025-04-24T08:06:34Z
author_association: CONTRIBUTOR
diff_hunk: @@ -66,14 +66,15 @@ struct kworker_s
 
 struct kwork_wqueue_s
 {
-  struct dq_queue_s q;         /* The queue of pending work */
-  sem_t             sem;       /* The counting semaphore of the wqueue */
-  sem_t             exsem;     /* Sync waiting for thread exit */
-  spinlock_t        lock;      /* Spinlock */
-  uint8_t           nthreads;  /* Number of worker threads */
-  bool              exit;      /* A flag to request the thread to exit */
-  int16_t           wait_count;
-  struct kworker_s  worker[0]; /* Describes a worker thread */
+  struct list_node   ready;     /* The queue of ready work. */
path: sched/wqueue/wqueue.h
user: login: GUIDINGLI
body: so, can we use one list ?
created_at: 2025-04-27T08:42:14Z
author_association: CONTRIBUTOR
diff_hunk: @@ -141,65 +80,50 @@ int work_queue_period_wq(FAR struct kwork_wqueue_s *wqueue,
                          FAR void *arg, clock_t delay, clock_t period)
 {
   irqstate_t flags;
+  clock_t    expected;
   int ret = OK;
 
   if (wqueue == NULL || work == NULL || worker == NULL)
     {
       return -EINVAL;
     }
 
+  if (!delay)
+    {
+      expected = clock_systime_ticks();
+    }
+  else
+    {
+      expected = clock_systime_ticks() + delay + 1;
+    }
+
   /* Interrupts are disabled so that this logic can be called from with
    * task logic or from interrupt handling logic.
    */
 
   flags = spin_lock_irqsave(&wqueue->lock);
   sched_lock();
 
-  /* Remove the entry from the timer and work queue. */
+  /* Check whether we own the work structure. */
 
-  if (work->worker != NULL)
+  if (!work_available(work))
     {
-      /* Remove the entry from the work queue and make sure that it is
-       * marked as available (i.e., the worker field is nullified).
-       */
+      /* Seize the ownership from the work thread. */
 
-      work->worker = NULL;
-      wd_cancel(&work->u.timer);
-      if (dq_inqueue((FAR dq_entry_t *)work, &wqueue->q))
-        {
-          dq_rem((FAR dq_entry_t *)work, &wqueue->q);
-        }
-    }
-
-  if (work_is_canceling(wqueue->worker, wqueue->nthreads, work))
-    {
-      goto out;
+      list_delete(&work->node);
     }
 
   /* Initialize the work structure. */
 
-  work->worker = worker;           /* Work callback. non-NULL means queued */
-  work->arg    = arg;              /* Callback argument */
-  work->wq     = wqueue;           /* Work queue */
+  work->worker = worker;   /* Work callback. non-NULL means queued */
+  work->arg    = arg;      /* Callback argument */
+  work->qtime  = expected; /* Expected time */
+  work->period = period;   /* Periodical delay */
 
-  /* Queue the new work */
+  /* Insert to the pending list of the wqueue. */
 
-  if (!delay)
-    {
-      queue_work(wqueue, work);
-    }
-  else if (period == 0)
-    {
-      ret = wd_start(&work->u.timer, delay,
-                     work_timer_expiry, (wdparm_t)work);
-    }
-  else
-    {
-      ret = wd_start_period(&work->u.ptimer, delay, period,
-                            work_timer_expiry, (wdparm_t)work);
-    }
+  work_insert_pending(wqueue, work);
path: sched/wqueue/kwork_queue.c
user: login: GUIDINGLI
body: if the delay is zero, then directly add to ready list ?
created_at: 2025-04-28T08:55:25Z
author_association: CONTRIBUTOR
diff_hunk: @@ -141,65 +80,50 @@ int work_queue_period_wq(FAR struct kwork_wqueue_s *wqueue,
                          FAR void *arg, clock_t delay, clock_t period)
 {
   irqstate_t flags;
+  clock_t    expected;
   int ret = OK;
 
   if (wqueue == NULL || work == NULL || worker == NULL)
     {
       return -EINVAL;
     }
 
+  if (!delay)
+    {
+      expected = clock_systime_ticks();
+    }
+  else
+    {
+      expected = clock_systime_ticks() + delay + 1;
+    }
+
   /* Interrupts are disabled so that this logic can be called from with
    * task logic or from interrupt handling logic.
    */
 
   flags = spin_lock_irqsave(&wqueue->lock);
   sched_lock();
 
-  /* Remove the entry from the timer and work queue. */
+  /* Check whether we own the work structure. */
 
-  if (work->worker != NULL)
+  if (!work_available(work))
     {
-      /* Remove the entry from the work queue and make sure that it is
-       * marked as available (i.e., the worker field is nullified).
-       */
+      /* Seize the ownership from the work thread. */
 
-      work->worker = NULL;
-      wd_cancel(&work->u.timer);
-      if (dq_inqueue((FAR dq_entry_t *)work, &wqueue->q))
-        {
-          dq_rem((FAR dq_entry_t *)work, &wqueue->q);
-        }
-    }
-
-  if (work_is_canceling(wqueue->worker, wqueue->nthreads, work))
-    {
-      goto out;
+      list_delete(&work->node);
     }
 
   /* Initialize the work structure. */
 
-  work->worker = worker;           /* Work callback. non-NULL means queued */
-  work->arg    = arg;              /* Callback argument */
-  work->wq     = wqueue;           /* Work queue */
+  work->worker = worker;   /* Work callback. non-NULL means queued */
+  work->arg    = arg;      /* Callback argument */
+  work->qtime  = expected; /* Expected time */
+  work->period = period;   /* Periodical delay */
 
-  /* Queue the new work */
+  /* Insert to the pending list of the wqueue. */
 
-  if (!delay)
-    {
-      queue_work(wqueue, work);
-    }
-  else if (period == 0)
-    {
-      ret = wd_start(&work->u.timer, delay,
-                     work_timer_expiry, (wdparm_t)work);
-    }
-  else
-    {
-      ret = wd_start_period(&work->u.ptimer, delay, period,
-                            work_timer_expiry, (wdparm_t)work);
-    }
+  work_insert_pending(wqueue, work);
path: sched/wqueue/kwork_queue.c
user: login: Fix-Point
body: Done.
created_at: 2025-04-29T03:29:48Z
author_association: CONTRIBUTOR
diff_hunk: @@ -31,7 +31,7 @@
 
 #include <nuttx/irq.h>
 #include <nuttx/arch.h>
-#include <nuttx/queue.h>
+#include <nuttx/list.h>
path: sched/wqueue/kwork_cancel.c
user: login: xiaoxiang781216
body: move to the first patch
created_at: 2025-05-05T07:37:46Z
author_association: CONTRIBUTOR
diff_hunk: @@ -66,14 +66,15 @@ struct kworker_s
 
 struct kwork_wqueue_s
 {
-  struct list_node  q;         /* The queue of pending work */
-  sem_t             sem;       /* The counting semaphore of the wqueue */
-  sem_t             exsem;     /* Sync waiting for thread exit */
-  spinlock_t        lock;      /* Spinlock */
-  uint8_t           nthreads;  /* Number of worker threads */
-  bool              exit;      /* A flag to request the thread to exit */
-  int16_t           wait_count;
-  struct kworker_s  worker[0]; /* Describes a worker thread */
+  struct list_node   expired;   /* The queue of expired work. */
path: sched/wqueue/wqueue.h
user: login: xiaoxiang781216
body: remove the extra spaces
created_at: 2025-05-05T07:40:17Z
author_association: CONTRIBUTOR
diff_hunk: @@ -249,19 +249,11 @@ typedef CODE void (*worker_t)(FAR void *arg);
 
 struct work_s
 {
-  union
-  {
-    struct
-    {
-      struct list_node node;     /* Implements a double linked list */
-      clock_t qtime;             /* Time work queued */
-    } s;
-    struct wdog_s timer;         /* Delay expiry timer */
-    struct wdog_period_s ptimer; /* Period expiry timer */
-  } u;
-  worker_t  worker;              /* Work callback */
-  FAR void *arg;                 /* Callback argument */
-  FAR struct kwork_wqueue_s *wq; /* Work queue */
+  struct list_node  node;   /* Implements a double linked list */
path: include/nuttx/wqueue.h
user: login: xiaoxiang781216
body: remove the extra space
created_at: 2025-05-05T07:40:48Z
author_association: CONTRIBUTOR
diff_hunk: @@ -159,6 +148,65 @@ static inline_function FAR struct kwork_wqueue_s *work_qid2wq(int qid)
     }
 }
 
+/****************************************************************************
+ * Name: work_insert_pending
+ *
+ * Description:
+ *   Internal public function to insert the work to the workqueue.
+ *   Require wqueue != NULL and work != NULL.
+ *
+ * Input Parameters:
+ *   wqueue - The work queue.
+ *   work   - The work to be inserted.
+ *
+ * Returned Value:
+ *   Return whether the work is inserted at the head of the pending queue.
+ *
+ ****************************************************************************/
+
+static inline_function
+bool work_insert_pending(FAR struct kwork_wqueue_s *wqueue,
+                         FAR struct work_s         *work)
+{
+  struct work_s *curr;
+
+  DEBUGASSERT(wqueue != NULL && work != NULL);
+
+  /* Insert the work into the wait queue sorted by the expired time. */
+
+  list_for_every_entry(&wqueue->pending, curr, struct work_s, node)
+    {
+      if (!clock_compare(curr->qtime, work->qtime))
+        {
+          break;
+        }
+    }
+
+  /* After the insertion, we do not violate the invariant that
+   * the wait queue is sorted by the expired time. Because
+   * curr->qtime > work_qtime.
path: sched/wqueue/wqueue.h
user: login: xiaoxiang781216
body: work->qtime
created_at: 2025-05-05T07:48:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -159,6 +148,65 @@ static inline_function FAR struct kwork_wqueue_s *work_qid2wq(int qid)
     }
 }
 
+/****************************************************************************
+ * Name: work_insert_pending
+ *
+ * Description:
+ *   Internal public function to insert the work to the workqueue.
+ *   Require wqueue != NULL and work != NULL.
+ *
+ * Input Parameters:
+ *   wqueue - The work queue.
+ *   work   - The work to be inserted.
+ *
+ * Returned Value:
+ *   Return whether the work is inserted at the head of the pending queue.
+ *
+ ****************************************************************************/
+
+static inline_function
+bool work_insert_pending(FAR struct kwork_wqueue_s *wqueue,
+                         FAR struct work_s         *work)
+{
+  struct work_s *curr;
+
+  DEBUGASSERT(wqueue != NULL && work != NULL);
+
+  /* Insert the work into the wait queue sorted by the expired time. */
+
+  list_for_every_entry(&wqueue->pending, curr, struct work_s, node)
+    {
+      if (!clock_compare(curr->qtime, work->qtime))
+        {
+          break;
+        }
+    }
+
+  /* After the insertion, we do not violate the invariant that
+   * the wait queue is sorted by the expired time. Because
+   * curr->qtime > work_qtime.
+   * In the case of the wqueue is empty, we insert
+   * the work at the head of the wait queue.
+   */
+
+  list_add_before(&curr->node, &work->node);
+
+  return &curr->node == &wqueue->pending;
path: sched/wqueue/wqueue.h
user: login: xiaoxiang781216
body: call list_is_head
created_at: 2025-05-05T07:51:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -59,17 +59,37 @@ static int work_qcancel(FAR struct kwork_wqueue_s *wqueue, bool sync,
    */
 
   flags = spin_lock_irqsave(&wqueue->lock);
-  if (work->worker != NULL)
+
+  /* Check whether we own the work structure. */
+
+  if (!work_available(work))
     {
-      /* Remove the entry from the work queue and make sure that it is
-       * marked as available (i.e., the worker field is nullified).
-       */
+      bool is_head = list_is_head(&wqueue->pending, &work->node);
+
+      /* Seize the ownership from the work thread. */
 
       work->worker = NULL;
-      wd_cancel(&work->u.timer);
-      list_delete(&work->u.s.node);
 
-      ret = OK;
+      list_delete(&work->node);
+
+      /* If the head of the pending queue has changed, we should reset
+       * the wqueue timer.
+       */
+
+      if (is_head)
+        {
+          if (!list_is_empty(&wqueue->expired))
path: sched/wqueue/kwork_cancel.c
user: login: xiaoxiang781216
body: should we use pending list?
created_at: 2025-05-05T07:53:23Z
author_association: CONTRIBUTOR
diff_hunk: @@ -89,6 +109,7 @@ static int work_qcancel(FAR struct kwork_wqueue_s *wqueue, bool sync,
     }
 
   spin_unlock_irqrestore(&wqueue->lock, flags);
+
path: sched/wqueue/kwork_cancel.c
user: login: xiaoxiang781216
body: revert the change
created_at: 2025-05-05T07:54:41Z
author_association: CONTRIBUTOR
diff_hunk: @@ -148,33 +193,54 @@ static int work_thread(int argc, FAR char *argv[])
   kworker = (FAR struct kworker_s *)
             ((uintptr_t)strtoul(argv[2], NULL, 16));
 
-  flags = spin_lock_irqsave(&wqueue->lock);
-  sched_lock();
-
-  /* Loop forever */
+  /* Loop until wqueue->exit != 0.
+   * Since the only way to set wqueue->exit is to call work_queue_free(),
+   * there is no need for entering the critical section.
+   */
 
   while (!wqueue->exit)
     {
-      /* And check each entry in the work queue.  Since we have disabled
+      /* And check first entry in the work queue. Since we have disabled
        * interrupts we know:  (1) we will not be suspended unless we do
        * so ourselves, and (2) there will be no changes to the work queue
        */
 
-      /* Remove the ready-to-execute work from the list */
+      flags = spin_lock_irqsave(&wqueue->lock);
+      sched_lock();
+
+      /* If the wqueue timer is expired and non-active, it indicates that
+       * there might be expired work in the pending queue.
+       */
 
-      while (!list_is_empty(&wqueue->q))
+      if (!WDOG_ISACTIVE(&wqueue->timer))
         {
-          work = list_first_entry(&wqueue->q, struct work_s, u.s.node);
+          unsigned int wake_count = work_dispatch(wqueue);
 
-          list_delete(&work->u.s.node);
+          spin_unlock_irqrestore(&wqueue->lock, flags);
+          sched_unlock();
+
+          /* Note that the thread execution this function is also
+           * a worker thread, which has already been woken up by the timer.
+           * So only `count - 1` semaphore will be posted.
+           */
 
-          if (work->worker == NULL)
+          while (wake_count-- > 1)
             {
-              continue;
+              nxsem_post(&wqueue->sem);
             }
 
-          /* Extract the work description from the entry (in case the work
-           * instance will be re-used after it has been de-queued).
+          flags = spin_lock_irqsave(&wqueue->lock);
+          sched_lock();
+        }
+
+      if (!list_is_empty(&wqueue->expired))
+        {
+          work  = list_first_entry(&wqueue->expired, struct work_s, node);
path: sched/wqueue/kwork_thread.c
user: login: xiaoxiang781216
body: remove extra space before =
created_at: 2025-05-05T08:03:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -148,33 +193,54 @@ static int work_thread(int argc, FAR char *argv[])
   kworker = (FAR struct kworker_s *)
             ((uintptr_t)strtoul(argv[2], NULL, 16));
 
-  flags = spin_lock_irqsave(&wqueue->lock);
-  sched_lock();
-
-  /* Loop forever */
+  /* Loop until wqueue->exit != 0.
+   * Since the only way to set wqueue->exit is to call work_queue_free(),
+   * there is no need for entering the critical section.
+   */
 
   while (!wqueue->exit)
     {
-      /* And check each entry in the work queue.  Since we have disabled
+      /* And check first entry in the work queue. Since we have disabled
        * interrupts we know:  (1) we will not be suspended unless we do
        * so ourselves, and (2) there will be no changes to the work queue
        */
 
-      /* Remove the ready-to-execute work from the list */
+      flags = spin_lock_irqsave(&wqueue->lock);
+      sched_lock();
+
+      /* If the wqueue timer is expired and non-active, it indicates that
+       * there might be expired work in the pending queue.
+       */
 
-      while (!list_is_empty(&wqueue->q))
+      if (!WDOG_ISACTIVE(&wqueue->timer))
         {
-          work = list_first_entry(&wqueue->q, struct work_s, u.s.node);
+          unsigned int wake_count = work_dispatch(wqueue);
 
-          list_delete(&work->u.s.node);
+          spin_unlock_irqrestore(&wqueue->lock, flags);
+          sched_unlock();
+
+          /* Note that the thread execution this function is also
+           * a worker thread, which has already been woken up by the timer.
+           * So only `count - 1` semaphore will be posted.
+           */
 
-          if (work->worker == NULL)
+          while (wake_count-- > 1)
             {
-              continue;
+              nxsem_post(&wqueue->sem);
path: sched/wqueue/kwork_thread.c
user: login: xiaoxiang781216
body: why not move the nxsem_post to work_dispatch? which could avoid the scheduler try to scheduling in each post.
created_at: 2025-05-05T08:15:18Z
author_association: CONTRIBUTOR
diff_hunk: @@ -141,65 +80,72 @@ int work_queue_period_wq(FAR struct kwork_wqueue_s *wqueue,
                          FAR void *arg, clock_t delay, clock_t period)
 {
   irqstate_t flags;
-  int ret = OK;
+  clock_t    expected;
+  bool wake = false;
+  int  ret  = OK;
 
   if (wqueue == NULL || work == NULL || worker == NULL)
     {
       return -EINVAL;
     }
 
+  /* delay+1 is to prevent the insufficient sleep time if we are
+   * currently near the boundary to the next tick.
+   * | current_tick | current_tick + 1 | current_tick + 2 | .... |
+   * |           ^ Here we get the current tick
+   * In this case we delay 1 tick, timer will be triggered at
+   * current_tick + 1, which is not enough for at least 1 tick.
+   */
+
+  expected = clock_systime_ticks() + delay + 1;
+
   /* Interrupts are disabled so that this logic can be called from with
    * task logic or from interrupt handling logic.
    */
 
   flags = spin_lock_irqsave(&wqueue->lock);
-  sched_lock();
 
-  /* Remove the entry from the timer and work queue. */
+  /* Check whether we own the work structure. */
 
-  if (work->worker != NULL)
+  if (!work_available(work))
     {
-      /* Remove the entry from the work queue and make sure that it is
-       * marked as available (i.e., the worker field is nullified).
-       */
-
-      work->worker = NULL;
-      wd_cancel(&work->u.timer);
-
-      list_delete(&work->u.s.node);
-    }
+      /* Seize the ownership from the work thread. */
 
-  if (work_is_canceling(wqueue->worker, wqueue->nthreads, work))
path: sched/wqueue/kwork_queue.c
user: login: xiaoxiang781216
body: why remove this check
created_at: 2025-05-05T08:22:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -141,65 +80,72 @@ int work_queue_period_wq(FAR struct kwork_wqueue_s *wqueue,
                          FAR void *arg, clock_t delay, clock_t period)
 {
   irqstate_t flags;
-  int ret = OK;
+  clock_t    expected;
+  bool wake = false;
+  int  ret  = OK;
 
   if (wqueue == NULL || work == NULL || worker == NULL)
     {
       return -EINVAL;
     }
 
+  /* delay+1 is to prevent the insufficient sleep time if we are
+   * currently near the boundary to the next tick.
+   * | current_tick | current_tick + 1 | current_tick + 2 | .... |
+   * |           ^ Here we get the current tick
+   * In this case we delay 1 tick, timer will be triggered at
+   * current_tick + 1, which is not enough for at least 1 tick.
+   */
+
+  expected = clock_systime_ticks() + delay + 1;
+
   /* Interrupts are disabled so that this logic can be called from with
    * task logic or from interrupt handling logic.
    */
 
   flags = spin_lock_irqsave(&wqueue->lock);
-  sched_lock();
 
-  /* Remove the entry from the timer and work queue. */
+  /* Check whether we own the work structure. */
 
-  if (work->worker != NULL)
+  if (!work_available(work))
     {
-      /* Remove the entry from the work queue and make sure that it is
-       * marked as available (i.e., the worker field is nullified).
-       */
-
-      work->worker = NULL;
-      wd_cancel(&work->u.timer);
-
-      list_delete(&work->u.s.node);
-    }
+      /* Seize the ownership from the work thread. */
 
-  if (work_is_canceling(wqueue->worker, wqueue->nthreads, work))
-    {
-      goto out;
+      list_delete(&work->node);
path: sched/wqueue/kwork_queue.c
user: login: xiaoxiang781216
body: may change the  head of pending list, need restart wdog in this case
created_at: 2025-05-05T08:23:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -31,7 +31,7 @@
 
 #include <nuttx/irq.h>
 #include <nuttx/arch.h>
-#include <nuttx/queue.h>
+#include <nuttx/list.h>
path: sched/wqueue/kwork_cancel.c
user: login: Fix-Point
body: Done.
created_at: 2025-05-06T02:17:47Z
author_association: CONTRIBUTOR
diff_hunk: @@ -66,14 +66,15 @@ struct kworker_s
 
 struct kwork_wqueue_s
 {
-  struct list_node  q;         /* The queue of pending work */
-  sem_t             sem;       /* The counting semaphore of the wqueue */
-  sem_t             exsem;     /* Sync waiting for thread exit */
-  spinlock_t        lock;      /* Spinlock */
-  uint8_t           nthreads;  /* Number of worker threads */
-  bool              exit;      /* A flag to request the thread to exit */
-  int16_t           wait_count;
-  struct kworker_s  worker[0]; /* Describes a worker thread */
+  struct list_node   expired;   /* The queue of expired work. */
path: sched/wqueue/wqueue.h
user: login: Fix-Point
body: Done.
created_at: 2025-05-06T02:17:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -249,19 +249,11 @@ typedef CODE void (*worker_t)(FAR void *arg);
 
 struct work_s
 {
-  union
-  {
-    struct
-    {
-      struct list_node node;     /* Implements a double linked list */
-      clock_t qtime;             /* Time work queued */
-    } s;
-    struct wdog_s timer;         /* Delay expiry timer */
-    struct wdog_period_s ptimer; /* Period expiry timer */
-  } u;
-  worker_t  worker;              /* Work callback */
-  FAR void *arg;                 /* Callback argument */
-  FAR struct kwork_wqueue_s *wq; /* Work queue */
+  struct list_node  node;   /* Implements a double linked list */
path: include/nuttx/wqueue.h
user: login: Fix-Point
body: Done.
created_at: 2025-05-06T02:17:57Z
author_association: CONTRIBUTOR
diff_hunk: @@ -159,6 +148,65 @@ static inline_function FAR struct kwork_wqueue_s *work_qid2wq(int qid)
     }
 }
 
+/****************************************************************************
+ * Name: work_insert_pending
+ *
+ * Description:
+ *   Internal public function to insert the work to the workqueue.
+ *   Require wqueue != NULL and work != NULL.
+ *
+ * Input Parameters:
+ *   wqueue - The work queue.
+ *   work   - The work to be inserted.
+ *
+ * Returned Value:
+ *   Return whether the work is inserted at the head of the pending queue.
+ *
+ ****************************************************************************/
+
+static inline_function
+bool work_insert_pending(FAR struct kwork_wqueue_s *wqueue,
+                         FAR struct work_s         *work)
+{
+  struct work_s *curr;
+
+  DEBUGASSERT(wqueue != NULL && work != NULL);
+
+  /* Insert the work into the wait queue sorted by the expired time. */
+
+  list_for_every_entry(&wqueue->pending, curr, struct work_s, node)
+    {
+      if (!clock_compare(curr->qtime, work->qtime))
+        {
+          break;
+        }
+    }
+
+  /* After the insertion, we do not violate the invariant that
+   * the wait queue is sorted by the expired time. Because
+   * curr->qtime > work_qtime.
path: sched/wqueue/wqueue.h
user: login: Fix-Point
body: Done.
created_at: 2025-05-06T02:18:01Z
author_association: CONTRIBUTOR
diff_hunk: @@ -159,6 +148,65 @@ static inline_function FAR struct kwork_wqueue_s *work_qid2wq(int qid)
     }
 }
 
+/****************************************************************************
+ * Name: work_insert_pending
+ *
+ * Description:
+ *   Internal public function to insert the work to the workqueue.
+ *   Require wqueue != NULL and work != NULL.
+ *
+ * Input Parameters:
+ *   wqueue - The work queue.
+ *   work   - The work to be inserted.
+ *
+ * Returned Value:
+ *   Return whether the work is inserted at the head of the pending queue.
+ *
+ ****************************************************************************/
+
+static inline_function
+bool work_insert_pending(FAR struct kwork_wqueue_s *wqueue,
+                         FAR struct work_s         *work)
+{
+  struct work_s *curr;
+
+  DEBUGASSERT(wqueue != NULL && work != NULL);
+
+  /* Insert the work into the wait queue sorted by the expired time. */
+
+  list_for_every_entry(&wqueue->pending, curr, struct work_s, node)
+    {
+      if (!clock_compare(curr->qtime, work->qtime))
+        {
+          break;
+        }
+    }
+
+  /* After the insertion, we do not violate the invariant that
+   * the wait queue is sorted by the expired time. Because
+   * curr->qtime > work_qtime.
+   * In the case of the wqueue is empty, we insert
+   * the work at the head of the wait queue.
+   */
+
+  list_add_before(&curr->node, &work->node);
+
+  return &curr->node == &wqueue->pending;
path: sched/wqueue/wqueue.h
user: login: Fix-Point
body: We can check if the `curr_node` is the list, which is cache-friendly and can reduce one memory access comparing to call `list_is_head(&wqueue->pending, &work->node)`.
created_at: 2025-05-06T02:19:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -89,6 +109,7 @@ static int work_qcancel(FAR struct kwork_wqueue_s *wqueue, bool sync,
     }
 
   spin_unlock_irqrestore(&wqueue->lock, flags);
+
path: sched/wqueue/kwork_cancel.c
user: login: Fix-Point
body: Done.
created_at: 2025-05-06T02:19:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -148,33 +193,54 @@ static int work_thread(int argc, FAR char *argv[])
   kworker = (FAR struct kworker_s *)
             ((uintptr_t)strtoul(argv[2], NULL, 16));
 
-  flags = spin_lock_irqsave(&wqueue->lock);
-  sched_lock();
-
-  /* Loop forever */
+  /* Loop until wqueue->exit != 0.
+   * Since the only way to set wqueue->exit is to call work_queue_free(),
+   * there is no need for entering the critical section.
+   */
 
   while (!wqueue->exit)
     {
-      /* And check each entry in the work queue.  Since we have disabled
+      /* And check first entry in the work queue. Since we have disabled
        * interrupts we know:  (1) we will not be suspended unless we do
        * so ourselves, and (2) there will be no changes to the work queue
        */
 
-      /* Remove the ready-to-execute work from the list */
+      flags = spin_lock_irqsave(&wqueue->lock);
+      sched_lock();
+
+      /* If the wqueue timer is expired and non-active, it indicates that
+       * there might be expired work in the pending queue.
+       */
 
-      while (!list_is_empty(&wqueue->q))
+      if (!WDOG_ISACTIVE(&wqueue->timer))
         {
-          work = list_first_entry(&wqueue->q, struct work_s, u.s.node);
+          unsigned int wake_count = work_dispatch(wqueue);
 
-          list_delete(&work->u.s.node);
+          spin_unlock_irqrestore(&wqueue->lock, flags);
+          sched_unlock();
+
+          /* Note that the thread execution this function is also
+           * a worker thread, which has already been woken up by the timer.
+           * So only `count - 1` semaphore will be posted.
+           */
 
-          if (work->worker == NULL)
+          while (wake_count-- > 1)
             {
-              continue;
+              nxsem_post(&wqueue->sem);
             }
 
-          /* Extract the work description from the entry (in case the work
-           * instance will be re-used after it has been de-queued).
+          flags = spin_lock_irqsave(&wqueue->lock);
+          sched_lock();
+        }
+
+      if (!list_is_empty(&wqueue->expired))
+        {
+          work  = list_first_entry(&wqueue->expired, struct work_s, node);
path: sched/wqueue/kwork_thread.c
user: login: Fix-Point
body: Done.
created_at: 2025-05-06T02:19:48Z
author_association: CONTRIBUTOR
diff_hunk: @@ -148,33 +193,54 @@ static int work_thread(int argc, FAR char *argv[])
   kworker = (FAR struct kworker_s *)
             ((uintptr_t)strtoul(argv[2], NULL, 16));
 
-  flags = spin_lock_irqsave(&wqueue->lock);
-  sched_lock();
-
-  /* Loop forever */
+  /* Loop until wqueue->exit != 0.
+   * Since the only way to set wqueue->exit is to call work_queue_free(),
+   * there is no need for entering the critical section.
+   */
 
   while (!wqueue->exit)
     {
-      /* And check each entry in the work queue.  Since we have disabled
+      /* And check first entry in the work queue. Since we have disabled
        * interrupts we know:  (1) we will not be suspended unless we do
        * so ourselves, and (2) there will be no changes to the work queue
        */
 
-      /* Remove the ready-to-execute work from the list */
+      flags = spin_lock_irqsave(&wqueue->lock);
+      sched_lock();
+
+      /* If the wqueue timer is expired and non-active, it indicates that
+       * there might be expired work in the pending queue.
+       */
 
-      while (!list_is_empty(&wqueue->q))
+      if (!WDOG_ISACTIVE(&wqueue->timer))
         {
-          work = list_first_entry(&wqueue->q, struct work_s, u.s.node);
+          unsigned int wake_count = work_dispatch(wqueue);
 
-          list_delete(&work->u.s.node);
+          spin_unlock_irqrestore(&wqueue->lock, flags);
+          sched_unlock();
+
+          /* Note that the thread execution this function is also
+           * a worker thread, which has already been woken up by the timer.
+           * So only `count - 1` semaphore will be posted.
+           */
 
-          if (work->worker == NULL)
+          while (wake_count-- > 1)
             {
-              continue;
+              nxsem_post(&wqueue->sem);
path: sched/wqueue/kwork_thread.c
user: login: Fix-Point
body: If we moved this `nxsem_post` to the `work_dispatch`, we have to set a timer that immediately fires, which is less-efficient. Here we are doing what the timer callback should do to wake up at least 1 worker thread.
created_at: 2025-05-06T02:27:07Z
author_association: CONTRIBUTOR
diff_hunk: @@ -141,65 +80,72 @@ int work_queue_period_wq(FAR struct kwork_wqueue_s *wqueue,
                          FAR void *arg, clock_t delay, clock_t period)
 {
   irqstate_t flags;
-  int ret = OK;
+  clock_t    expected;
+  bool wake = false;
+  int  ret  = OK;
 
   if (wqueue == NULL || work == NULL || worker == NULL)
     {
       return -EINVAL;
     }
 
+  /* delay+1 is to prevent the insufficient sleep time if we are
+   * currently near the boundary to the next tick.
+   * | current_tick | current_tick + 1 | current_tick + 2 | .... |
+   * |           ^ Here we get the current tick
+   * In this case we delay 1 tick, timer will be triggered at
+   * current_tick + 1, which is not enough for at least 1 tick.
+   */
+
+  expected = clock_systime_ticks() + delay + 1;
+
   /* Interrupts are disabled so that this logic can be called from with
    * task logic or from interrupt handling logic.
    */
 
   flags = spin_lock_irqsave(&wqueue->lock);
-  sched_lock();
 
-  /* Remove the entry from the timer and work queue. */
+  /* Check whether we own the work structure. */
 
-  if (work->worker != NULL)
+  if (!work_available(work))
     {
-      /* Remove the entry from the work queue and make sure that it is
-       * marked as available (i.e., the worker field is nullified).
-       */
-
-      work->worker = NULL;
-      wd_cancel(&work->u.timer);
-
-      list_delete(&work->u.s.node);
-    }
+      /* Seize the ownership from the work thread. */
 
-  if (work_is_canceling(wqueue->worker, wqueue->nthreads, work))
-    {
-      goto out;
+      list_delete(&work->node);
path: sched/wqueue/kwork_queue.c
user: login: Fix-Point
body: Done.
created_at: 2025-05-06T02:27:19Z
author_association: CONTRIBUTOR
diff_hunk: @@ -141,65 +80,72 @@ int work_queue_period_wq(FAR struct kwork_wqueue_s *wqueue,
                          FAR void *arg, clock_t delay, clock_t period)
 {
   irqstate_t flags;
-  int ret = OK;
+  clock_t    expected;
+  bool wake = false;
+  int  ret  = OK;
 
   if (wqueue == NULL || work == NULL || worker == NULL)
     {
       return -EINVAL;
     }
 
+  /* delay+1 is to prevent the insufficient sleep time if we are
+   * currently near the boundary to the next tick.
+   * | current_tick | current_tick + 1 | current_tick + 2 | .... |
+   * |           ^ Here we get the current tick
+   * In this case we delay 1 tick, timer will be triggered at
+   * current_tick + 1, which is not enough for at least 1 tick.
+   */
+
+  expected = clock_systime_ticks() + delay + 1;
+
   /* Interrupts are disabled so that this logic can be called from with
    * task logic or from interrupt handling logic.
    */
 
   flags = spin_lock_irqsave(&wqueue->lock);
-  sched_lock();
 
-  /* Remove the entry from the timer and work queue. */
+  /* Check whether we own the work structure. */
 
-  if (work->worker != NULL)
+  if (!work_available(work))
     {
-      /* Remove the entry from the work queue and make sure that it is
-       * marked as available (i.e., the worker field is nullified).
-       */
-
-      work->worker = NULL;
-      wd_cancel(&work->u.timer);
-
-      list_delete(&work->u.s.node);
-    }
+      /* Seize the ownership from the work thread. */
 
-  if (work_is_canceling(wqueue->worker, wqueue->nthreads, work))
path: sched/wqueue/kwork_queue.c
user: login: Fix-Point
body: Done.
created_at: 2025-05-06T02:27:25Z
author_association: CONTRIBUTOR
diff_hunk: @@ -59,17 +59,37 @@ static int work_qcancel(FAR struct kwork_wqueue_s *wqueue, bool sync,
    */
 
   flags = spin_lock_irqsave(&wqueue->lock);
-  if (work->worker != NULL)
+
+  /* Check whether we own the work structure. */
+
+  if (!work_available(work))
     {
-      /* Remove the entry from the work queue and make sure that it is
-       * marked as available (i.e., the worker field is nullified).
-       */
+      bool is_head = list_is_head(&wqueue->pending, &work->node);
+
+      /* Seize the ownership from the work thread. */
 
       work->worker = NULL;
-      wd_cancel(&work->u.timer);
-      list_delete(&work->u.s.node);
 
-      ret = OK;
+      list_delete(&work->node);
+
+      /* If the head of the pending queue has changed, we should reset
+       * the wqueue timer.
+       */
+
+      if (is_head)
+        {
+          if (!list_is_empty(&wqueue->expired))
path: sched/wqueue/kwork_cancel.c
user: login: xiaoxiang781216
body: no change
created_at: 2025-05-06T03:06:30Z
author_association: CONTRIBUTOR
diff_hunk: @@ -159,6 +148,65 @@ static inline_function FAR struct kwork_wqueue_s *work_qid2wq(int qid)
     }
 }
 
+/****************************************************************************
+ * Name: work_insert_pending
+ *
+ * Description:
+ *   Internal public function to insert the work to the workqueue.
+ *   Require wqueue != NULL and work != NULL.
+ *
+ * Input Parameters:
+ *   wqueue - The work queue.
+ *   work   - The work to be inserted.
+ *
+ * Returned Value:
+ *   Return whether the work is inserted at the head of the pending queue.
+ *
+ ****************************************************************************/
+
+static inline_function
+bool work_insert_pending(FAR struct kwork_wqueue_s *wqueue,
+                         FAR struct work_s         *work)
+{
+  struct work_s *curr;
+
+  DEBUGASSERT(wqueue != NULL && work != NULL);
+
+  /* Insert the work into the wait queue sorted by the expired time. */
+
+  list_for_every_entry(&wqueue->pending, curr, struct work_s, node)
+    {
+      if (!clock_compare(curr->qtime, work->qtime))
+        {
+          break;
+        }
+    }
+
+  /* After the insertion, we do not violate the invariant that
+   * the wait queue is sorted by the expired time. Because
+   * curr->qtime > work_qtime.
+   * In the case of the wqueue is empty, we insert
+   * the work at the head of the wait queue.
+   */
+
+  list_add_before(&curr->node, &work->node);
+
+  return &curr->node == &wqueue->pending;
path: sched/wqueue/wqueue.h
user: login: xiaoxiang781216
body: but you always touch work at line 192
created_at: 2025-05-06T03:11:23Z
author_association: CONTRIBUTOR
diff_hunk: @@ -148,33 +193,54 @@ static int work_thread(int argc, FAR char *argv[])
   kworker = (FAR struct kworker_s *)
             ((uintptr_t)strtoul(argv[2], NULL, 16));
 
-  flags = spin_lock_irqsave(&wqueue->lock);
-  sched_lock();
-
-  /* Loop forever */
+  /* Loop until wqueue->exit != 0.
+   * Since the only way to set wqueue->exit is to call work_queue_free(),
+   * there is no need for entering the critical section.
+   */
 
   while (!wqueue->exit)
     {
-      /* And check each entry in the work queue.  Since we have disabled
+      /* And check first entry in the work queue. Since we have disabled
        * interrupts we know:  (1) we will not be suspended unless we do
        * so ourselves, and (2) there will be no changes to the work queue
        */
 
-      /* Remove the ready-to-execute work from the list */
+      flags = spin_lock_irqsave(&wqueue->lock);
+      sched_lock();
+
+      /* If the wqueue timer is expired and non-active, it indicates that
+       * there might be expired work in the pending queue.
+       */
 
-      while (!list_is_empty(&wqueue->q))
+      if (!WDOG_ISACTIVE(&wqueue->timer))
         {
-          work = list_first_entry(&wqueue->q, struct work_s, u.s.node);
+          unsigned int wake_count = work_dispatch(wqueue);
 
-          list_delete(&work->u.s.node);
+          spin_unlock_irqrestore(&wqueue->lock, flags);
+          sched_unlock();
+
+          /* Note that the thread execution this function is also
+           * a worker thread, which has already been woken up by the timer.
+           * So only `count - 1` semaphore will be posted.
+           */
 
-          if (work->worker == NULL)
+          while (wake_count-- > 1)
             {
-              continue;
+              nxsem_post(&wqueue->sem);
path: sched/wqueue/kwork_thread.c
user: login: xiaoxiang781216
body: but it isn't truth, since sched_lock is holding during the whole life time of work_dispatch
created_at: 2025-05-06T03:13:41Z
author_association: CONTRIBUTOR
diff_hunk: @@ -159,6 +148,65 @@ static inline_function FAR struct kwork_wqueue_s *work_qid2wq(int qid)
     }
 }
 
+/****************************************************************************
+ * Name: work_insert_pending
+ *
+ * Description:
+ *   Internal public function to insert the work to the workqueue.
+ *   Require wqueue != NULL and work != NULL.
+ *
+ * Input Parameters:
+ *   wqueue - The work queue.
+ *   work   - The work to be inserted.
+ *
+ * Returned Value:
+ *   Return whether the work is inserted at the head of the pending queue.
+ *
+ ****************************************************************************/
+
+static inline_function
+bool work_insert_pending(FAR struct kwork_wqueue_s *wqueue,
+                         FAR struct work_s         *work)
+{
+  struct work_s *curr;
+
+  DEBUGASSERT(wqueue != NULL && work != NULL);
+
+  /* Insert the work into the wait queue sorted by the expired time. */
+
+  list_for_every_entry(&wqueue->pending, curr, struct work_s, node)
+    {
+      if (!clock_compare(curr->qtime, work->qtime))
+        {
+          break;
+        }
+    }
+
+  /* After the insertion, we do not violate the invariant that
+   * the wait queue is sorted by the expired time. Because
+   * curr->qtime > work_qtime.
+   * In the case of the wqueue is empty, we insert
+   * the work at the head of the wait queue.
+   */
+
+  list_add_before(&curr->node, &work->node);
+
+  return &curr->node == &wqueue->pending;
path: sched/wqueue/wqueue.h
user: login: Fix-Point
body: `list_is_head(list, item)` will be expanded to `((list)->next == (item))`. The work node will not be accessed. We are not supposed to access `wqueue->pending.next` since it is already cold.
created_at: 2025-05-06T03:19:25Z
author_association: CONTRIBUTOR
diff_hunk: @@ -148,33 +193,54 @@ static int work_thread(int argc, FAR char *argv[])
   kworker = (FAR struct kworker_s *)
             ((uintptr_t)strtoul(argv[2], NULL, 16));
 
-  flags = spin_lock_irqsave(&wqueue->lock);
-  sched_lock();
-
-  /* Loop forever */
+  /* Loop until wqueue->exit != 0.
+   * Since the only way to set wqueue->exit is to call work_queue_free(),
+   * there is no need for entering the critical section.
+   */
 
   while (!wqueue->exit)
     {
-      /* And check each entry in the work queue.  Since we have disabled
+      /* And check first entry in the work queue. Since we have disabled
        * interrupts we know:  (1) we will not be suspended unless we do
        * so ourselves, and (2) there will be no changes to the work queue
        */
 
-      /* Remove the ready-to-execute work from the list */
+      flags = spin_lock_irqsave(&wqueue->lock);
+      sched_lock();
+
+      /* If the wqueue timer is expired and non-active, it indicates that
+       * there might be expired work in the pending queue.
+       */
 
-      while (!list_is_empty(&wqueue->q))
+      if (!WDOG_ISACTIVE(&wqueue->timer))
         {
-          work = list_first_entry(&wqueue->q, struct work_s, u.s.node);
+          unsigned int wake_count = work_dispatch(wqueue);
 
-          list_delete(&work->u.s.node);
+          spin_unlock_irqrestore(&wqueue->lock, flags);
+          sched_unlock();
+
+          /* Note that the thread execution this function is also
+           * a worker thread, which has already been woken up by the timer.
+           * So only `count - 1` semaphore will be posted.
+           */
 
-          if (work->worker == NULL)
+          while (wake_count-- > 1)
             {
-              continue;
+              nxsem_post(&wqueue->sem);
path: sched/wqueue/kwork_thread.c
user: login: Fix-Point
body: Done.
created_at: 2025-05-06T03:25:38Z
author_association: CONTRIBUTOR
diff_hunk: @@ -59,17 +59,37 @@ static int work_qcancel(FAR struct kwork_wqueue_s *wqueue, bool sync,
    */
 
   flags = spin_lock_irqsave(&wqueue->lock);
-  if (work->worker != NULL)
+
+  /* Check whether we own the work structure. */
+
+  if (!work_available(work))
     {
-      /* Remove the entry from the work queue and make sure that it is
-       * marked as available (i.e., the worker field is nullified).
-       */
+      bool is_head = list_is_head(&wqueue->pending, &work->node);
+
+      /* Seize the ownership from the work thread. */
 
       work->worker = NULL;
-      wd_cancel(&work->u.timer);
-      list_delete(&work->u.s.node);
 
-      ret = OK;
+      list_delete(&work->node);
+
+      /* If the head of the pending queue has changed, we should reset
+       * the wqueue timer.
+       */
+
+      if (is_head)
+        {
+          if (!list_is_empty(&wqueue->expired))
path: sched/wqueue/kwork_cancel.c
user: login: Fix-Point
body: Done.
created_at: 2025-05-06T03:28:49Z
author_association: CONTRIBUTOR
diff_hunk: @@ -159,6 +148,65 @@ static inline_function FAR struct kwork_wqueue_s *work_qid2wq(int qid)
     }
 }
 
+/****************************************************************************
+ * Name: work_insert_pending
+ *
+ * Description:
+ *   Internal public function to insert the work to the workqueue.
+ *   Require wqueue != NULL and work != NULL.
+ *
+ * Input Parameters:
+ *   wqueue - The work queue.
+ *   work   - The work to be inserted.
+ *
+ * Returned Value:
+ *   Return whether the work is inserted at the head of the pending queue.
+ *
+ ****************************************************************************/
+
+static inline_function
+bool work_insert_pending(FAR struct kwork_wqueue_s *wqueue,
+                         FAR struct work_s         *work)
+{
+  struct work_s *curr;
+
+  DEBUGASSERT(wqueue != NULL && work != NULL);
+
+  /* Insert the work into the wait queue sorted by the expired time. */
+
+  list_for_every_entry(&wqueue->pending, curr, struct work_s, node)
+    {
+      if (!clock_compare(curr->qtime, work->qtime))
+        {
+          break;
+        }
+    }
+
+  /* After the insertion, we do not violate the invariant that
+   * the wait queue is sorted by the expired time. Because
+   * curr->qtime > work_qtime.
+   * In the case of the wqueue is empty, we insert
+   * the work at the head of the wait queue.
+   */
+
+  list_add_before(&curr->node, &work->node);
+
+  return &curr->node == &wqueue->pending;
path: sched/wqueue/wqueue.h
user: login: xiaoxiang781216
body: Ok.
created_at: 2025-05-06T03:36:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -110,6 +116,53 @@ struct lp_wqueue_s g_lpwork =
  * Private Functions
  ****************************************************************************/
 
+static inline_function
+void work_dispatch(FAR struct kwork_wqueue_s *wq)
+{
+  FAR struct work_s *work;
+  FAR struct work_s *next;
+  unsigned int count = 0;
path: sched/wqueue/kwork_thread.c
user: login: xiaoxiang781216
body: let's remove the count and call nxsem_post in the loop directly
created_at: 2025-05-06T03:37:12Z
author_association: CONTRIBUTOR
diff_hunk: @@ -89,7 +89,7 @@ static int work_qcancel(FAR struct usr_wqueue_s *wqueue,
        */
 
       curr = wqueue->q.head;
-      while (curr && curr != &work->u.s.dq)
+      while (curr && curr != (FAR dq_entry_t *)&work->node)
path: libs/libc/wqueue/work_cancel.c
user: login: xiaoxiang781216
body: let's use list_node for usr_wqueue_s in the first patch too
created_at: 2025-05-06T03:38:40Z
author_association: CONTRIBUTOR
diff_hunk: @@ -87,17 +87,17 @@ static int work_qqueue(FAR struct usr_wqueue_s *wqueue,
 
   /* Initialize the work structure */
 
-  work->worker = worker;             /* Work callback. non-NULL means queued */
-  work->arg    = arg;                /* Callback argument */
-  work->u.s.qtime = clock() + delay; /* Delay until work performed */
+  work->worker = worker;          /* Work callback. non-NULL means queued */
+  work->arg    = arg;             /* Callback argument */
+  work->qtime  = clock() + delay; /* Delay until work performed */
 
   /* Do the easy case first -- when the work queue is empty. */
 
   if (wqueue->q.head == NULL)
path: libs/libc/wqueue/work_queue.c
user: login: xiaoxiang781216
body: need change from dq_entry_s to list_node in usr_wqueue_s at:
https://github.com/apache/nuttx/blob/master/libs/libc/wqueue/wqueue.h#L52
created_at: 2025-05-06T05:42:09Z
author_association: CONTRIBUTOR
diff_hunk: @@ -136,7 +136,7 @@ static void work_process(FAR struct usr_wqueue_s *wqueue)
           dq_remfirst(&wqueue->q);
path: libs/libc/wqueue/work_usrthread.c
user: login: xiaoxiang781216
body: need change all dq_xxx to list_xxx in this file
created_at: 2025-05-06T05:45:07Z
author_association: CONTRIBUTOR
diff_hunk: @@ -89,7 +89,7 @@ static int work_qcancel(FAR struct usr_wqueue_s *wqueue,
        */
 
       curr = wqueue->q.head;
-      while (curr && curr != &work->u.s.dq)
+      while (curr && curr != (FAR dq_entry_t *)&work->node)
path: libs/libc/wqueue/work_cancel.c
user: login: Fix-Point
body: Done.
created_at: 2025-05-06T07:51:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -87,17 +87,17 @@ static int work_qqueue(FAR struct usr_wqueue_s *wqueue,
 
   /* Initialize the work structure */
 
-  work->worker = worker;             /* Work callback. non-NULL means queued */
-  work->arg    = arg;                /* Callback argument */
-  work->u.s.qtime = clock() + delay; /* Delay until work performed */
+  work->worker = worker;          /* Work callback. non-NULL means queued */
+  work->arg    = arg;             /* Callback argument */
+  work->qtime  = clock() + delay; /* Delay until work performed */
 
   /* Do the easy case first -- when the work queue is empty. */
 
   if (wqueue->q.head == NULL)
path: libs/libc/wqueue/work_queue.c
user: login: Fix-Point
body: Done.
created_at: 2025-05-06T07:51:57Z
author_association: CONTRIBUTOR
diff_hunk: @@ -136,7 +136,7 @@ static void work_process(FAR struct usr_wqueue_s *wqueue)
           dq_remfirst(&wqueue->q);
path: libs/libc/wqueue/work_usrthread.c
user: login: Fix-Point
body: Done.
created_at: 2025-05-06T07:52:00Z
author_association: CONTRIBUTOR

PR comments:
user: login: nuttxpr
created_at: 2025-04-17T07:09:53Z
body: [**\[Experimental Bot, please feedback here\]**](https://github.com/search?q=repo%3Aapache%2Fnuttx+13552&type=issues)



The PR summary is good, clearly explaining the "why" and "how".  However, it lacks crucial cross-referencing to a NuttX issue.  This is mandatory.  The submitter *must* create an issue first and link the PR to it.

The Impact section is too vague.  While it mentions affected modules, it doesn't detail the *specific* impacts as requested by the template.  The template explicitly asks for YES/NO answers for each category with descriptions.  The author needs to address each point individually (Impact on user, build, hardware, documentation, security, compatibility) with YES/NO and explanations. For example, even if the impact on the user is minimal, they should state "NO" followed by a brief justification like "Existing user code using workqueues should continue to function as expected with potentially slightly different timing."

The Testing section is insufficient.  While it mentions the test used (`wqueue_test`), it lacks vital information.  The template *requires* logs before *and* after the change.  Simply stating "Periodical workqueue can work now" is not enough.  Concrete evidence of improvement or correct functionality is necessary through logs.  The Build Host details are also missing (OS, compiler version, etc.).  Furthermore, testing on only one platform (QEMU/x86_64) is likely insufficient for a core change like this.  More architectures and boards should be tested.


**Therefore, this PR does *not* fully meet the NuttX requirements.**  The author needs to:

1. **Link to a NuttX Issue:** Create a corresponding issue in the NuttX issue tracker and reference it in the PR summary.
2. **Elaborate on Impact:** Provide specific YES/NO answers for each impact category with detailed explanations.
3. **Improve Testing:**  Include "before" and "after" logs, specify the build host environment details, and ideally test on more platforms/architectures.


By addressing these points, the PR will be much stronger and have a higher chance of being accepted.

author_association: NONE
user: login: acassis
created_at: 2025-04-17T15:17:09Z
body: @Fix-Point @xiaoxiang781216 I think it should be tested on real hardware as well. Normally small changes on core features like this could break the system. I think it requires test on sensors (APDS9960) and devices that uses workqueue.
author_association: CONTRIBUTOR
user: login: fdcavalcanti
created_at: 2025-04-30T11:42:24Z
body: I'll run this through Espressif's CI and see if anything comes up.
author_association: CONTRIBUTOR
user: login: fdcavalcanti
created_at: 2025-04-30T13:27:35Z
body: Most tests look ok, however `sotest` defconfig is failing to build across many SoCs due to some LIBC errors, which I suspect is due to the apps directory not in sync. Some other configs are failing on the `refresh.sh` script.

Can you rebase this change? That would give us better results.

author_association: CONTRIBUTOR
user: login: fdcavalcanti
created_at: 2025-05-01T16:40:01Z
body: I've found no issues on Espressif's CI test results.
author_association: CONTRIBUTOR
user: login: xiaoxiang781216
created_at: 2025-05-06T10:18:49Z
body: @Fix-Point please fix:
```
/home/runner/work/nuttx/nuttx/nuttx/sched/wdog/wd_start.c:297: Absoulute ==> Absolute
``
author_association: CONTRIBUTOR