This is an automated email from the ASF dual-hosted git repository.

xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git


The following commit(s) were added to refs/heads/master by this push:
     new befe29801f sem: change sem wait to atomic operation
befe29801f is described below

commit befe29801ff50494eada535906369f89eb4f8424
Author: zhangyuan29 <[email protected]>
AuthorDate: Thu Aug 22 10:25:32 2024 +0800

    sem: change sem wait to atomic operation
    
    Add sem_wait fast operations, use atomic to ensure
    atomicity of semcount operations, and do not depend
    on critical section.
    
    Test with robot:
    before modify:
    nxmutex_lock cost: 78 ns
    nxmutex_unlock cost: 82 ns
    
    after modify:
    nxmutex_lock cost: 28 ns
    nxmutex_unlock cost: 14 ns
    
    Signed-off-by: zhangyuan29 <[email protected]>
---
 sched/semaphore/sem_destroy.c |  14 ++++-
 sched/semaphore/sem_holder.c  |   4 +-
 sched/semaphore/sem_post.c    |  72 ++++++++++++++++++++++----
 sched/semaphore/sem_recover.c |   4 +-
 sched/semaphore/sem_reset.c   |  15 ++++--
 sched/semaphore/sem_trywait.c | 115 ++++++++++++++++++++++++++++++------------
 sched/semaphore/sem_wait.c    |  82 +++++++++++++++++++++++-------
 sched/semaphore/sem_waitirq.c |   4 +-
 sched/semaphore/semaphore.h   |   7 +++
 9 files changed, 249 insertions(+), 68 deletions(-)

diff --git a/sched/semaphore/sem_destroy.c b/sched/semaphore/sem_destroy.c
index f207b339bb..fa94ef92ad 100644
--- a/sched/semaphore/sem_destroy.c
+++ b/sched/semaphore/sem_destroy.c
@@ -60,6 +60,8 @@
 
 int nxsem_destroy(FAR sem_t *sem)
 {
+  short old;
+
   DEBUGASSERT(sem != NULL);
 
   /* There is really no particular action that we need
@@ -72,10 +74,18 @@ int nxsem_destroy(FAR sem_t *sem)
    * leave the count unchanged but still return OK.
    */
 
-  if (sem->semcount >= 0)
+  do
     {
-      sem->semcount = 1;
+      old = atomic_load(NXSEM_COUNT(sem));
+      if (old < 0)
+        {
+          break;
+        }
     }
+  while (!atomic_compare_exchange_weak_explicit(NXSEM_COUNT(sem),
+                                                &old, 1,
+                                                memory_order_release,
+                                                memory_order_relaxed));
 
   /* Release holders of the semaphore */
 
diff --git a/sched/semaphore/sem_holder.c b/sched/semaphore/sem_holder.c
index 8fe2b3b52a..0c23f8aabf 100644
--- a/sched/semaphore/sem_holder.c
+++ b/sched/semaphore/sem_holder.c
@@ -886,7 +886,7 @@ void nxsem_canceled(FAR struct tcb_s *stcb, FAR sem_t *sem)
 {
   /* Check our assumptions */
 
-  DEBUGASSERT(sem->semcount <= 0);
+  DEBUGASSERT(atomic_load(NXSEM_COUNT(sem)) <= 0);
 
   /* Adjust the priority of every holder as necessary */
 
@@ -984,7 +984,7 @@ void nxsem_release_all(FAR struct tcb_s *htcb)
        * that was taken by sem_wait() or sem_post().
        */
 
-      sem->semcount++;
+      atomic_fetch_add(NXSEM_COUNT(sem), 1);
     }
 }
 
diff --git a/sched/semaphore/sem_post.c b/sched/semaphore/sem_post.c
index 33293ad037..500b00f595 100644
--- a/sched/semaphore/sem_post.c
+++ b/sched/semaphore/sem_post.c
@@ -37,11 +37,11 @@
 #include "semaphore/semaphore.h"
 
 /****************************************************************************
- * Public Functions
+ * Private Functions
  ****************************************************************************/
 
 /****************************************************************************
- * Name: nxsem_post
+ * Name: nxsem_post_slow
  *
  * Description:
  *   When a kernel thread has finished with a semaphore, it will call
@@ -69,7 +69,7 @@
  *
  ****************************************************************************/
 
-int nxsem_post(FAR sem_t *sem)
+static int nxsem_post_slow(FAR sem_t *sem)
 {
   FAR struct tcb_s *stcb = NULL;
   irqstate_t flags;
@@ -78,8 +78,6 @@ int nxsem_post(FAR sem_t *sem)
   uint8_t proto;
 #endif
 
-  DEBUGASSERT(sem != NULL);
-
   /* The following operations must be performed with interrupts
    * disabled because sem_post() may be called from an interrupt
    * handler.
@@ -87,12 +85,13 @@ int nxsem_post(FAR sem_t *sem)
 
   flags = enter_critical_section();
 
-  sem_count = sem->semcount;
+  sem_count = atomic_fetch_add(NXSEM_COUNT(sem), 1);
 
   /* Check the maximum allowable value */
 
   if (sem_count >= SEM_VALUE_MAX)
     {
+      atomic_fetch_sub(NXSEM_COUNT(sem), 1);
       leave_critical_section(flags);
       return -EOVERFLOW;
     }
@@ -115,8 +114,6 @@ int nxsem_post(FAR sem_t *sem)
    */
 
   nxsem_release_holder(sem);
-  sem_count++;
-  sem->semcount = sem_count;
 
 #if defined(CONFIG_PRIORITY_INHERITANCE) || defined(CONFIG_PRIORITY_PROTECT)
   /* Don't let any unblocked tasks run until we complete any priority
@@ -138,7 +135,7 @@ int nxsem_post(FAR sem_t *sem)
    * there must be some task waiting for the semaphore.
    */
 
-  if (sem_count <= 0)
+  if (sem_count < 0)
     {
       /* Check if there are any tasks in the waiting for semaphore
        * task list that are waiting for this semaphore.  This is a
@@ -217,3 +214,60 @@ int nxsem_post(FAR sem_t *sem)
 
   return OK;
 }
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nxsem_post
+ *
+ * Description:
+ *   When a kernel thread has finished with a semaphore, it will call
+ *   nxsem_post().  This function unlocks the semaphore referenced by sem
+ *   by performing the semaphore unlock operation on that semaphore.
+ *
+ *   If the semaphore value resulting from this operation is positive, then
+ *   no tasks were blocked waiting for the semaphore to become unlocked; the
+ *   semaphore is simply incremented.
+ *
+ *   If the value of the semaphore resulting from this operation is zero,
+ *   then one of the tasks blocked waiting for the semaphore shall be
+ *   allowed to return successfully from its call to nxsem_wait().
+ *
+ * Input Parameters:
+ *   sem - Semaphore descriptor
+ *
+ * Returned Value:
+ *   This is an internal OS interface and should not be used by applications.
+ *   It follows the NuttX internal error return policy:  Zero (OK) is
+ *   returned on success.  A negated errno value is returned on failure.
+ *
+ * Assumptions:
+ *   This function may be called from an interrupt handler.
+ *
+ ****************************************************************************/
+
+int nxsem_post(FAR sem_t *sem)
+{
+  DEBUGASSERT(sem != NULL);
+
+  /* If this is a mutex, we can try to unlock the mutex in fast mode,
+   * else try to get it in slow mode.
+   */
+
+#if !defined(CONFIG_PRIORITY_INHERITANCE) && !defined(CONFIG_PRIORITY_PROTECT)
+  if (sem->flags & SEM_TYPE_MUTEX)
+    {
+      short old = 0;
+      if (atomic_compare_exchange_weak_explicit(NXSEM_COUNT(sem), &old, 1,
+                                                memory_order_release,
+                                                memory_order_relaxed))
+        {
+          return OK;
+        }
+    }
+#endif
+
+  return nxsem_post_slow(sem);
+}
diff --git a/sched/semaphore/sem_recover.c b/sched/semaphore/sem_recover.c
index 1a6e7ea2bf..dd0418d4bb 100644
--- a/sched/semaphore/sem_recover.c
+++ b/sched/semaphore/sem_recover.c
@@ -85,7 +85,7 @@ void nxsem_recover(FAR struct tcb_s *tcb)
   if (tcb->task_state == TSTATE_WAIT_SEM)
     {
       FAR sem_t *sem = tcb->waitobj;
-      DEBUGASSERT(sem != NULL && sem->semcount < 0);
+      DEBUGASSERT(sem != NULL && atomic_load(NXSEM_COUNT(sem)) < 0);
 
       /* Restore the correct priority of all threads that hold references
        * to this semaphore.
@@ -99,7 +99,7 @@ void nxsem_recover(FAR struct tcb_s *tcb)
        * place.
        */
 
-      sem->semcount++;
+      atomic_fetch_add(NXSEM_COUNT(sem), 1);
     }
 
   /* Release all semphore holders for the task */
diff --git a/sched/semaphore/sem_reset.c b/sched/semaphore/sem_reset.c
index 14418fe704..d7774ea504 100644
--- a/sched/semaphore/sem_reset.c
+++ b/sched/semaphore/sem_reset.c
@@ -60,6 +60,7 @@
 int nxsem_reset(FAR sem_t *sem, int16_t count)
 {
   irqstate_t flags;
+  short semcount;
 
   DEBUGASSERT(sem != NULL && count >= 0);
 
@@ -80,7 +81,7 @@ int nxsem_reset(FAR sem_t *sem, int16_t count)
    * out counts to any waiting threads.
    */
 
-  while (sem->semcount < 0 && count > 0)
+  while (atomic_load(NXSEM_COUNT(sem)) < 0 && count > 0)
     {
       /* Give out one counting, waking up one of the waiting threads
        * and, perhaps, kicking off a lot of priority inheritance
@@ -98,10 +99,18 @@ int nxsem_reset(FAR sem_t *sem, int16_t count)
    * value of sem->semcount is already correct in this case.
    */
 
-  if (sem->semcount >= 0)
+  do
     {
-      sem->semcount = count;
+      semcount = atomic_load(NXSEM_COUNT(sem));
+      if (semcount < 0)
+        {
+          break;
+        }
     }
+  while (!atomic_compare_exchange_weak_explicit(NXSEM_COUNT(sem),
+                                                &semcount, count,
+                                                memory_order_release,
+                                                memory_order_relaxed));
 
   /* Allow any pending context switches to occur now */
 
diff --git a/sched/semaphore/sem_trywait.c b/sched/semaphore/sem_trywait.c
index 8b0239ec9e..c4b512547b 100644
--- a/sched/semaphore/sem_trywait.c
+++ b/sched/semaphore/sem_trywait.c
@@ -38,6 +38,78 @@
 #include "sched/sched.h"
 #include "semaphore/semaphore.h"
 
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nxsem_trywait_slow
+ *
+ * Description:
+ *   This function locks the specified semaphore in slow mode.
+ *
+ * Input Parameters:
+ *   sem - the semaphore descriptor
+ *
+ * Returned Value:
+ *
+ *     EINVAL - Invalid attempt to get the semaphore
+ *     EAGAIN - The semaphore is not available.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int nxsem_trywait_slow(FAR sem_t *sem)
+{
+  FAR struct tcb_s *rtcb;
+  irqstate_t flags;
+  short semcount;
+  int ret;
+
+  /* The following operations must be performed with interrupts disabled
+   * because sem_post() may be called from an interrupt handler.
+   */
+
+  flags = enter_critical_section();
+  rtcb = this_task();
+
+  /* If the semaphore is available, give it to the requesting task */
+
+  do
+    {
+      semcount = atomic_load(NXSEM_COUNT(sem));
+      if (semcount <= 0)
+        {
+          leave_critical_section(flags);
+          return -EAGAIN;
+        }
+    }
+  while (!atomic_compare_exchange_weak_explicit(NXSEM_COUNT(sem),
+                                                &semcount, semcount - 1,
+                                                memory_order_acquire,
+                                                memory_order_relaxed));
+
+  /* It is, let the task take the semaphore */
+
+  ret = nxsem_protect_wait(sem);
+  if (ret < 0)
+    {
+      atomic_fetch_add(NXSEM_COUNT(sem), 1);
+      leave_critical_section(flags);
+      return ret;
+    }
+
+  nxsem_add_holder(sem);
+  rtcb->waitobj = NULL;
+  ret = OK;
+
+  /* Interrupts may now be enabled. */
+
+  leave_critical_section(flags);
+  return ret;
+}
+
 /****************************************************************************
  * Public Functions
  ****************************************************************************/
@@ -68,49 +140,30 @@
 
 int nxsem_trywait(FAR sem_t *sem)
 {
-  FAR struct tcb_s *rtcb = this_task();
-  irqstate_t flags;
-  int ret;
-
   /* This API should not be called from the idleloop */
 
   DEBUGASSERT(sem != NULL);
   DEBUGASSERT(!OSINIT_IDLELOOP() || !sched_idletask() ||
               up_interrupt_context());
 
-  /* The following operations must be performed with interrupts disabled
-   * because sem_post() may be called from an interrupt handler.
+  /* If this is a mutex, we can try to get the mutex in fast mode,
+   * else try to get it in slow mode.
    */
 
-  flags = enter_critical_section();
-
-  /* If the semaphore is available, give it to the requesting task */
-
-  if (sem->semcount > 0)
+#if !defined(CONFIG_PRIORITY_INHERITANCE) && !defined(CONFIG_PRIORITY_PROTECT)
+  if (sem->flags & SEM_TYPE_MUTEX)
     {
-      /* It is, let the task take the semaphore */
-
-      ret = nxsem_protect_wait(sem);
-      if (ret < 0)
+      short old = 1;
+      if (atomic_compare_exchange_weak_explicit(NXSEM_COUNT(sem), &old, 0,
+                                                memory_order_acquire,
+                                                memory_order_relaxed))
         {
-          leave_critical_section(flags);
-          return ret;
+          return OK;
         }
 
-      sem->semcount--;
-      nxsem_add_holder(sem);
-      rtcb->waitobj = NULL;
-      ret = OK;
-    }
-  else
-    {
-      /* Semaphore is not available */
-
-      ret = -EAGAIN;
+      return -EAGAIN;
     }
+#endif
 
-  /* Interrupts may now be enabled. */
-
-  leave_critical_section(flags);
-  return ret;
+  return nxsem_trywait_slow(sem);
 }
diff --git a/sched/semaphore/sem_wait.c b/sched/semaphore/sem_wait.c
index 7ea4d36480..a0a8b9d76f 100644
--- a/sched/semaphore/sem_wait.c
+++ b/sched/semaphore/sem_wait.c
@@ -38,16 +38,15 @@
 #include "semaphore/semaphore.h"
 
 /****************************************************************************
- * Public Functions
+ * Private Functions
  ****************************************************************************/
 
 /****************************************************************************
- * Name: nxsem_wait
+ * Name: nxsem_wait_slow
  *
  * Description:
- *   This function attempts to lock the semaphore referenced by 'sem'.  If
- *   the semaphore value is (<=) zero, then the calling task will not return
- *   until it successfully acquires the lock.
+ *   This function attempts to lock the semaphore referenced by 'sem' in
+ *   slow mode.
  *
  *   This is an internal OS interface.  It is functionally equivalent to
  *   sem_wait except that:
@@ -69,17 +68,12 @@
  *
  ****************************************************************************/
 
-int nxsem_wait(FAR sem_t *sem)
+static int nxsem_wait_slow(FAR sem_t *sem)
 {
   FAR struct tcb_s *rtcb = this_task();
   irqstate_t flags;
   int ret;
 
-  /* This API should not be called from interrupt handlers & idleloop */
-
-  DEBUGASSERT(sem != NULL && up_interrupt_context() == false);
-  DEBUGASSERT(!OSINIT_IDLELOOP() || !sched_idletask());
-
   /* The following operations must be performed with interrupts
    * disabled because nxsem_post() may be called from an interrupt
    * handler.
@@ -91,7 +85,7 @@ int nxsem_wait(FAR sem_t *sem)
 
   /* Check if the lock is available */
 
-  if (sem->semcount > 0)
+  if (atomic_fetch_sub(NXSEM_COUNT(sem), 1) > 0)
     {
       /* It is, let the task take the semaphore. */
 
@@ -102,7 +96,6 @@ int nxsem_wait(FAR sem_t *sem)
           return ret;
         }
 
-      sem->semcount--;
       nxsem_add_holder(sem);
       rtcb->waitobj = NULL;
       ret = OK;
@@ -124,10 +117,6 @@ int nxsem_wait(FAR sem_t *sem)
 
       DEBUGASSERT(rtcb->waitobj == NULL);
 
-      /* Handle the POSIX semaphore (but don't set the owner yet) */
-
-      sem->semcount--;
-
       /* Save the waited on semaphore in the TCB */
 
       rtcb->waitobj = sem;
@@ -224,6 +213,65 @@ int nxsem_wait(FAR sem_t *sem)
   return ret;
 }
 
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nxsem_wait
+ *
+ * Description:
+ *   This function attempts to lock the semaphore referenced by 'sem'.  If
+ *   the semaphore value is (<=) zero, then the calling task will not return
+ *   until it successfully acquires the lock.
+ *
+ *   This is an internal OS interface.  It is functionally equivalent to
+ *   sem_wait except that:
+ *
+ *   - It is not a cancellation point, and
+ *   - It does not modify the errno value.
+ *
+ * Input Parameters:
+ *   sem - Semaphore descriptor.
+ *
+ * Returned Value:
+ *   This is an internal OS interface and should not be used by applications.
+ *   It follows the NuttX internal error return policy:  Zero (OK) is
+ *   returned on success.  A negated errno value is returned on failure.
+ *   Possible returned errors:
+ *
+ *   - EINVAL:  Invalid attempt to get the semaphore
+ *   - EINTR:   The wait was interrupted by the receipt of a signal.
+ *
+ ****************************************************************************/
+
+int nxsem_wait(FAR sem_t *sem)
+{
+  /* This API should not be called from interrupt handlers & idleloop */
+
+  DEBUGASSERT(sem != NULL && up_interrupt_context() == false);
+  DEBUGASSERT(!OSINIT_IDLELOOP() || !sched_idletask());
+
+  /* If this is a mutex, we can try to get the mutex in fast mode,
+   * else try to get it in slow mode.
+   */
+
+#if !defined(CONFIG_PRIORITY_INHERITANCE) && !defined(CONFIG_PRIORITY_PROTECT)
+  if (sem->flags & SEM_TYPE_MUTEX)
+    {
+      short old = 1;
+      if (atomic_compare_exchange_weak_explicit(NXSEM_COUNT(sem), &old, 0,
+                                                memory_order_acquire,
+                                                memory_order_relaxed))
+        {
+          return OK;
+        }
+    }
+#endif
+
+  return nxsem_wait_slow(sem);
+}
+
 /****************************************************************************
  * Name: nxsem_wait_uninterruptible
  *
diff --git a/sched/semaphore/sem_waitirq.c b/sched/semaphore/sem_waitirq.c
index 746c46e05f..e3cd08db18 100644
--- a/sched/semaphore/sem_waitirq.c
+++ b/sched/semaphore/sem_waitirq.c
@@ -87,7 +87,7 @@ void nxsem_wait_irq(FAR struct tcb_s *wtcb, int errcode)
    * and already changed the task's state.
    */
 
-  DEBUGASSERT(sem != NULL && sem->semcount < 0);
+  DEBUGASSERT(sem != NULL && atomic_load(NXSEM_COUNT(sem)) < 0);
 
   /* Restore the correct priority of all threads that hold references
    * to this semaphore.
@@ -101,7 +101,7 @@ void nxsem_wait_irq(FAR struct tcb_s *wtcb, int errcode)
    * place.
    */
 
-  sem->semcount++;
+  atomic_fetch_add(NXSEM_COUNT(sem), 1);
 
   /* Remove task from waiting list */
 
diff --git a/sched/semaphore/semaphore.h b/sched/semaphore/semaphore.h
index b61085ea3c..45f745f58b 100644
--- a/sched/semaphore/semaphore.h
+++ b/sched/semaphore/semaphore.h
@@ -31,10 +31,17 @@
 #include <nuttx/compiler.h>
 #include <nuttx/semaphore.h>
 #include <nuttx/sched.h>
+#include <nuttx/atomic.h>
 
 #include <stdint.h>
 #include <stdbool.h>
 
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define NXSEM_COUNT(s) ((FAR atomic_short *)&(s)->semcount)
+
 /****************************************************************************
  * Public Function Prototypes
  ****************************************************************************/

Reply via email to