Browse Source

feat: add ISR safe completion API

Since the completion is used to sync with ISR mostly, we should set the
default semantic to ISR-safe. So most user will be happy and don't see
any weird behavior in their codes.

Changes:
- Added `rt_completion_wait_noisr` and
  `rt_completion_wait_flags_noisr` functions in `completion.h`,
  `completion_comm.c`, `completion_mp.c`, and `completion_up.c`.
- The new APIs allow waiting for completions in non-ISR contexts
  while ensuring thread context safety.
- Existing documentation and comments were updated to clarify
  usage contexts and emphasize restrictions on ISR usage.

Signed-off-by: Shell <smokewood@qq.com>
Shell 8 months ago
parent
commit
c451dce820

+ 6 - 2
components/drivers/include/ipc/completion.h

@@ -14,9 +14,9 @@
 #include <rtconfig.h>
 #include <rtconfig.h>
 
 
 /**
 /**
- * Completion - A tiny & rapid IPC primitive for resource-constrained scenarios
+ * RT-Completion - A Tiny(resource-constrained) & Rapid(lockless) IPC Primitive
  *
  *
- * It's an IPC using one CPU word with the encoding:
+ * It's an IPC using one pointer word with the encoding:
  *
  *
  * BIT      | MAX-1 ----------------- 1 |       0        |
  * BIT      | MAX-1 ----------------- 1 |       0        |
  * CONTENT  |   suspended_thread & ~1   | completed flag |
  * CONTENT  |   suspended_thread & ~1   | completed flag |
@@ -33,8 +33,12 @@ struct rt_completion
 void rt_completion_init(struct rt_completion *completion);
 void rt_completion_init(struct rt_completion *completion);
 rt_err_t rt_completion_wait(struct rt_completion *completion,
 rt_err_t rt_completion_wait(struct rt_completion *completion,
                             rt_int32_t            timeout);
                             rt_int32_t            timeout);
+rt_err_t rt_completion_wait_noisr(struct rt_completion *completion,
+                                  rt_int32_t            timeout);
 rt_err_t rt_completion_wait_flags(struct rt_completion *completion,
 rt_err_t rt_completion_wait_flags(struct rt_completion *completion,
                                   rt_int32_t timeout, int suspend_flag);
                                   rt_int32_t timeout, int suspend_flag);
+rt_err_t rt_completion_wait_flags_noisr(struct rt_completion *completion,
+                                        rt_int32_t timeout, int suspend_flag);
 void rt_completion_done(struct rt_completion *completion);
 void rt_completion_done(struct rt_completion *completion);
 rt_err_t rt_completion_wakeup(struct rt_completion *completion);
 rt_err_t rt_completion_wakeup(struct rt_completion *completion);
 rt_err_t rt_completion_wakeup_by_errno(struct rt_completion *completion, rt_err_t error);
 rt_err_t rt_completion_wakeup_by_errno(struct rt_completion *completion, rt_err_t error);

+ 22 - 0
components/drivers/ipc/completion_comm.c

@@ -35,6 +35,28 @@ rt_err_t rt_completion_wakeup(struct rt_completion *completion)
     return rt_completion_wakeup_by_errno(completion, -1);
     return rt_completion_wakeup_by_errno(completion, -1);
 }
 }
 
 
+/**
+ * @brief This is same as rt_completion_wait(), except that this API is NOT
+ *        ISR-safe (you can NOT call completion_done() on isr routine).
+ *
+ * @param completion is a pointer to a completion object.
+ *
+ * @param timeout is a timeout period (unit: OS ticks). If the completion is unavailable, the thread will wait for
+ *                the completion done up to the amount of time specified by the argument.
+ *                NOTE: Generally, we use the macro RT_WAITING_FOREVER to set this parameter, which means that when the
+ *                completion is unavailable, the thread will be waitting forever.
+ *
+ * @return Return the operation status. ONLY when the return value is RT_EOK, the operation is successful.
+ *         If the return value is any other values, it means that the completion wait failed.
+ *
+ * @warning This function can ONLY be called in the thread context. It MUST NOT be called in interrupt context.
+ */
+rt_err_t rt_completion_wait_noisr(struct rt_completion *completion,
+                                  rt_int32_t            timeout)
+{
+    return rt_completion_wait_flags_noisr(completion, timeout, RT_UNINTERRUPTIBLE);
+}
+
 /**
 /**
  * @brief This function will wait for a completion, if the completion is unavailable, the thread shall wait for
  * @brief This function will wait for a completion, if the completion is unavailable, the thread shall wait for
  *        the completion up to a specified time.
  *        the completion up to a specified time.

+ 31 - 5
components/drivers/ipc/completion_mp.c

@@ -65,11 +65,10 @@ void rt_completion_init(struct rt_completion *completion)
 RTM_EXPORT(rt_completion_init);
 RTM_EXPORT(rt_completion_init);
 
 
 /**
 /**
- * @brief This function will wait for a completion, if the completion is unavailable, the thread shall wait for
- *        the completion up to a specified time.
+ * @brief This is same as rt_completion_wait_flags(), except that this API is NOT
+ *        ISR-safe (you can NOT call completion_done() on isr routine).
  *
  *
  * @param completion is a pointer to a completion object.
  * @param completion is a pointer to a completion object.
- *
  * @param timeout is a timeout period (unit: OS ticks). If the completion is unavailable, the thread will wait for
  * @param timeout is a timeout period (unit: OS ticks). If the completion is unavailable, the thread will wait for
  *                the completion done up to the amount of time specified by the argument.
  *                the completion done up to the amount of time specified by the argument.
  *                NOTE: Generally, we use the macro RT_WAITING_FOREVER to set this parameter, which means that when the
  *                NOTE: Generally, we use the macro RT_WAITING_FOREVER to set this parameter, which means that when the
@@ -81,8 +80,8 @@ RTM_EXPORT(rt_completion_init);
  *
  *
  * @warning This function can ONLY be called in the thread context. It MUST NOT be called in interrupt context.
  * @warning This function can ONLY be called in the thread context. It MUST NOT be called in interrupt context.
  */
  */
-rt_err_t rt_completion_wait_flags(struct rt_completion *completion,
-                                  rt_int32_t timeout, int suspend_flag)
+rt_err_t rt_completion_wait_flags_noisr(struct rt_completion *completion,
+                                        rt_int32_t timeout, int suspend_flag)
 {
 {
     rt_err_t result = -RT_ERROR;
     rt_err_t result = -RT_ERROR;
     rt_thread_t thread;
     rt_thread_t thread;
@@ -159,6 +158,33 @@ rt_err_t rt_completion_wait_flags(struct rt_completion *completion,
     return result;
     return result;
 }
 }
 
 
+/**
+ * @brief This function will wait for a completion, if the completion is unavailable, the thread shall wait for
+ *        the completion up to a specified time.
+ *
+ * @param completion is a pointer to a completion object.
+ * @param timeout is a timeout period (unit: OS ticks). If the completion is unavailable, the thread will wait for
+ *                the completion done up to the amount of time specified by the argument.
+ *                NOTE: Generally, we use the macro RT_WAITING_FOREVER to set this parameter, which means that when the
+ *                completion is unavailable, the thread will be waitting forever.
+ * @param suspend_flag suspend flags. See rt_thread_suspend_with_flag()
+ *
+ * @return Return the operation status. ONLY when the return value is RT_EOK, the operation is successful.
+ *         If the return value is any other values, it means that the completion wait failed.
+ *
+ * @warning This function can ONLY be called in the thread context. It MUST NOT be called in interrupt context.
+ */
+rt_err_t rt_completion_wait_flags(struct rt_completion *completion,
+                                  rt_int32_t timeout, int suspend_flag)
+{
+    rt_err_t error;
+    rt_ubase_t level;
+    level = rt_hw_local_irq_disable();
+    error = rt_completion_wait_flags_noisr(completion, timeout, suspend_flag);
+    rt_hw_local_irq_enable(level);
+    return error;
+}
+
 static rt_base_t _wait_until_update(struct rt_completion *completion, rt_base_t expected)
 static rt_base_t _wait_until_update(struct rt_completion *completion, rt_base_t expected)
 {
 {
     rt_base_t current_value;
     rt_base_t current_value;

+ 22 - 0
components/drivers/ipc/completion_up.c

@@ -148,6 +148,28 @@ __exit:
     return result;
     return result;
 }
 }
 
 
+/**
+ * @brief This is same as rt_completion_wait_flags(), except that this API is NOT
+ *        ISR-safe (you can NOT call completion_done() on isr routine).
+ *
+ * @param completion is a pointer to a completion object.
+ * @param timeout is a timeout period (unit: OS ticks). If the completion is unavailable, the thread will wait for
+ *                the completion done up to the amount of time specified by the argument.
+ *                NOTE: Generally, we use the macro RT_WAITING_FOREVER to set this parameter, which means that when the
+ *                completion is unavailable, the thread will be waitting forever.
+ * @param suspend_flag suspend flags. See rt_thread_suspend_with_flag()
+ *
+ * @return Return the operation status. ONLY when the return value is RT_EOK, the operation is successful.
+ *         If the return value is any other values, it means that the completion wait failed.
+ *
+ * @warning This function can ONLY be called in the thread context. It MUST NOT be called in interrupt context.
+ */
+rt_err_t rt_completion_wait_flags_noisr(struct rt_completion *completion,
+                                        rt_int32_t timeout, int suspend_flag)
+{
+    return rt_completion_wait_flags(completion, timeout, suspend_flag);
+}
+
 /**
 /**
  * @brief   This function indicates a completion has done and wakeup the thread
  * @brief   This function indicates a completion has done and wakeup the thread
  *          and update its errno. No update is applied if it's a negative value.
  *          and update its errno. No update is applied if it's a negative value.