From 2c734c17e1bb134c40cf95314d58543e1360b89b Mon Sep 17 00:00:00 2001 From: Rbb666 Date: Fri, 26 Jun 2026 12:28:51 +0800 Subject: [PATCH] [kernel] Prevent duplicate defunct enqueue for deleted threads. --- include/rtsched.h | 1 + src/scheduler_comm.c | 1 + src/thread.c | 38 +++++++++++++++++++---- src/utest/thread_tc.c | 71 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 105 insertions(+), 6 deletions(-) diff --git a/include/rtsched.h b/include/rtsched.h index 5bafea93eb2..9dafd71598f 100644 --- a/include/rtsched.h +++ b/include/rtsched.h @@ -53,6 +53,7 @@ struct rt_sched_thread_ctx rt_uint8_t stat; /**< thread status */ rt_uint8_t sched_flag_locked:1; /**< calling thread have the scheduler locked */ rt_uint8_t sched_flag_ttmr_set:1; /**< thread timer is start */ + rt_uint8_t sched_flag_defunct:1; /**< thread has been enqueued to defunct */ #ifdef ARCH_USING_HW_THREAD_SELF rt_uint8_t critical_switch_flag:1; /**< critical switch pending */ diff --git a/src/scheduler_comm.c b/src/scheduler_comm.c index 84ac0a7416c..79e4d0b0022 100644 --- a/src/scheduler_comm.c +++ b/src/scheduler_comm.c @@ -35,6 +35,7 @@ void rt_sched_thread_init_ctx(struct rt_thread *thread, rt_uint32_t tick, rt_uin { /* setup thread status */ RT_SCHED_CTX(thread).stat = RT_THREAD_INIT; + RT_SCHED_CTX(thread).sched_flag_defunct = 0; #ifdef RT_USING_SMP /* not bind on any cpu */ diff --git a/src/thread.c b/src/thread.c index 745e3f774f2..266d787f3af 100644 --- a/src/thread.c +++ b/src/thread.c @@ -114,6 +114,22 @@ static void _thread_detach_from_mutex(rt_thread_t thread) static void _thread_detach_from_mutex(rt_thread_t thread) {} #endif +static rt_err_t _thread_mark_defunct(rt_thread_t thread) +{ + rt_err_t error = -RT_ERROR; + rt_base_t level; + + level = rt_spin_lock_irqsave(&thread->spinlock); + if (!RT_SCHED_CTX(thread).sched_flag_defunct) + { + RT_SCHED_CTX(thread).sched_flag_defunct = 1; + error = RT_EOK; + } + rt_spin_unlock_irqrestore(&thread->spinlock, level); + + return error; +} + static void _thread_exit(void) { struct rt_thread *thread; @@ -126,10 +142,13 @@ static void _thread_exit(void) rt_thread_close(thread); - _thread_detach_from_mutex(thread); + if (_thread_mark_defunct(thread) == RT_EOK) + { + _thread_detach_from_mutex(thread); - /* insert to defunct thread list */ - rt_thread_defunct_enqueue(thread); + /* insert to defunct thread list */ + rt_thread_defunct_enqueue(thread); + } rt_exit_critical_safe(critical_level); @@ -513,10 +532,17 @@ static rt_err_t _thread_detach(rt_thread_t thread) error = rt_thread_close(thread); - _thread_detach_from_mutex(thread); + if (error == RT_EOK) + { + error = _thread_mark_defunct(thread); + if (error == RT_EOK) + { + _thread_detach_from_mutex(thread); - /* insert to defunct thread list */ - rt_thread_defunct_enqueue(thread); + /* insert to defunct thread list */ + rt_thread_defunct_enqueue(thread); + } + } rt_exit_critical_safe(critical_level); return error; diff --git a/src/utest/thread_tc.c b/src/utest/thread_tc.c index 7e859d40f3c..b78d6e8543e 100644 --- a/src/utest/thread_tc.c +++ b/src/utest/thread_tc.c @@ -26,6 +26,7 @@ * * Test Scenarios: * - Create, start, and delete a dynamic thread to verify dynamic thread lifecycle management + * - Let a dynamic thread exit normally, then call rt_thread_delete again in cleanup to verify duplicate delete handling * - Initialize, start, and detach a static thread to verify static thread lifecycle management * - Delay a thread for a specific tick count and check timing accuracy * - Register and remove an idle hook, verifying it is called as expected @@ -37,6 +38,7 @@ * * Verification Metrics: * - Threads are created, started, deleted, and detached successfully + * - Repeated deletion after normal thread exit returns an error without abnormal cleanup * - The precision of both relative delay and absolute delay is correct. * - Idle hook is invoked as expected during thread idle periods * - Thread yield causes correct scheduling among threads of the same priority @@ -71,6 +73,7 @@ static struct rt_thread thread2; static rt_thread_t tid5 = RT_NULL; static rt_thread_t tid6 = RT_NULL; static rt_thread_t tid7 = RT_NULL; + static rt_thread_t tid9 = RT_NULL; #endif /* RT_USING_HEAP */ static volatile rt_uint32_t tid3_delay_pass_flag = 0; @@ -78,6 +81,9 @@ static volatile rt_uint32_t tid3_finish_flag = 0; static volatile rt_uint32_t tid4_finish_flag = 0; static volatile rt_uint32_t tid6_finish_flag = 0; static volatile rt_uint32_t thread5_source = 0; +static rt_atomic_t thread9_exit_flag = 0; +static rt_atomic_t thread9_cleanup_flag = 0; +static rt_atomic_t thread9_delete_ret = -RT_ERROR; #ifndef RT_USING_SMP static volatile rt_uint32_t thread_yield_flag = 0; @@ -452,6 +458,66 @@ static void test_thread_priority(void) return; } +#ifdef RT_USING_HEAP +static void thread9_entry(void *parameter) +{ + RT_UNUSED(parameter); + + while (rt_atomic_load(&thread9_exit_flag) == 0) + { + rt_thread_mdelay(1); + } +} + +static void thread9_cleanup(struct rt_thread *thread) +{ + rt_atomic_store(&thread9_delete_ret, rt_thread_delete(thread)); + rt_atomic_store(&thread9_cleanup_flag, 1); +} + +static void test_thread_exit_delete_again(void) +{ + rt_err_t ret_startup = -RT_ERROR; + + rt_atomic_store(&thread9_exit_flag, 0); + rt_atomic_store(&thread9_cleanup_flag, 0); + rt_atomic_store(&thread9_delete_ret, RT_EOK); + + tid9 = rt_thread_create("thread9", + thread9_entry, + RT_NULL, + THREAD_STACK_SIZE, + UTEST_THR_PRIORITY - 1, + THREAD_TIMESLICE); + if (tid9 == RT_NULL) + { + LOG_E("rt_thread_create failed!"); + uassert_false(tid9 == RT_NULL); + return; + } + + tid9->cleanup = thread9_cleanup; + + ret_startup = rt_thread_startup(tid9); + if (ret_startup != RT_EOK) + { + LOG_E("rt_thread_startup failed!"); + uassert_false(ret_startup != RT_EOK); + rt_thread_delete(tid9); + return; + } + + rt_atomic_store(&thread9_exit_flag, 1); + + while (rt_atomic_load(&thread9_cleanup_flag) == 0) + { + rt_thread_mdelay(1); + } + + uassert_int_equal(rt_atomic_load(&thread9_delete_ret), -RT_ERROR); +} +#endif /* RT_USING_HEAP */ + static void test_delay_until(void) { rt_tick_t tick; @@ -756,6 +822,9 @@ static rt_err_t utest_tc_init(void) tid3_finish_flag = 0; tid4_finish_flag = 0; tid6_finish_flag = 0; + rt_atomic_store(&thread9_exit_flag, 0); + rt_atomic_store(&thread9_cleanup_flag, 0); + rt_atomic_store(&thread9_delete_ret, -RT_ERROR); entry_idle_hook_times = 0; count = 0; return RT_EOK; @@ -772,6 +841,8 @@ static void testcase(void) UTEST_UNIT_RUN(test_static_thread); /* create, delete */ UTEST_UNIT_RUN(test_dynamic_thread); + /* exit, duplicate delete */ + UTEST_UNIT_RUN(test_thread_exit_delete_again); /* delay */ UTEST_UNIT_RUN(test_thread_delay); /* idle_sethook, idle_delhook */