1 /*
2  * Copyright (c) 2006-2022, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2024-08-30     heyuanjie87  the first version
9  *
10  */
11 
12 #include <rthw.h>
13 #include <rtthread.h>
14 
15 #ifndef SYSTEM_THREAD_STACK_SIZE
16 #define SYSTEM_THREAD_STACK_SIZE IDLE_THREAD_STACK_SIZE
17 #endif
18 static rt_list_t          _rt_thread_defunct = RT_LIST_OBJECT_INIT(_rt_thread_defunct);
19 static struct rt_spinlock _defunct_spinlock;
20 #if defined(RT_USING_SMP) || defined(RT_USING_SMART)
21 static struct rt_thread rt_system_thread;
22 rt_align(RT_ALIGN_SIZE) static rt_uint8_t rt_system_stack[SYSTEM_THREAD_STACK_SIZE];
23 static struct rt_semaphore system_sem;
24 #endif
25 
26 /**
27  * @brief Enqueue a thread to defunct queue.
28  *
29  * @param thread the thread to be enqueued.
30  *
31  * @note It must be called between rt_hw_interrupt_disable and rt_hw_interrupt_enable
32  */
rt_thread_defunct_enqueue(rt_thread_t thread)33 void rt_thread_defunct_enqueue(rt_thread_t thread)
34 {
35     rt_base_t level;
36     level = rt_spin_lock_irqsave(&_defunct_spinlock);
37     rt_list_insert_after(&_rt_thread_defunct, &RT_THREAD_LIST_NODE(thread));
38     rt_spin_unlock_irqrestore(&_defunct_spinlock, level);
39 #if defined(RT_USING_SMP) || defined(RT_USING_SMART)
40     rt_sem_release(&system_sem);
41 #endif
42 }
43 
44 /**
45  * @brief Dequeue a thread from defunct queue.
46  */
rt_thread_defunct_dequeue(void)47 rt_thread_t rt_thread_defunct_dequeue(void)
48 {
49     rt_base_t   level;
50     rt_thread_t thread = RT_NULL;
51     rt_list_t  *l      = &_rt_thread_defunct;
52 
53     level = rt_spin_lock_irqsave(&_defunct_spinlock);
54     if (!rt_list_isempty(l))
55     {
56         thread = RT_THREAD_LIST_NODE_ENTRY(l->next);
57         rt_list_remove(&RT_THREAD_LIST_NODE(thread));
58     }
59     rt_spin_unlock_irqrestore(&_defunct_spinlock, level);
60 
61     return thread;
62 }
63 
64 /**
65  * @brief This function will perform system background job when system idle.
66  */
rt_defunct_execute(void)67 void rt_defunct_execute(void)
68 {
69     /* Loop until there is no dead thread. So one call to rt_defunct_execute
70      * will do all the cleanups. */
71     while (1)
72     {
73         rt_thread_t thread;
74         rt_bool_t   object_is_systemobject;
75         void (*cleanup)(struct rt_thread *tid);
76 
77 #ifdef RT_USING_MODULE
78         struct rt_dlmodule *module = RT_NULL;
79 #endif
80         /* get defunct thread */
81         thread = rt_thread_defunct_dequeue();
82         if (thread == RT_NULL)
83         {
84             break;
85         }
86 
87 #ifdef RT_USING_MODULE
88         module = (struct rt_dlmodule *)thread->parent.module_id;
89         if (module)
90         {
91             dlmodule_destroy(module);
92         }
93 #endif
94 
95 #ifdef RT_USING_SIGNALS
96         rt_thread_free_sig(thread);
97 #endif
98 
99         /* store the point of "thread->cleanup" avoid to lose */
100         cleanup = thread->cleanup;
101 
102         /* if it's a system object, detach it */
103         object_is_systemobject = rt_object_is_systemobject((rt_object_t)thread);
104         if (object_is_systemobject == RT_TRUE)
105         {
106             /* detach this object */
107             rt_object_detach((rt_object_t)thread);
108         }
109 
110         /* invoke thread cleanup */
111         if (cleanup != RT_NULL)
112         {
113             cleanup(thread);
114         }
115 
116 #ifdef RT_USING_HEAP
117 #ifdef RT_USING_MEM_PROTECTION
118         if (thread->mem_regions != RT_NULL)
119         {
120             RT_KERNEL_FREE(thread->mem_regions);
121         }
122 #endif
123         /* if need free, delete it */
124         if (object_is_systemobject == RT_FALSE)
125         {
126             /* release thread's stack */
127 #ifdef RT_USING_HW_STACK_GUARD
128             RT_KERNEL_FREE(thread->stack_buf);
129 #else
130             RT_KERNEL_FREE(thread->stack_addr);
131 #endif
132             /* delete thread object */
133             rt_object_delete((rt_object_t)thread);
134         }
135 #endif
136     }
137 }
138 
139 #if defined(RT_USING_SMP) || defined(RT_USING_SMART)
rt_thread_system_entry(void * parameter)140 static void rt_thread_system_entry(void *parameter)
141 {
142     RT_UNUSED(parameter);
143 
144     while (1)
145     {
146         int ret = rt_sem_take(&system_sem, RT_WAITING_FOREVER);
147         if (ret != RT_EOK)
148         {
149             rt_kprintf("failed to sem_take() error %d\n", ret);
150             RT_ASSERT(0);
151         }
152         rt_defunct_execute();
153     }
154 }
155 #endif
156 
rt_thread_defunct_init(void)157 void rt_thread_defunct_init(void)
158 {
159     RT_ASSERT(RT_THREAD_PRIORITY_MAX > 2);
160 
161     rt_spin_lock_init(&_defunct_spinlock);
162 
163 #if defined(RT_USING_SMP) || defined(RT_USING_SMART)
164     rt_sem_init(&system_sem, "defunct", 0, RT_IPC_FLAG_FIFO);
165 
166     /* create defunct thread */
167     rt_thread_init(&rt_system_thread,
168                    "tsystem",
169                    rt_thread_system_entry,
170                    RT_NULL,
171                    rt_system_stack,
172                    sizeof(rt_system_stack),
173                    RT_THREAD_PRIORITY_MAX - 2,
174                    32);
175     /* startup */
176     rt_thread_startup(&rt_system_thread);
177 #endif
178 }
179