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 * 2006-03-23 Bernard the first version
9 * 2010-11-10 Bernard add cleanup callback function in thread exit.
10 * 2012-12-29 Bernard fix compiling warning.
11 * 2013-12-21 Grissiom let rt_thread_idle_excute loop until there is no
12 * dead thread.
13 * 2016-08-09 ArdaFu add method to get the handler of the idle thread.
14 * 2018-02-07 Bernard lock scheduler to protect tid->cleanup.
15 * 2018-07-14 armink add idle hook list
16 * 2018-11-22 Jesven add per cpu idle task
17 * combine the code of primary and secondary cpu
18 * 2021-11-15 THEWON Remove duplicate work between idle and _thread_exit
19 * 2023-09-15 xqyjlj perf rt_hw_interrupt_disable/enable
20 * 2023-11-07 xqyjlj fix thread exit
21 * 2023-12-10 xqyjlj add _hook_spinlock
22 */
23
24 #include <rthw.h>
25 #include <rtthread.h>
26
27 #ifdef RT_USING_MODULE
28 #include <dlmodule.h>
29 #endif /* RT_USING_MODULE */
30
31 #ifdef RT_USING_HOOK
32 #ifndef RT_USING_IDLE_HOOK
33 #define RT_USING_IDLE_HOOK
34 #endif /* RT_USING_IDLE_HOOK */
35 #endif /* RT_USING_HOOK */
36
37 #ifndef IDLE_THREAD_STACK_SIZE
38 #if defined (RT_USING_IDLE_HOOK) || defined(RT_USING_HEAP)
39 #define IDLE_THREAD_STACK_SIZE 256
40 #else
41 #define IDLE_THREAD_STACK_SIZE 128
42 #endif /* (RT_USING_IDLE_HOOK) || defined(RT_USING_HEAP) */
43 #endif /* IDLE_THREAD_STACK_SIZE */
44
45 #define _CPUS_NR RT_CPUS_NR
46
47 static struct rt_thread idle_thread[_CPUS_NR];
48 rt_align(RT_ALIGN_SIZE)
49 static rt_uint8_t idle_thread_stack[_CPUS_NR][IDLE_THREAD_STACK_SIZE];
50
51 #ifdef RT_USING_IDLE_HOOK
52 #ifndef RT_IDLE_HOOK_LIST_SIZE
53 #define RT_IDLE_HOOK_LIST_SIZE 4
54 #endif /* RT_IDLE_HOOK_LIST_SIZE */
55
56 static void (*idle_hook_list[RT_IDLE_HOOK_LIST_SIZE])(void);
57 static struct rt_spinlock _hook_spinlock;
58
59 /**
60 * @brief This function sets a hook function to idle thread loop. When the system performs
61 * idle loop, this hook function should be invoked.
62 *
63 * @param hook the specified hook function.
64 *
65 * @return RT_EOK: set OK.
66 * -RT_EFULL: hook list is full.
67 *
68 * @note the hook function must be simple and never be blocked or suspend.
69 */
rt_thread_idle_sethook(void (* hook)(void))70 rt_err_t rt_thread_idle_sethook(void (*hook)(void))
71 {
72 rt_size_t i;
73 rt_err_t ret = -RT_EFULL;
74 rt_base_t level;
75
76 level = rt_spin_lock_irqsave(&_hook_spinlock);
77
78 for (i = 0; i < RT_IDLE_HOOK_LIST_SIZE; i++)
79 {
80 if (idle_hook_list[i] == RT_NULL)
81 {
82 idle_hook_list[i] = hook;
83 ret = RT_EOK;
84 break;
85 }
86 }
87
88 rt_spin_unlock_irqrestore(&_hook_spinlock, level);
89
90 return ret;
91 }
92
93 /**
94 * @brief delete the idle hook on hook list.
95 *
96 * @param hook the specified hook function.
97 *
98 * @return RT_EOK: delete OK.
99 * -RT_ENOSYS: hook was not found.
100 */
rt_thread_idle_delhook(void (* hook)(void))101 rt_err_t rt_thread_idle_delhook(void (*hook)(void))
102 {
103 rt_size_t i;
104 rt_err_t ret = -RT_ENOSYS;
105 rt_base_t level;
106
107 level = rt_spin_lock_irqsave(&_hook_spinlock);
108
109 for (i = 0; i < RT_IDLE_HOOK_LIST_SIZE; i++)
110 {
111 if (idle_hook_list[i] == hook)
112 {
113 idle_hook_list[i] = RT_NULL;
114 ret = RT_EOK;
115 break;
116 }
117 }
118
119 rt_spin_unlock_irqrestore(&_hook_spinlock, level);
120
121 return ret;
122 }
123
124 #endif /* RT_USING_IDLE_HOOK */
125
idle_thread_entry(void * parameter)126 static void idle_thread_entry(void *parameter)
127 {
128 RT_UNUSED(parameter);
129 #ifdef RT_USING_SMP
130 if (rt_cpu_get_id() != 0)
131 {
132 while (1)
133 {
134 rt_hw_secondary_cpu_idle_exec();
135 }
136 }
137 #endif /* RT_USING_SMP */
138
139 while (1)
140 {
141 #ifdef RT_USING_IDLE_HOOK
142 rt_size_t i;
143 void (*idle_hook)(void);
144
145 for (i = 0; i < RT_IDLE_HOOK_LIST_SIZE; i++)
146 {
147 idle_hook = idle_hook_list[i];
148 if (idle_hook != RT_NULL)
149 {
150 idle_hook();
151 }
152 }
153 #endif /* RT_USING_IDLE_HOOK */
154
155 #if !defined(RT_USING_SMP) && !defined(RT_USING_SMART)
156 rt_defunct_execute();
157 #endif
158
159 #ifdef RT_USING_PM
160 void rt_system_power_manager(void);
161 rt_system_power_manager();
162 #endif /* RT_USING_PM */
163 }
164 }
165
166 /**
167 * @brief This function will initialize idle thread, then start it.
168 *
169 * @note this function must be invoked when system init.
170 */
rt_thread_idle_init(void)171 void rt_thread_idle_init(void)
172 {
173 rt_ubase_t i;
174 #if RT_NAME_MAX > 0
175 char idle_thread_name[RT_NAME_MAX];
176 #endif /* RT_NAME_MAX > 0 */
177
178 #ifdef RT_USING_IDLE_HOOK
179 rt_spin_lock_init(&_hook_spinlock);
180 #endif
181
182 for (i = 0; i < _CPUS_NR; i++)
183 {
184 #if RT_NAME_MAX > 0
185 rt_snprintf(idle_thread_name, RT_NAME_MAX, "tidle%d", i);
186 #endif /* RT_NAME_MAX > 0 */
187 rt_thread_init(&idle_thread[i],
188 #if RT_NAME_MAX > 0
189 idle_thread_name,
190 #else
191 "tidle",
192 #endif /* RT_NAME_MAX > 0 */
193 idle_thread_entry,
194 RT_NULL,
195 &idle_thread_stack[i][0],
196 sizeof(idle_thread_stack[i]),
197 RT_THREAD_PRIORITY_MAX - 1,
198 32);
199 #ifdef RT_USING_SMP
200 rt_thread_control(&idle_thread[i], RT_THREAD_CTRL_BIND_CPU, (void*)i);
201 #endif /* RT_USING_SMP */
202
203 /* update */
204 rt_cpu_index(i)->idle_thread = &idle_thread[i];
205
206 /* startup */
207 rt_thread_startup(&idle_thread[i]);
208 }
209 }
210
211 /**
212 * @brief This function will get the handler of the idle thread.
213 */
rt_thread_idle_gethandler(void)214 rt_thread_t rt_thread_idle_gethandler(void)
215 {
216 int id = rt_cpu_get_id();
217
218 return (rt_thread_t)(&idle_thread[id]);
219 }
220