Skip to content

Commit c368337

Browse files
committed
fix(kernel): properly release mutexes in _thread_detach_from_mutex()
1 parent f9564d4 commit c368337

File tree

3 files changed

+80
-17
lines changed

3 files changed

+80
-17
lines changed

include/rtthread.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,7 @@ rt_err_t rt_mutex_trytake(rt_mutex_t mutex);
465465
rt_err_t rt_mutex_take_interruptible(rt_mutex_t mutex, rt_int32_t time);
466466
rt_err_t rt_mutex_take_killable(rt_mutex_t mutex, rt_int32_t time);
467467
rt_err_t rt_mutex_release(rt_mutex_t mutex);
468+
rt_err_t rt_mutex_force_release(rt_mutex_t mutex);
468469
rt_err_t rt_mutex_control(rt_mutex_t mutex, int cmd, void *arg);
469470

470471
rt_inline rt_thread_t rt_mutex_get_owner(rt_mutex_t mutex)

src/ipc.c

Lines changed: 78 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1575,18 +1575,32 @@ RTM_EXPORT(rt_mutex_trytake);
15751575

15761576

15771577
/**
1578-
* @brief This function will release a mutex. If there is thread suspended on the mutex, the thread will be resumed.
1579-
*
1580-
* @note If there are threads suspended on this mutex, the first thread in the list of this mutex object
1581-
* will be resumed, and a thread scheduling (rt_schedule) will be executed.
1582-
* If no threads are suspended on this mutex, the count value mutex->value of this mutex will increase by 1.
1583-
*
1584-
* @param mutex is a pointer to a mutex object.
1585-
*
1586-
* @return Return the operation status. When the return value is RT_EOK, the operation is successful.
1587-
* If the return value is any other values, it means that the mutex release failed.
1578+
* @brief Release a mutex, optionally forcing the release even if the current thread is not the owner.
1579+
* If threads are suspended on the mutex, the highest-priority thread will be resumed.
1580+
*
1581+
* @note This function provides two distinct release modes:
1582+
* Standard mode enforces normal ownership rules where only the owning thread
1583+
* may release the mutex. Force mode bypasses ownership checks and directly
1584+
* modifies the hold count to enable release by any thread.
1585+
*
1586+
* In both modes, if threads are suspended on the mutex, the highest priority
1587+
* waiting thread will be resumed, potentially triggering thread scheduling.
1588+
* When no threads are waiting, the mutex owner and hold count are reset.
1589+
*
1590+
* @param mutex Pointer to the mutex object.
1591+
* @param is_force If RT_TRUE, bypass ownership check and force release.
1592+
* If RT_FALSE, enforce standard owner-only release.
1593+
*
1594+
* @return Operation status:
1595+
* RT_EOK: Success.
1596+
* -RT_ERROR: Failed (non-force mode and current thread is not owner).
1597+
*
1598+
* @warning Forced release (is_force=RT_TRUE) should only be used when:
1599+
* The caller is not the mutex owner and the original owner thread
1600+
* is guaranteed to be closed (rt_thread_close) and no longer executing any code,
1601+
* or the caller is the current mutex owner.
15881602
*/
1589-
rt_err_t rt_mutex_release(rt_mutex_t mutex)
1603+
static rt_err_t _rt_mutex_release(rt_mutex_t mutex, rt_bool_t is_force)
15901604
{
15911605
rt_sched_lock_level_t slvl;
15921606
struct rt_thread *thread;
@@ -1611,9 +1625,21 @@ rt_err_t rt_mutex_release(rt_mutex_t mutex)
16111625

16121626
RT_OBJECT_HOOK_CALL(rt_object_put_hook, (&(mutex->parent.parent)));
16131627

1614-
/* mutex only can be released by owner */
1615-
if (thread != mutex->owner)
1628+
if (is_force)
16161629
{
1630+
/*
1631+
* Force release mode:
1632+
* Bypass ownership check and prepare for release by
1633+
* setting hold count to 1 before decrement
1634+
*/
1635+
mutex->hold = 1;
1636+
}
1637+
else if (thread != mutex->owner)
1638+
{
1639+
/*
1640+
* Standard release mode:
1641+
* Reject non-owner thread's release attempt
1642+
*/
16171643
thread->error = -RT_ERROR;
16181644
rt_spin_unlock(&(mutex->spinlock));
16191645

@@ -1713,9 +1739,47 @@ rt_err_t rt_mutex_release(rt_mutex_t mutex)
17131739

17141740
return RT_EOK;
17151741
}
1742+
1743+
1744+
/**
1745+
* @brief Release a mutex owned by current thread.
1746+
*
1747+
* @note Resumes first suspended thread if any (requires scheduling).
1748+
* Increases mutex->value if no threads waiting.
1749+
* Must be called by mutex owner with spinlock held.
1750+
*
1751+
* @param mutex Pointer to the mutex object.
1752+
*
1753+
* @return RT_EOK on success, -RT_ERROR if not owner.
1754+
*/
1755+
rt_err_t rt_mutex_release(rt_mutex_t mutex)
1756+
{
1757+
return _rt_mutex_release(mutex, RT_FALSE);
1758+
}
17161759
RTM_EXPORT(rt_mutex_release);
17171760

17181761

1762+
/**
1763+
* @brief Forcefully release a mutex regardless of ownership or recursive holds.
1764+
*
1765+
* @note This function bypasses all ownership verification and ignores recursive hold counts.
1766+
* It will resume the first suspended thread if available, which may trigger scheduling.
1767+
* If no threads are waiting, the mutex value will be reset.
1768+
*
1769+
* @warning When releasing a mutex not owned by the caller, the original owner thread
1770+
* must have been properly terminated via rt_thread_close and must not be
1771+
* executing any code at all.
1772+
*
1773+
* @param mutex Pointer to the mutex object.
1774+
*
1775+
* @return RT_EOK on success.
1776+
*/
1777+
rt_err_t rt_mutex_force_release(rt_mutex_t mutex)
1778+
{
1779+
return _rt_mutex_release(mutex, RT_TRUE);
1780+
}
1781+
1782+
17191783
/**
17201784
* @brief This function will set some extra attributions of a mutex object.
17211785
*
@@ -4030,4 +4094,4 @@ RTM_EXPORT(rt_mq_control);
40304094

40314095
/**@}*/
40324096
#endif /* RT_USING_MESSAGEQUEUE */
4033-
/**@}*/
4097+
/**@}*/

src/thread.c

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,7 @@ static void _thread_detach_from_mutex(rt_thread_t thread)
101101
{
102102
mutex = rt_list_entry(node, struct rt_mutex, taken_list);
103103
LOG_D("Thread [%s] exits while holding mutex [%s].\n", thread->parent.name, mutex->parent.parent.name);
104-
/* recursively take */
105-
mutex->hold = 1;
106-
rt_mutex_release(mutex);
104+
rt_mutex_force_release(mutex);
107105
}
108106

109107
rt_spin_unlock_irqrestore(&thread->spinlock, level);

0 commit comments

Comments
 (0)