diff options
| author | 蒙蒙plus <[email protected]> | 2026-07-15 11:02:13 +0800 |
|---|---|---|
| committer | Rbb666 <[email protected]> | 2026-07-22 09:45:59 +0800 |
| commit | 7e451489778af014081f5ca3bf356fd0f62083a9 (patch) | |
| tree | ef4129c5e2937b64664b1be871afe65f48857222 /src/utest | |
| parent | c49320d57d324d789703d428b5fd78ca5413af4d (diff) | |
[mutex] 跨线程 rt_thread_delete 被删线程持有的mutex也一并释放
- 增加单元测试
Clarified mutex release conditions and added exception handling for closed owner threads.
Diffstat (limited to 'src/utest')
| -rw-r--r-- | src/utest/mutex_tc.c | 115 |
1 files changed, 115 insertions, 0 deletions
diff --git a/src/utest/mutex_tc.c b/src/utest/mutex_tc.c index 514ded6300..02cf557199 100644 --- a/src/utest/mutex_tc.c +++ b/src/utest/mutex_tc.c @@ -7,6 +7,7 @@ * Date Author Notes * 2021-09.01 luckyzjq the first version * 2023-09-15 xqyjlj change stack size in cpu64 + * 2026-07-15 meng-plus add cross-thread delete owner regression (#11619) */ /** @@ -22,6 +23,7 @@ * - Priority inheritance when high-priority threads are blocked by lower-priority holders * - Behavior differences between static and dynamic mutexes * - Mutex release error handling, invalid release, and cleanup + * - Cross-thread delete of a mutex owner must release the lock and wake waiters * Verification Metrics: * - Correct return codes for all mutex operations (RT_EOK, timeouts, error states) * - Proper priority inheritance and restoration during contention @@ -790,6 +792,118 @@ static void test_recurse_lock(void) uassert_true(result == RT_EOK); } +#ifdef RT_USING_HEAP +/* + * Issue #11619: deleting a thread that still holds a mutex must not leave an + * orphan lock; waiters blocked on that mutex must be woken and acquire ownership. + */ +static volatile int _holder_ready; +static volatile int _waiter_got; + +static void orphan_mutex_holder_entry(void *param) +{ + rt_mutex_t mutex = (rt_mutex_t)param; + + if (rt_mutex_take(mutex, RT_WAITING_FOREVER) != RT_EOK) + { + uassert_true(RT_FALSE); + return; + } + + _holder_ready = 1; + + /* Hold forever until deleted by another thread */ + while (1) + { + rt_thread_mdelay(1000); + } +} + +static void orphan_mutex_waiter_entry(void *param) +{ + rt_err_t result; + rt_mutex_t mutex = (rt_mutex_t)param; + + result = rt_mutex_take(mutex, RT_WAITING_FOREVER); + uassert_true(result == RT_EOK); + + result = rt_mutex_release(mutex); + uassert_true(result == RT_EOK); + + /* Signal after release so the main thread can take without racing */ + _waiter_got = 1; +} + +static void test_cross_thread_delete_mutex_owner(void) +{ + rt_err_t result; + rt_thread_t holder; + rt_thread_t waiter; + int timeout; + + _holder_ready = 0; + _waiter_got = 0; + + result = rt_mutex_init(&static_mutex, "orphan_mtx", RT_IPC_FLAG_PRIO); + uassert_true(result == RT_EOK); + + /* Lower priority holder takes the mutex and keeps it */ + holder = rt_thread_create("mtx_hold", + orphan_mutex_holder_entry, + &static_mutex, + THREAD_STACKSIZE, + 12, + 10); + uassert_true(holder != RT_NULL); + rt_thread_startup(holder); + + timeout = 100; + while ((_holder_ready == 0) && (timeout-- > 0)) + { + rt_thread_mdelay(10); + } + uassert_true(_holder_ready == 1); + + /* Higher priority waiter blocks on the held mutex */ + waiter = rt_thread_create("mtx_wait", + orphan_mutex_waiter_entry, + &static_mutex, + THREAD_STACKSIZE, + 10, + 10); + uassert_true(waiter != RT_NULL); + rt_thread_startup(waiter); + + /* Ensure waiter is pending before cross-thread delete */ + rt_thread_mdelay(50); + uassert_true(_waiter_got == 0); + + result = rt_thread_delete(holder); + uassert_true(result == RT_EOK); + + timeout = 100; + while ((_waiter_got == 0) && (timeout-- > 0)) + { + rt_thread_mdelay(10); + } + uassert_true(_waiter_got == 1); + + /* After waiter releases, mutex must be acquirable again */ + result = rt_mutex_take(&static_mutex, rt_tick_from_millisecond(500)); + uassert_true(result == RT_EOK); + uassert_true(rt_mutex_get_hold(&static_mutex) == 1); + + result = rt_mutex_release(&static_mutex); + uassert_true(result == RT_EOK); + + result = rt_mutex_detach(&static_mutex); + uassert_true(result == RT_EOK); + + /* Let waiter thread exit cleanly */ + rt_thread_mdelay(50); +} +#endif /* RT_USING_HEAP */ + static rt_err_t utest_tc_init(void) { #ifdef RT_USING_HEAP @@ -821,6 +935,7 @@ static void testcase(void) UTEST_UNIT_RUN(test_dynamic_mutex_release); UTEST_UNIT_RUN(test_dynamic_mutex_trytake); UTEST_UNIT_RUN(test_dynamic_pri_reverse); + UTEST_UNIT_RUN(test_cross_thread_delete_mutex_owner); #endif UTEST_UNIT_RUN(test_recurse_lock); } |
