From c368337a81a0f7a562213683ec6757ff4c7b5732 Mon Sep 17 00:00:00 2001 From: eatvector <2302147681@qq.com> Date: Fri, 25 Jul 2025 12:14:43 +0000 Subject: [PATCH] fix(kernel): properly release mutexes in _thread_detach_from_mutex() --- include/rtthread.h | 1 + src/ipc.c | 92 +++++++++++++++++++++++++++++++++++++++------- src/thread.c | 4 +- 3 files changed, 80 insertions(+), 17 deletions(-) diff --git a/include/rtthread.h b/include/rtthread.h index d9b582ab1e2..6a2901365d5 100644 --- a/include/rtthread.h +++ b/include/rtthread.h @@ -465,6 +465,7 @@ rt_err_t rt_mutex_trytake(rt_mutex_t mutex); rt_err_t rt_mutex_take_interruptible(rt_mutex_t mutex, rt_int32_t time); rt_err_t rt_mutex_take_killable(rt_mutex_t mutex, rt_int32_t time); rt_err_t rt_mutex_release(rt_mutex_t mutex); +rt_err_t rt_mutex_force_release(rt_mutex_t mutex); rt_err_t rt_mutex_control(rt_mutex_t mutex, int cmd, void *arg); rt_inline rt_thread_t rt_mutex_get_owner(rt_mutex_t mutex) diff --git a/src/ipc.c b/src/ipc.c index fdf33b2e330..184e9c0da59 100644 --- a/src/ipc.c +++ b/src/ipc.c @@ -1575,18 +1575,32 @@ RTM_EXPORT(rt_mutex_trytake); /** - * @brief This function will release a mutex. If there is thread suspended on the mutex, the thread will be resumed. - * - * @note If there are threads suspended on this mutex, the first thread in the list of this mutex object - * will be resumed, and a thread scheduling (rt_schedule) will be executed. - * If no threads are suspended on this mutex, the count value mutex->value of this mutex will increase by 1. - * - * @param mutex is a pointer to a mutex object. - * - * @return Return the operation status. When the return value is RT_EOK, the operation is successful. - * If the return value is any other values, it means that the mutex release failed. + * @brief Release a mutex, optionally forcing the release even if the current thread is not the owner. + * If threads are suspended on the mutex, the highest-priority thread will be resumed. + * + * @note This function provides two distinct release modes: + * Standard mode enforces normal ownership rules where only the owning thread + * may release the mutex. Force mode bypasses ownership checks and directly + * modifies the hold count to enable release by any thread. + * + * In both modes, if threads are suspended on the mutex, the highest priority + * waiting thread will be resumed, potentially triggering thread scheduling. + * When no threads are waiting, the mutex owner and hold count are reset. + * + * @param mutex Pointer to the mutex object. + * @param is_force If RT_TRUE, bypass ownership check and force release. + * If RT_FALSE, enforce standard owner-only release. + * + * @return Operation status: + * RT_EOK: Success. + * -RT_ERROR: Failed (non-force mode and current thread is not owner). + * + * @warning Forced release (is_force=RT_TRUE) should only be used when: + * The caller is not the mutex owner and the original owner thread + * is guaranteed to be closed (rt_thread_close) and no longer executing any code, + * or the caller is the current mutex owner. */ -rt_err_t rt_mutex_release(rt_mutex_t mutex) +static rt_err_t _rt_mutex_release(rt_mutex_t mutex, rt_bool_t is_force) { rt_sched_lock_level_t slvl; struct rt_thread *thread; @@ -1611,9 +1625,21 @@ rt_err_t rt_mutex_release(rt_mutex_t mutex) RT_OBJECT_HOOK_CALL(rt_object_put_hook, (&(mutex->parent.parent))); - /* mutex only can be released by owner */ - if (thread != mutex->owner) + if (is_force) { + /* + * Force release mode: + * Bypass ownership check and prepare for release by + * setting hold count to 1 before decrement + */ + mutex->hold = 1; + } + else if (thread != mutex->owner) + { + /* + * Standard release mode: + * Reject non-owner thread's release attempt + */ thread->error = -RT_ERROR; rt_spin_unlock(&(mutex->spinlock)); @@ -1713,9 +1739,47 @@ rt_err_t rt_mutex_release(rt_mutex_t mutex) return RT_EOK; } + + +/** + * @brief Release a mutex owned by current thread. + * + * @note Resumes first suspended thread if any (requires scheduling). + * Increases mutex->value if no threads waiting. + * Must be called by mutex owner with spinlock held. + * + * @param mutex Pointer to the mutex object. + * + * @return RT_EOK on success, -RT_ERROR if not owner. + */ +rt_err_t rt_mutex_release(rt_mutex_t mutex) +{ + return _rt_mutex_release(mutex, RT_FALSE); +} RTM_EXPORT(rt_mutex_release); +/** + * @brief Forcefully release a mutex regardless of ownership or recursive holds. + * + * @note This function bypasses all ownership verification and ignores recursive hold counts. + * It will resume the first suspended thread if available, which may trigger scheduling. + * If no threads are waiting, the mutex value will be reset. + * + * @warning When releasing a mutex not owned by the caller, the original owner thread + * must have been properly terminated via rt_thread_close and must not be + * executing any code at all. + * + * @param mutex Pointer to the mutex object. + * + * @return RT_EOK on success. + */ +rt_err_t rt_mutex_force_release(rt_mutex_t mutex) +{ + return _rt_mutex_release(mutex, RT_TRUE); +} + + /** * @brief This function will set some extra attributions of a mutex object. * @@ -4030,4 +4094,4 @@ RTM_EXPORT(rt_mq_control); /**@}*/ #endif /* RT_USING_MESSAGEQUEUE */ -/**@}*/ +/**@}*/ \ No newline at end of file diff --git a/src/thread.c b/src/thread.c index a520a323b16..5f332bf88b4 100644 --- a/src/thread.c +++ b/src/thread.c @@ -101,9 +101,7 @@ static void _thread_detach_from_mutex(rt_thread_t thread) { mutex = rt_list_entry(node, struct rt_mutex, taken_list); LOG_D("Thread [%s] exits while holding mutex [%s].\n", thread->parent.name, mutex->parent.parent.name); - /* recursively take */ - mutex->hold = 1; - rt_mutex_release(mutex); + rt_mutex_force_release(mutex); } rt_spin_unlock_irqrestore(&thread->spinlock, level);