idle.c 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. /*
  2. * File : idle.c
  3. * This file is part of RT-Thread RTOS
  4. * COPYRIGHT (C) 2006 - 2012, RT-Thread Development Team
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation; either version 2 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License along
  17. * with this program; if not, write to the Free Software Foundation, Inc.,
  18. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  19. *
  20. * Change Logs:
  21. * Date Author Notes
  22. * 2006-03-23 Bernard the first version
  23. * 2010-11-10 Bernard add cleanup callback function in thread exit.
  24. * 2012-12-29 Bernard fix compiling warning.
  25. * 2013-12-21 Grissiom let rt_thread_idle_excute loop until there is no
  26. * dead thread.
  27. * 2016-08-09 ArdaFu add method to get the handler of the idle thread.
  28. * 2018-02-07 Bernard lock scheduler to protect tid->cleanup.
  29. * 2018-07-14 armink add idle hook list
  30. */
  31. #include <rthw.h>
  32. #include <rtthread.h>
  33. #ifdef RT_USING_MODULE
  34. #include <dlmodule.h>
  35. #endif
  36. #if defined (RT_USING_HOOK)
  37. #ifndef RT_USING_IDLE_HOOK
  38. #define RT_USING_IDLE_HOOK
  39. #endif
  40. #endif
  41. #ifndef IDLE_THREAD_STACK_SIZE
  42. #if defined (RT_USING_IDLE_HOOK) || defined(RT_USING_HEAP)
  43. #define IDLE_THREAD_STACK_SIZE 256
  44. #else
  45. #define IDLE_THREAD_STACK_SIZE 128
  46. #endif
  47. #endif
  48. static struct rt_thread idle;
  49. ALIGN(RT_ALIGN_SIZE)
  50. static rt_uint8_t rt_thread_stack[IDLE_THREAD_STACK_SIZE];
  51. extern rt_list_t rt_thread_defunct;
  52. #ifdef RT_USING_IDLE_HOOK
  53. #ifndef RT_IDEL_HOOK_LIST_SIZE
  54. #define RT_IDEL_HOOK_LIST_SIZE 4
  55. #endif
  56. static void (*idle_hook_list[RT_IDEL_HOOK_LIST_SIZE])();
  57. /**
  58. * @ingroup Hook
  59. * This function sets a hook function to idle thread loop. When the system performs
  60. * idle loop, this hook function should be invoked.
  61. *
  62. * @param hook the specified hook function
  63. *
  64. * @return RT_EOK: set OK
  65. * -RT_EFULL: hook list is full
  66. *
  67. * @note the hook function must be simple and never be blocked or suspend.
  68. */
  69. rt_err_t rt_thread_idle_sethook(void (*hook)(void))
  70. {
  71. rt_size_t i;
  72. rt_base_t level;
  73. rt_err_t ret = -RT_EFULL;
  74. /* disable interrupt */
  75. level = rt_hw_interrupt_disable();
  76. for (i = 0; i < RT_IDEL_HOOK_LIST_SIZE; i++)
  77. {
  78. if (idle_hook_list[i] == RT_NULL)
  79. {
  80. idle_hook_list[i] = hook;
  81. ret = RT_EOK;
  82. break;
  83. }
  84. }
  85. /* enable interrupt */
  86. rt_hw_interrupt_enable(level);
  87. return ret;
  88. }
  89. /**
  90. * delete the idle hook on hook list
  91. *
  92. * @param hook the specified hook function
  93. *
  94. * @return RT_EOK: delete OK
  95. * -RT_ENOSYS: hook was not found
  96. */
  97. rt_err_t rt_thread_idle_delhook(void (*hook)(void))
  98. {
  99. rt_size_t i;
  100. rt_base_t level;
  101. rt_err_t ret = -RT_ENOSYS;
  102. /* disable interrupt */
  103. level = rt_hw_interrupt_disable();
  104. for (i = 0; i < RT_IDEL_HOOK_LIST_SIZE; i++)
  105. {
  106. if (idle_hook_list[i] == hook)
  107. {
  108. idle_hook_list[i] = RT_NULL;
  109. ret = RT_EOK;
  110. break;
  111. }
  112. }
  113. /* enable interrupt */
  114. rt_hw_interrupt_enable(level);
  115. return ret;
  116. }
  117. #endif
  118. /* Return whether there is defunctional thread to be deleted. */
  119. rt_inline int _has_defunct_thread(void)
  120. {
  121. /* The rt_list_isempty has prototype of "int rt_list_isempty(const rt_list_t *l)".
  122. * So the compiler has a good reason that the rt_thread_defunct list does
  123. * not change within rt_thread_idle_excute thus optimize the "while" loop
  124. * into a "if".
  125. *
  126. * So add the volatile qualifier here. */
  127. const volatile rt_list_t *l = (const volatile rt_list_t *)&rt_thread_defunct;
  128. return l->next != l;
  129. }
  130. /**
  131. * @ingroup Thread
  132. *
  133. * This function will perform system background job when system idle.
  134. */
  135. void rt_thread_idle_excute(void)
  136. {
  137. /* Loop until there is no dead thread. So one call to rt_thread_idle_excute
  138. * will do all the cleanups. */
  139. while (_has_defunct_thread())
  140. {
  141. rt_base_t lock;
  142. rt_thread_t thread;
  143. #ifdef RT_USING_MODULE
  144. struct rt_dlmodule *module = RT_NULL;
  145. #endif
  146. RT_DEBUG_NOT_IN_INTERRUPT;
  147. /* disable interrupt */
  148. lock = rt_hw_interrupt_disable();
  149. /* re-check whether list is empty */
  150. if (_has_defunct_thread())
  151. {
  152. /* get defunct thread */
  153. thread = rt_list_entry(rt_thread_defunct.next,
  154. struct rt_thread,
  155. tlist);
  156. #ifdef RT_USING_MODULE
  157. module = (struct rt_dlmodule*)thread->module_id;
  158. if (module)
  159. {
  160. dlmodule_destroy(module);
  161. }
  162. #endif
  163. /* remove defunct thread */
  164. rt_list_remove(&(thread->tlist));
  165. /* lock scheduler to prevent scheduling in cleanup function. */
  166. rt_enter_critical();
  167. /* invoke thread cleanup */
  168. if (thread->cleanup != RT_NULL)
  169. thread->cleanup(thread);
  170. #ifdef RT_USING_SIGNALS
  171. rt_thread_free_sig(thread);
  172. #endif
  173. /* if it's a system object, not delete it */
  174. if (rt_object_is_systemobject((rt_object_t)thread) == RT_TRUE)
  175. {
  176. /* unlock scheduler */
  177. rt_exit_critical();
  178. /* enable interrupt */
  179. rt_hw_interrupt_enable(lock);
  180. return;
  181. }
  182. /* unlock scheduler */
  183. rt_exit_critical();
  184. }
  185. else
  186. {
  187. /* enable interrupt */
  188. rt_hw_interrupt_enable(lock);
  189. /* may the defunct thread list is removed by others, just return */
  190. return;
  191. }
  192. /* enable interrupt */
  193. rt_hw_interrupt_enable(lock);
  194. #ifdef RT_USING_HEAP
  195. /* release thread's stack */
  196. RT_KERNEL_FREE(thread->stack_addr);
  197. /* delete thread object */
  198. rt_object_delete((rt_object_t)thread);
  199. #endif
  200. }
  201. }
  202. static void rt_thread_idle_entry(void *parameter)
  203. {
  204. #ifdef RT_USING_IDLE_HOOK
  205. rt_size_t i;
  206. #endif
  207. while (1)
  208. {
  209. #ifdef RT_USING_IDLE_HOOK
  210. for (i = 0; i < RT_IDEL_HOOK_LIST_SIZE; i++)
  211. {
  212. if (idle_hook_list[i] != RT_NULL)
  213. {
  214. idle_hook_list[i]();
  215. }
  216. }
  217. #endif
  218. rt_thread_idle_excute();
  219. }
  220. }
  221. /**
  222. * @ingroup SystemInit
  223. *
  224. * This function will initialize idle thread, then start it.
  225. *
  226. * @note this function must be invoked when system init.
  227. */
  228. void rt_thread_idle_init(void)
  229. {
  230. /* initialize thread */
  231. rt_thread_init(&idle,
  232. "tidle",
  233. rt_thread_idle_entry,
  234. RT_NULL,
  235. &rt_thread_stack[0],
  236. sizeof(rt_thread_stack),
  237. RT_THREAD_PRIORITY_MAX - 1,
  238. 32);
  239. /* startup */
  240. rt_thread_startup(&idle);
  241. }
  242. /**
  243. * @ingroup Thread
  244. *
  245. * This function will get the handler of the idle thread.
  246. *
  247. */
  248. rt_thread_t rt_thread_idle_gethandler(void)
  249. {
  250. return (rt_thread_t)(&idle);
  251. }