watermark_queue.h 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. /*
  2. * COPYRIGHT (C) 2011-2021, Real-Thread Information Technology Ltd
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. *
  6. * Change Logs:
  7. * Date Author Notes
  8. * 2014-04-16 Grissiom first version
  9. */
  10. struct rt_watermark_queue
  11. {
  12. /* Current water level. */
  13. unsigned int level;
  14. unsigned int high_mark;
  15. unsigned int low_mark;
  16. rt_list_t suspended_threads;
  17. };
  18. /** Init the struct rt_watermark_queue.
  19. */
  20. void rt_wm_que_init(struct rt_watermark_queue *wg,
  21. unsigned int low, unsigned int high);
  22. void rt_wm_que_set_mark(struct rt_watermark_queue *wg,
  23. unsigned int low, unsigned int high);
  24. void rt_wm_que_dump(struct rt_watermark_queue *wg);
  25. /* Water marks are often used in performance critical places. Benchmark shows
  26. * inlining functions will have 10% performance gain in some situation(for
  27. * example, VBus). So keep the inc/dec compact and inline. */
  28. /** Increase the water level.
  29. *
  30. * It should be called in the thread that want to raise the water level. If the
  31. * current level is above the high mark, the thread will be suspended up to
  32. * @timeout ticks.
  33. *
  34. * @return RT_EOK if water level increased successfully. -RT_EFULL on @timeout
  35. * is zero and the level is above water mark. -RT_ETIMEOUT if timeout occurred.
  36. */
  37. rt_inline rt_err_t rt_wm_que_inc(struct rt_watermark_queue *wg,
  38. int timeout)
  39. {
  40. rt_base_t level;
  41. /* Assert as early as possible. */
  42. if (timeout != 0)
  43. {
  44. RT_DEBUG_IN_THREAD_CONTEXT;
  45. }
  46. level = rt_hw_interrupt_disable();
  47. while (wg->level > wg->high_mark)
  48. {
  49. rt_thread_t thread;
  50. if (timeout == 0)
  51. {
  52. rt_hw_interrupt_enable(level);
  53. return -RT_EFULL;
  54. }
  55. thread = rt_thread_self();
  56. thread->error = RT_EOK;
  57. rt_thread_suspend(thread);
  58. rt_list_insert_after(&wg->suspended_threads, &RT_THREAD_LIST_NODE(thread));
  59. if (timeout > 0)
  60. {
  61. rt_tick_t timeout_tick = timeout;
  62. rt_timer_control(&(thread->thread_timer),
  63. RT_TIMER_CTRL_SET_TIME,
  64. &timeout_tick);
  65. rt_timer_start(&(thread->thread_timer));
  66. }
  67. rt_hw_interrupt_enable(level);
  68. rt_schedule();
  69. if (thread->error != RT_EOK)
  70. return thread->error;
  71. level = rt_hw_interrupt_disable();
  72. }
  73. wg->level++;
  74. if (wg->level == 0)
  75. {
  76. wg->level = ~0;
  77. }
  78. rt_hw_interrupt_enable(level);
  79. return RT_EOK;
  80. }
  81. /** Decrease the water level.
  82. *
  83. * It should be called by the consumer that drain the water out. If the water
  84. * level reached low mark, all the thread suspended in this queue will be waken
  85. * up. It's safe to call this function in interrupt context.
  86. */
  87. rt_inline void rt_wm_que_dec(struct rt_watermark_queue *wg)
  88. {
  89. int need_sched = 0;
  90. rt_base_t level;
  91. if (wg->level == 0)
  92. return;
  93. level = rt_hw_interrupt_disable();
  94. wg->level--;
  95. if (wg->level == wg->low_mark)
  96. {
  97. /* There should be spaces between the low mark and high mark, so it's
  98. * safe to resume all the threads. */
  99. while (!rt_list_isempty(&wg->suspended_threads))
  100. {
  101. rt_thread_t thread;
  102. thread = RT_THREAD_LIST_NODE_ENTRY(wg->suspended_threads.next);
  103. rt_thread_resume(thread);
  104. need_sched = 1;
  105. }
  106. }
  107. rt_hw_interrupt_enable(level);
  108. if (need_sched)
  109. rt_schedule();
  110. }