From c451dce8203fb2616d6ec4634db1f4a7b766680c Mon Sep 17 00:00:00 2001 From: Shell Date: Thu, 15 Aug 2024 21:30:58 +0800 Subject: [PATCH] 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 --- components/drivers/include/ipc/completion.h | 8 +++-- components/drivers/ipc/completion_comm.c | 22 +++++++++++++ components/drivers/ipc/completion_mp.c | 36 ++++++++++++++++++--- components/drivers/ipc/completion_up.c | 22 +++++++++++++ 4 files changed, 81 insertions(+), 7 deletions(-) diff --git a/components/drivers/include/ipc/completion.h b/components/drivers/include/ipc/completion.h index 30f4acf81c..bc075c410c 100644 --- a/components/drivers/include/ipc/completion.h +++ b/components/drivers/include/ipc/completion.h @@ -14,9 +14,9 @@ #include /** - * 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 | * CONTENT | suspended_thread & ~1 | completed flag | @@ -33,8 +33,12 @@ struct rt_completion void rt_completion_init(struct rt_completion *completion); rt_err_t rt_completion_wait(struct rt_completion *completion, 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_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); 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); diff --git a/components/drivers/ipc/completion_comm.c b/components/drivers/ipc/completion_comm.c index 6828714489..67c00b4b65 100644 --- a/components/drivers/ipc/completion_comm.c +++ b/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); } +/** + * @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 * the completion up to a specified time. diff --git a/components/drivers/ipc/completion_mp.c b/components/drivers/ipc/completion_mp.c index 21627e073b..38ffc6f3fd 100644 --- a/components/drivers/ipc/completion_mp.c +++ b/components/drivers/ipc/completion_mp.c @@ -65,11 +65,10 @@ void rt_completion_init(struct rt_completion *completion) 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 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 @@ -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. */ -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_thread_t thread; @@ -159,6 +158,33 @@ rt_err_t rt_completion_wait_flags(struct rt_completion *completion, 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) { rt_base_t current_value; diff --git a/components/drivers/ipc/completion_up.c b/components/drivers/ipc/completion_up.c index 5c8e546687..8c3bbbffa1 100644 --- a/components/drivers/ipc/completion_up.c +++ b/components/drivers/ipc/completion_up.c @@ -148,6 +148,28 @@ __exit: 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 * and update its errno. No update is applied if it's a negative value.