summaryrefslogtreecommitdiff
path: root/src/utest
diff options
context:
space:
mode:
author蒙蒙plus <[email protected]>2026-07-15 11:02:13 +0800
committerRbb666 <[email protected]>2026-07-22 09:45:59 +0800
commit7e451489778af014081f5ca3bf356fd0f62083a9 (patch)
treeef4129c5e2937b64664b1be871afe65f48857222 /src/utest
parentc49320d57d324d789703d428b5fd78ca5413af4d (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.c115
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);
}