waitqueue.c 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. /*
  2. * File : waitqueue.c
  3. * This file is part of RT-Thread RTOS
  4. * COPYRIGHT (C) 2006 - 2018, 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. * 2018/06/26 Bernard Fix the wait queue issue when wakeup a soon
  23. * to blocked thread.
  24. */
  25. #include <stdint.h>
  26. #include <rthw.h>
  27. #include <rtdevice.h>
  28. #include <rtservice.h>
  29. void rt_wqueue_add(rt_wqueue_t *queue, struct rt_wqueue_node *node)
  30. {
  31. rt_base_t level;
  32. level = rt_hw_interrupt_disable();
  33. rt_list_insert_before(&(queue->waiting_list), &(node->list));
  34. rt_hw_interrupt_enable(level);
  35. }
  36. void rt_wqueue_remove(struct rt_wqueue_node *node)
  37. {
  38. rt_base_t level;
  39. level = rt_hw_interrupt_disable();
  40. rt_list_remove(&(node->list));
  41. rt_hw_interrupt_enable(level);
  42. }
  43. int __wqueue_default_wake(struct rt_wqueue_node *wait, void *key)
  44. {
  45. return 0;
  46. }
  47. void rt_wqueue_wakeup(rt_wqueue_t *queue, void *key)
  48. {
  49. rt_base_t level;
  50. register int need_schedule = 0;
  51. rt_list_t *queue_list;
  52. struct rt_list_node *node;
  53. struct rt_wqueue_node *entry;
  54. queue_list = &(queue->waiting_list);
  55. level = rt_hw_interrupt_disable();
  56. /* set wakeup flag in the queue */
  57. queue->flag = RT_WQ_FLAG_WAKEUP;
  58. if (!(rt_list_isempty(queue_list)))
  59. {
  60. for (node = queue_list->next; node != queue_list; node = node->next)
  61. {
  62. entry = rt_list_entry(node, struct rt_wqueue_node, list);
  63. if (entry->wakeup(entry, key) == 0)
  64. {
  65. rt_thread_resume(entry->polling_thread);
  66. need_schedule = 1;
  67. rt_wqueue_remove(entry);
  68. break;
  69. }
  70. }
  71. }
  72. rt_hw_interrupt_enable(level);
  73. if (need_schedule)
  74. rt_schedule();
  75. }
  76. int rt_wqueue_wait(rt_wqueue_t *queue, int condition, int msec)
  77. {
  78. int tick;
  79. rt_thread_t tid = rt_thread_self();
  80. rt_timer_t tmr = &(tid->thread_timer);
  81. struct rt_wqueue_node __wait;
  82. rt_base_t level;
  83. /* current context checking */
  84. RT_DEBUG_NOT_IN_INTERRUPT;
  85. tick = rt_tick_from_millisecond(msec);
  86. if ((condition) || (tick == 0))
  87. return 0;
  88. __wait.polling_thread = rt_thread_self();
  89. __wait.key = 0;
  90. __wait.wakeup = __wqueue_default_wake;
  91. rt_list_init(&__wait.list);
  92. level = rt_hw_interrupt_disable();
  93. if (queue->flag == RT_WQ_FLAG_WAKEUP)
  94. {
  95. /* already wakeup */
  96. goto __exit_wakeup;
  97. }
  98. rt_wqueue_add(queue, &__wait);
  99. rt_thread_suspend(tid);
  100. /* start timer */
  101. if (tick != RT_WAITING_FOREVER)
  102. {
  103. rt_timer_control(tmr,
  104. RT_TIMER_CTRL_SET_TIME,
  105. &tick);
  106. rt_timer_start(tmr);
  107. }
  108. rt_hw_interrupt_enable(level);
  109. rt_schedule();
  110. level = rt_hw_interrupt_disable();
  111. __exit_wakeup:
  112. queue->flag = 0;
  113. rt_hw_interrupt_enable(level);
  114. rt_wqueue_remove(&__wait);
  115. return 0;
  116. }