Skip to content

Commit 3c48de7

Browse files
committed
smp: ensure safe thread termination in rt_thread_close
1 parent f9564d4 commit 3c48de7

File tree

1 file changed

+87
-8
lines changed

1 file changed

+87
-8
lines changed

src/thread.c

Lines changed: 87 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ static void _thread_detach_from_mutex(rt_thread_t thread) {}
116116

117117
static void _thread_exit(void)
118118
{
119+
rt_err_t error;
119120
struct rt_thread *thread;
120121
rt_base_t critical_level;
121122

@@ -124,12 +125,19 @@ static void _thread_exit(void)
124125

125126
critical_level = rt_enter_critical();
126127

127-
rt_thread_close(thread);
128+
error=rt_thread_close(thread);
128129

129-
_thread_detach_from_mutex(thread);
130+
/**
131+
* this check prevents concurrent _thread_detach operations by other threads.
132+
* proceeds only when the current thread has successfully closed the target thread.
133+
*/
134+
if(error == RT_EOK)
135+
{
136+
_thread_detach_from_mutex(thread);
130137

131-
/* insert to defunct thread list */
132-
rt_thread_defunct_enqueue(thread);
138+
/* insert to defunct thread list */
139+
rt_thread_defunct_enqueue(thread);
140+
}
133141

134142
rt_exit_critical_safe(critical_level);
135143

@@ -423,12 +431,15 @@ RTM_EXPORT(rt_thread_startup);
423431
*/
424432
rt_err_t rt_thread_close(rt_thread_t thread)
425433
{
434+
rt_err_t error;
426435
rt_sched_lock_level_t slvl;
427436
rt_uint8_t thread_status;
428437

429438
/* forbid scheduling on current core if closing current thread */
430439
RT_ASSERT(thread != rt_thread_self() || rt_critical_level());
431440

441+
error = RT_EOK;
442+
432443
/* before checking status of scheduler */
433444
rt_sched_lock(&slvl);
434445

@@ -447,12 +458,71 @@ rt_err_t rt_thread_close(rt_thread_t thread)
447458

448459
/* change stat */
449460
rt_sched_thread_close(thread);
461+
462+
#ifdef RT_USING_SMP
463+
int cpu_id;
464+
rt_tick_t start_tick = rt_tick_get();
465+
/**
466+
* using conservative 500ms timeout, may adjust based on
467+
* hardware characteristics and system load.
468+
*/
469+
rt_tick_t timeout = RT_TICK_PER_SECOND >> 1;
470+
rt_bool_t need_wait = RT_FALSE;
471+
472+
/**
473+
* in SMP, the current thread and target thread may run on different CPUs.
474+
* although we set the target thread's state to closed, it may still execute
475+
* on another CPU until rescheduled. send IPI to force immediate rescheduling.
476+
*/
477+
cpu_id = RT_SCHED_CTX(thread).oncpu;
478+
rt_sched_unlock(slvl);
479+
if ((cpu_id != RT_CPU_DETACHED) && (cpu_id != rt_cpu_get_id()))
480+
{
481+
rt_hw_ipi_send(RT_SCHEDULE_IPI, RT_CPU_MASK ^ (1 << cpu_id));
482+
need_wait = RT_TRUE;
483+
}
484+
485+
/**
486+
* continuously check if target thread has detached from CPU core.
487+
* this loop ensures the thread fully stops before resource cleanup.
488+
* a timeout prevents deadlock if thread fails to detach promptly.
489+
*/
490+
while (need_wait)
491+
{
492+
if (rt_tick_get_delta(start_tick) >= timeout)
493+
{
494+
LOG_D("Timeout waiting for thread %s (tid=%p) to detach from CPU%d",
495+
RT_NAME_MAX, thread->parent.name, thread, cpu_id);
496+
error = -RT_ETIMEOUT;
497+
break;
498+
}
499+
500+
rt_sched_lock(&slvl);
501+
cpu_id = RT_SCHED_CTX(thread).oncpu;
502+
rt_sched_unlock(slvl);
503+
504+
if (cpu_id == RT_CPU_DETACHED)
505+
{
506+
break;
507+
}
508+
}
509+
510+
return error;
511+
#endif
512+
}
513+
else
514+
{
515+
/**
516+
* avoid duplicate closing: if the thread is already closed, return an error.
517+
* this prevents race conditions when multiple threads call _thread_detach/_thread_exit concurrently.
518+
*/
519+
error = -RT_ERROR;
450520
}
451521

452522
/* scheduler works are done */
453523
rt_sched_unlock(slvl);
454524

455-
return RT_EOK;
525+
return error;
456526
}
457527
RTM_EXPORT(rt_thread_close);
458528

@@ -491,10 +561,18 @@ static rt_err_t _thread_detach(rt_thread_t thread)
491561

492562
error = rt_thread_close(thread);
493563

494-
_thread_detach_from_mutex(thread);
564+
/**
565+
* prevents concurrent thread cleanup operations by synchronizing between external
566+
* _thread_detach calls and the target thread's own _thread_exit path.
567+
* proceeds only when the current thread has successfully closed the target thread.
568+
*/
569+
if (error == RT_EOK)
570+
{
571+
_thread_detach_from_mutex(thread);
495572

496-
/* insert to defunct thread list */
497-
rt_thread_defunct_enqueue(thread);
573+
/* insert to defunct thread list */
574+
rt_thread_defunct_enqueue(thread);
575+
}
498576

499577
rt_exit_critical_safe(critical_level);
500578
return error;
@@ -1143,3 +1221,4 @@ rt_err_t rt_thread_get_name(rt_thread_t thread, char *name, rt_uint8_t name_size
11431221
RTM_EXPORT(rt_thread_get_name);
11441222

11451223
/**@}*/
1224+

0 commit comments

Comments
 (0)