Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions include/rtthread.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
92 changes: 78 additions & 14 deletions src/ipc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Comment on lines +1598 to +1601
Copy link

Copilot AI Aug 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The warning documentation should specify that forced release bypasses the recursive mutex mechanism, which could lead to undefined behavior if the original owner thread is still running and expects to maintain its hold count.

Suggested change
* @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.
* @warning Forced release (is_force=RT_TRUE) bypasses the recursive mutex mechanism,
* which could lead to undefined behavior if the original owner thread is still running
* and expects to maintain its hold count. Forced release 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.

Copilot uses AI. Check for mistakes.
*/
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;
Expand All @@ -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));

Expand Down Expand Up @@ -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.
Comment on lines +1769 to +1771
Copy link

Copilot AI Aug 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The warning should also mention that forced release bypasses recursive hold counts and may leave the system in an inconsistent state if the original owner had taken the mutex multiple times recursively.

Suggested change
* @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.
* @warning Forced release bypasses recursive hold counts and may leave the system in an inconsistent state
* if the original owner had taken the mutex multiple times recursively. 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.

Copilot uses AI. Check for mistakes.
*
* @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.
*
Expand Down Expand Up @@ -4030,4 +4094,4 @@ RTM_EXPORT(rt_mq_control);

/**@}*/
#endif /* RT_USING_MESSAGEQUEUE */
/**@}*/
/**@}*/
4 changes: 1 addition & 3 deletions src/thread.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down