waitqueue.c 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. /*
  2. * Copyright (c) 2006-2023, RT-Thread Development Team
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. *
  6. * Change Logs:
  7. * Date Author Notes
  8. * 2018/06/26 Bernard Fix the wait queue issue when wakeup a soon
  9. * to blocked thread.
  10. * 2022-01-24 THEWON let rt_wqueue_wait return thread->error when using signal
  11. * 2023-09-15 xqyjlj perf rt_hw_interrupt_disable/enable
  12. * 2023-11-21 Shell Support wakeup_all
  13. */
  14. #define DBG_TAG "ipc.waitqueue"
  15. #define DBG_LVL DBG_INFO
  16. #include <rtdbg.h>
  17. #include <stdint.h>
  18. #include <rthw.h>
  19. #include <rtdevice.h>
  20. /**
  21. * @brief This function will insert a node to the wait queue.
  22. *
  23. * @param queue is a pointer to the wait queue.
  24. *
  25. * @param node is a pointer to the node to be inserted.
  26. */
  27. void rt_wqueue_add(rt_wqueue_t *queue, struct rt_wqueue_node *node)
  28. {
  29. rt_base_t level;
  30. level = rt_spin_lock_irqsave(&(queue->spinlock));
  31. node->wqueue = queue;
  32. rt_list_insert_before(&(queue->waiting_list), &(node->list));
  33. rt_spin_unlock_irqrestore(&(queue->spinlock), level);
  34. }
  35. /**
  36. * @brief This function will remove a node from the wait queue.
  37. *
  38. * @param node is a pointer to the node to be removed.
  39. */
  40. void rt_wqueue_remove(struct rt_wqueue_node *node)
  41. {
  42. rt_base_t level;
  43. RT_ASSERT(node->wqueue != RT_NULL);
  44. level = rt_spin_lock_irqsave(&(node->wqueue->spinlock));
  45. rt_list_remove(&(node->list));
  46. rt_spin_unlock_irqrestore(&(node->wqueue->spinlock), level);
  47. }
  48. /**
  49. * @brief This function is the default wakeup function, but it doesn't do anything in actual.
  50. * It always return 0, user should define their own wakeup function.
  51. *
  52. * @param wait is a pointer to the wait queue.
  53. *
  54. * @param key is the wakeup condition.
  55. *
  56. * @return always return 0.
  57. */
  58. int __wqueue_default_wake(struct rt_wqueue_node *wait, void *key)
  59. {
  60. return 0;
  61. }
  62. /**
  63. * @brief This function will wake up a pending thread on the specified
  64. * waiting queue that meets the conditions.
  65. *
  66. * @param queue is a pointer to the wait queue.
  67. *
  68. * @param key is the wakeup conditions, but it is not effective now, because
  69. * default wakeup function always return 0.
  70. * If user wants to use it, user should define their own wakeup function.
  71. */
  72. void rt_wqueue_wakeup(rt_wqueue_t *queue, void *key)
  73. {
  74. rt_base_t level;
  75. int need_schedule = 0;
  76. rt_list_t *queue_list;
  77. struct rt_list_node *node;
  78. struct rt_wqueue_node *entry;
  79. queue_list = &(queue->waiting_list);
  80. level = rt_spin_lock_irqsave(&(queue->spinlock));
  81. /* set wakeup flag in the queue */
  82. queue->flag = RT_WQ_FLAG_WAKEUP;
  83. if (!(rt_list_isempty(queue_list)))
  84. {
  85. for (node = queue_list->next; node != queue_list; node = node->next)
  86. {
  87. entry = rt_list_entry(node, struct rt_wqueue_node, list);
  88. if (entry->wakeup(entry, key) == 0)
  89. {
  90. /**
  91. * even though another thread may interrupt the thread and
  92. * wakeup it meanwhile, we can asuume that condition is ready
  93. */
  94. entry->polling_thread->error = RT_EOK;
  95. if (!rt_thread_resume(entry->polling_thread))
  96. {
  97. need_schedule = 1;
  98. rt_list_remove(&(entry->list));
  99. break;
  100. }
  101. }
  102. }
  103. }
  104. rt_spin_unlock_irqrestore(&(queue->spinlock), level);
  105. if (need_schedule)
  106. rt_schedule();
  107. return;
  108. }
  109. /**
  110. * @brief This function will wake up all pending thread on the specified
  111. * waiting queue that meets the conditions.
  112. *
  113. * @param queue is a pointer to the wait queue.
  114. *
  115. * @param key is the wakeup conditions, but it is not effective now, because
  116. * default wakeup function always return 0.
  117. * If user wants to use it, user should define their own wakeup
  118. * function.
  119. */
  120. void rt_wqueue_wakeup_all(rt_wqueue_t *queue, void *key)
  121. {
  122. rt_base_t level;
  123. int need_schedule = 0;
  124. rt_list_t *queue_list;
  125. struct rt_list_node *node;
  126. struct rt_wqueue_node *entry;
  127. queue_list = &(queue->waiting_list);
  128. level = rt_spin_lock_irqsave(&(queue->spinlock));
  129. /* set wakeup flag in the queue */
  130. queue->flag = RT_WQ_FLAG_WAKEUP;
  131. if (!(rt_list_isempty(queue_list)))
  132. {
  133. for (node = queue_list->next; node != queue_list; )
  134. {
  135. entry = rt_list_entry(node, struct rt_wqueue_node, list);
  136. if (entry->wakeup(entry, key) == 0)
  137. {
  138. /**
  139. * even though another thread may interrupt the thread and
  140. * wakeup it meanwhile, we can asuume that condition is ready
  141. */
  142. entry->polling_thread->error = RT_EOK;
  143. if (!rt_thread_resume(entry->polling_thread))
  144. {
  145. need_schedule = 1;
  146. }
  147. else
  148. {
  149. /* wakeup happened too soon that waker hadn't slept */
  150. LOG_D("%s: Thread resume failed", __func__);
  151. }
  152. node = node->next;
  153. }
  154. else
  155. {
  156. node = node->next;
  157. }
  158. }
  159. }
  160. rt_spin_unlock_irqrestore(&(queue->spinlock), level);
  161. if (need_schedule)
  162. rt_schedule();
  163. return;
  164. }
  165. /**
  166. * @brief This function will join a thread to the specified waiting queue, the thread will holds a wait or
  167. * timeout return on the specified wait queue.
  168. *
  169. * @param queue is a pointer to the wait queue.
  170. *
  171. * @param condition is parameters compatible with POSIX standard interface (currently meaningless, just pass in 0).
  172. *
  173. * @param msec is the timeout value, unit is millisecond.
  174. *
  175. * @return Return 0 if the thread is woken up.
  176. */
  177. static int _rt_wqueue_wait(rt_wqueue_t *queue, int condition, int msec, int suspend_flag)
  178. {
  179. int tick;
  180. rt_thread_t tid = rt_thread_self();
  181. rt_timer_t tmr = &(tid->thread_timer);
  182. struct rt_wqueue_node __wait;
  183. rt_base_t level;
  184. rt_err_t ret;
  185. /* current context checking */
  186. RT_DEBUG_SCHEDULER_AVAILABLE(RT_TRUE);
  187. tick = rt_tick_from_millisecond(msec);
  188. if ((condition) || (tick == 0))
  189. return 0;
  190. __wait.polling_thread = rt_thread_self();
  191. __wait.key = 0;
  192. __wait.wakeup = __wqueue_default_wake;
  193. __wait.wqueue = queue;
  194. rt_list_init(&__wait.list);
  195. level = rt_spin_lock_irqsave(&(queue->spinlock));
  196. /* reset thread error */
  197. tid->error = RT_EOK;
  198. if (queue->flag == RT_WQ_FLAG_WAKEUP)
  199. {
  200. /* already wakeup */
  201. goto __exit_wakeup;
  202. }
  203. ret = rt_thread_suspend_with_flag(tid, suspend_flag);
  204. if (ret != RT_EOK)
  205. {
  206. rt_spin_unlock_irqrestore(&(queue->spinlock), level);
  207. /* suspend failed */
  208. return -RT_EINTR;
  209. }
  210. rt_list_insert_before(&(queue->waiting_list), &(__wait.list));
  211. /* start timer */
  212. if (tick != RT_WAITING_FOREVER)
  213. {
  214. rt_timer_control(tmr,
  215. RT_TIMER_CTRL_SET_TIME,
  216. &tick);
  217. rt_timer_start(tmr);
  218. }
  219. rt_spin_unlock_irqrestore(&(queue->spinlock), level);
  220. rt_schedule();
  221. level = rt_spin_lock_irqsave(&(queue->spinlock));
  222. __exit_wakeup:
  223. queue->flag = RT_WQ_FLAG_CLEAN;
  224. rt_spin_unlock_irqrestore(&(queue->spinlock), level);
  225. rt_wqueue_remove(&__wait);
  226. return tid->error > 0 ? -tid->error : tid->error;
  227. }
  228. int rt_wqueue_wait(rt_wqueue_t *queue, int condition, int msec)
  229. {
  230. return _rt_wqueue_wait(queue, condition, msec, RT_UNINTERRUPTIBLE);
  231. }
  232. int rt_wqueue_wait_killable(rt_wqueue_t *queue, int condition, int msec)
  233. {
  234. return _rt_wqueue_wait(queue, condition, msec, RT_KILLABLE);
  235. }
  236. int rt_wqueue_wait_interruptible(rt_wqueue_t *queue, int condition, int msec)
  237. {
  238. return _rt_wqueue_wait(queue, condition, msec, RT_INTERRUPTIBLE);
  239. }