completion.c 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  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. * 2012-09-30 Bernard first version.
  9. * 2021-08-18 chenyingchun add comments
  10. * 2023-09-15 xqyjlj perf rt_hw_interrupt_disable/enable
  11. */
  12. #include <rthw.h>
  13. #include <rtdevice.h>
  14. #define RT_COMPLETED 1
  15. #define RT_UNCOMPLETED 0
  16. /**
  17. * @brief This function will initialize a completion object.
  18. *
  19. * @param completion is a pointer to a completion object.
  20. */
  21. void rt_completion_init(struct rt_completion *completion)
  22. {
  23. rt_base_t level;
  24. RT_ASSERT(completion != RT_NULL);
  25. rt_spin_lock_init(&(completion->spinlock));
  26. level = rt_spin_lock_irqsave(&(completion->spinlock));
  27. completion->flag = RT_UNCOMPLETED;
  28. rt_list_init(&completion->suspended_list);
  29. rt_spin_unlock_irqrestore(&(completion->spinlock), level);
  30. }
  31. RTM_EXPORT(rt_completion_init);
  32. /**
  33. * @brief This function will wait for a completion, if the completion is unavailable, the thread shall wait for
  34. * the completion up to a specified time.
  35. *
  36. * @param completion is a pointer to a completion object.
  37. *
  38. * @param timeout is a timeout period (unit: OS ticks). If the completion is unavailable, the thread will wait for
  39. * the completion done up to the amount of time specified by the argument.
  40. * NOTE: Generally, we use the macro RT_WAITING_FOREVER to set this parameter, which means that when the
  41. * completion is unavailable, the thread will be waitting forever.
  42. *
  43. * @return Return the operation status. ONLY when the return value is RT_EOK, the operation is successful.
  44. * If the return value is any other values, it means that the completion wait failed.
  45. *
  46. * @warning This function can ONLY be called in the thread context. It MUST NOT be called in interrupt context.
  47. */
  48. rt_err_t rt_completion_wait(struct rt_completion *completion,
  49. rt_int32_t timeout)
  50. {
  51. rt_err_t result;
  52. rt_base_t level;
  53. rt_thread_t thread;
  54. RT_ASSERT(completion != RT_NULL);
  55. /* current context checking */
  56. RT_DEBUG_SCHEDULER_AVAILABLE(timeout != 0);
  57. result = RT_EOK;
  58. thread = rt_thread_self();
  59. level = rt_spin_lock_irqsave(&(completion->spinlock));
  60. if (completion->flag != RT_COMPLETED)
  61. {
  62. /* only one thread can suspend on complete */
  63. RT_ASSERT(rt_list_isempty(&(completion->suspended_list)));
  64. if (timeout == 0)
  65. {
  66. result = -RT_ETIMEOUT;
  67. goto __exit;
  68. }
  69. else
  70. {
  71. /* reset thread error number */
  72. thread->error = RT_EOK;
  73. /* suspend thread */
  74. rt_thread_suspend_with_flag(thread, RT_UNINTERRUPTIBLE);
  75. /* add to suspended list */
  76. rt_list_insert_before(&(completion->suspended_list),
  77. &(thread->tlist));
  78. /* current context checking */
  79. RT_DEBUG_NOT_IN_INTERRUPT;
  80. /* start timer */
  81. if (timeout > 0)
  82. {
  83. /* reset the timeout of thread timer and start it */
  84. rt_timer_control(&(thread->thread_timer),
  85. RT_TIMER_CTRL_SET_TIME,
  86. &timeout);
  87. rt_timer_start(&(thread->thread_timer));
  88. }
  89. /* enable interrupt */
  90. rt_spin_unlock_irqrestore(&(completion->spinlock), level);
  91. /* do schedule */
  92. rt_schedule();
  93. /* thread is waked up */
  94. result = thread->error;
  95. level = rt_spin_lock_irqsave(&(completion->spinlock));
  96. }
  97. }
  98. /* clean completed flag */
  99. completion->flag = RT_UNCOMPLETED;
  100. __exit:
  101. rt_spin_unlock_irqrestore(&(completion->spinlock), level);
  102. return result;
  103. }
  104. RTM_EXPORT(rt_completion_wait);
  105. /**
  106. * @brief This function indicates a completion has done.
  107. *
  108. * @param completion is a pointer to a completion object.
  109. */
  110. void rt_completion_done(struct rt_completion *completion)
  111. {
  112. rt_base_t level;
  113. RT_ASSERT(completion != RT_NULL);
  114. if (completion->flag == RT_COMPLETED)
  115. return;
  116. level = rt_spin_lock_irqsave(&(completion->spinlock));
  117. completion->flag = RT_COMPLETED;
  118. if (!rt_list_isempty(&(completion->suspended_list)))
  119. {
  120. /* there is one thread in suspended list */
  121. struct rt_thread *thread;
  122. /* get thread entry */
  123. thread = rt_list_entry(completion->suspended_list.next,
  124. struct rt_thread,
  125. tlist);
  126. /* resume it */
  127. rt_thread_resume(thread);
  128. rt_spin_unlock_irqrestore(&(completion->spinlock), level);
  129. /* perform a schedule */
  130. rt_schedule();
  131. }
  132. else
  133. {
  134. rt_spin_unlock_irqrestore(&(completion->spinlock), level);
  135. }
  136. }
  137. RTM_EXPORT(rt_completion_done);