dataqueue.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459
  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. * 2016-10-31 armink fix some resume push and pop thread bugs
  10. * 2023-09-15 xqyjlj perf rt_hw_interrupt_disable/enable
  11. * 2024-01-25 Shell porting to susp_list API
  12. */
  13. #include <rthw.h>
  14. #include <rtdevice.h>
  15. #define DATAQUEUE_MAGIC 0xbead0e0e
  16. struct rt_data_item
  17. {
  18. const void *data_ptr;
  19. rt_size_t data_size;
  20. };
  21. /**
  22. * @brief This function will initialize the data queue. Calling this function will
  23. * initialize the data queue control block and set the notification callback function.
  24. *
  25. * @param queue is a pointer to the data queue object.
  26. *
  27. * @param size is the maximum number of data in the data queue.
  28. *
  29. * @param lwm is low water mark.
  30. * When the number of data in the data queue is less than this value, this function will
  31. * wake up the thread waiting for write data.
  32. *
  33. * @param evt_notify is the notification callback function.
  34. *
  35. * @return Return the operation status. When the return value is RT_EOK, the initialization is successful.
  36. * When the return value is -RT_ENOMEM, it means insufficient memory allocation failed.
  37. */
  38. rt_err_t
  39. rt_data_queue_init(struct rt_data_queue *queue,
  40. rt_uint16_t size,
  41. rt_uint16_t lwm,
  42. void (*evt_notify)(struct rt_data_queue *queue, rt_uint32_t event))
  43. {
  44. RT_ASSERT(queue != RT_NULL);
  45. RT_ASSERT(size > 0);
  46. queue->evt_notify = evt_notify;
  47. queue->magic = DATAQUEUE_MAGIC;
  48. queue->size = size;
  49. queue->lwm = lwm;
  50. queue->get_index = 0;
  51. queue->put_index = 0;
  52. queue->is_empty = 1;
  53. queue->is_full = 0;
  54. rt_spin_lock_init(&(queue->spinlock));
  55. rt_list_init(&(queue->suspended_push_list));
  56. rt_list_init(&(queue->suspended_pop_list));
  57. queue->queue = (struct rt_data_item *)rt_malloc(sizeof(struct rt_data_item) * size);
  58. if (queue->queue == RT_NULL)
  59. {
  60. return -RT_ENOMEM;
  61. }
  62. return RT_EOK;
  63. }
  64. RTM_EXPORT(rt_data_queue_init);
  65. /**
  66. * @brief This function will write data to the data queue. If the data queue is full,
  67. * the thread will suspend for the specified amount of time.
  68. *
  69. * @param queue is a pointer to the data queue object.
  70. * .
  71. * @param data_ptr is the buffer pointer of the data to be written.
  72. *
  73. * @param size is the size in bytes of the data to be written.
  74. *
  75. * @param timeout is the waiting time.
  76. *
  77. * @return Return the operation status. When the return value is RT_EOK, the operation is successful.
  78. * When the return value is -RT_ETIMEOUT, it means the specified time out.
  79. */
  80. rt_err_t rt_data_queue_push(struct rt_data_queue *queue,
  81. const void *data_ptr,
  82. rt_size_t data_size,
  83. rt_int32_t timeout)
  84. {
  85. rt_base_t level;
  86. rt_thread_t thread;
  87. rt_err_t result;
  88. RT_ASSERT(queue != RT_NULL);
  89. RT_ASSERT(queue->magic == DATAQUEUE_MAGIC);
  90. /* current context checking */
  91. RT_DEBUG_SCHEDULER_AVAILABLE(timeout != 0);
  92. result = RT_EOK;
  93. thread = rt_thread_self();
  94. level = rt_spin_lock_irqsave(&(queue->spinlock));
  95. while (queue->is_full)
  96. {
  97. /* queue is full */
  98. if (timeout == 0)
  99. {
  100. result = -RT_ETIMEOUT;
  101. goto __exit;
  102. }
  103. /* reset thread error number */
  104. thread->error = RT_EOK;
  105. /* suspend thread on the push list */
  106. result = rt_thread_suspend_to_list(thread, &queue->suspended_push_list,
  107. RT_IPC_FLAG_FIFO, RT_UNINTERRUPTIBLE);
  108. if (result == RT_EOK)
  109. {
  110. /* start timer */
  111. if (timeout > 0)
  112. {
  113. /* reset the timeout of thread timer and start it */
  114. rt_timer_control(&(thread->thread_timer),
  115. RT_TIMER_CTRL_SET_TIME,
  116. &timeout);
  117. rt_timer_start(&(thread->thread_timer));
  118. }
  119. /* enable interrupt */
  120. rt_spin_unlock_irqrestore(&(queue->spinlock), level);
  121. /* do schedule */
  122. rt_schedule();
  123. /* thread is waked up */
  124. level = rt_spin_lock_irqsave(&(queue->spinlock));
  125. /* error may be modified by waker, so take the lock before accessing it */
  126. result = thread->error;
  127. }
  128. if (result != RT_EOK) goto __exit;
  129. }
  130. queue->queue[queue->put_index].data_ptr = data_ptr;
  131. queue->queue[queue->put_index].data_size = data_size;
  132. queue->put_index += 1;
  133. if (queue->put_index == queue->size)
  134. {
  135. queue->put_index = 0;
  136. }
  137. queue->is_empty = 0;
  138. if (queue->put_index == queue->get_index)
  139. {
  140. queue->is_full = 1;
  141. }
  142. /* there is at least one thread in suspended list */
  143. if (rt_susp_list_dequeue(&queue->suspended_pop_list,
  144. RT_THREAD_RESUME_RES_THR_ERR))
  145. {
  146. /* unlock and perform a schedule */
  147. rt_spin_unlock_irqrestore(&(queue->spinlock), level);
  148. /* perform a schedule */
  149. rt_schedule();
  150. return result;
  151. }
  152. __exit:
  153. rt_spin_unlock_irqrestore(&(queue->spinlock), level);
  154. if ((result == RT_EOK) && queue->evt_notify != RT_NULL)
  155. {
  156. queue->evt_notify(queue, RT_DATAQUEUE_EVENT_PUSH);
  157. }
  158. return result;
  159. }
  160. RTM_EXPORT(rt_data_queue_push);
  161. /**
  162. * @brief This function will pop data from the data queue. If the data queue is empty,the thread
  163. * will suspend for the specified amount of time.
  164. *
  165. * @note When the number of data in the data queue is less than lwm(low water mark), will
  166. * wake up the thread waiting for write data.
  167. *
  168. * @param queue is a pointer to the data queue object.
  169. *
  170. * @param data_ptr is the buffer pointer of the data to be fetched.
  171. *
  172. * @param size is the size in bytes of the data to be fetched.
  173. *
  174. * @param timeout is the waiting time.
  175. *
  176. * @return Return the operation status. When the return value is RT_EOK, the operation is successful.
  177. * When the return value is -RT_ETIMEOUT, it means the specified time out.
  178. */
  179. rt_err_t rt_data_queue_pop(struct rt_data_queue *queue,
  180. const void **data_ptr,
  181. rt_size_t *size,
  182. rt_int32_t timeout)
  183. {
  184. rt_base_t level;
  185. rt_thread_t thread;
  186. rt_err_t result;
  187. RT_ASSERT(queue != RT_NULL);
  188. RT_ASSERT(queue->magic == DATAQUEUE_MAGIC);
  189. RT_ASSERT(data_ptr != RT_NULL);
  190. RT_ASSERT(size != RT_NULL);
  191. /* current context checking */
  192. RT_DEBUG_SCHEDULER_AVAILABLE(timeout != 0);
  193. result = RT_EOK;
  194. thread = rt_thread_self();
  195. level = rt_spin_lock_irqsave(&(queue->spinlock));
  196. while (queue->is_empty)
  197. {
  198. /* queue is empty */
  199. if (timeout == 0)
  200. {
  201. result = -RT_ETIMEOUT;
  202. goto __exit;
  203. }
  204. /* reset thread error number */
  205. thread->error = RT_EOK;
  206. /* suspend thread on the pop list */
  207. result = rt_thread_suspend_to_list(thread, &queue->suspended_pop_list,
  208. RT_IPC_FLAG_FIFO, RT_UNINTERRUPTIBLE);
  209. if (result == RT_EOK)
  210. {
  211. /* start timer */
  212. if (timeout > 0)
  213. {
  214. /* reset the timeout of thread timer and start it */
  215. rt_timer_control(&(thread->thread_timer),
  216. RT_TIMER_CTRL_SET_TIME,
  217. &timeout);
  218. rt_timer_start(&(thread->thread_timer));
  219. }
  220. /* enable interrupt */
  221. rt_spin_unlock_irqrestore(&(queue->spinlock), level);
  222. /* do schedule */
  223. rt_schedule();
  224. /* thread is waked up */
  225. level = rt_spin_lock_irqsave(&(queue->spinlock));
  226. result = thread->error;
  227. if (result != RT_EOK)
  228. goto __exit;
  229. }
  230. }
  231. *data_ptr = queue->queue[queue->get_index].data_ptr;
  232. *size = queue->queue[queue->get_index].data_size;
  233. queue->get_index += 1;
  234. if (queue->get_index == queue->size)
  235. {
  236. queue->get_index = 0;
  237. }
  238. queue->is_full = 0;
  239. if (queue->put_index == queue->get_index)
  240. {
  241. queue->is_empty = 1;
  242. }
  243. if (rt_data_queue_len(queue) <= queue->lwm)
  244. {
  245. /* there is at least one thread in suspended list */
  246. if (rt_susp_list_dequeue(&queue->suspended_push_list,
  247. RT_THREAD_RESUME_RES_THR_ERR))
  248. {
  249. /* unlock and perform a schedule */
  250. rt_spin_unlock_irqrestore(&(queue->spinlock), level);
  251. /* perform a schedule */
  252. rt_schedule();
  253. }
  254. else
  255. {
  256. rt_spin_unlock_irqrestore(&(queue->spinlock), level);
  257. }
  258. if (queue->evt_notify != RT_NULL)
  259. queue->evt_notify(queue, RT_DATAQUEUE_EVENT_LWM);
  260. return result;
  261. }
  262. __exit:
  263. rt_spin_unlock_irqrestore(&(queue->spinlock), level);
  264. if ((result == RT_EOK) && (queue->evt_notify != RT_NULL))
  265. {
  266. queue->evt_notify(queue, RT_DATAQUEUE_EVENT_POP);
  267. }
  268. return result;
  269. }
  270. RTM_EXPORT(rt_data_queue_pop);
  271. /**
  272. * @brief This function will fetch but retaining data in the data queue.
  273. *
  274. * @param queue is a pointer to the data queue object.
  275. *
  276. * @param data_ptr is the buffer pointer of the data to be fetched.
  277. *
  278. * @param size is the size in bytes of the data to be fetched.
  279. *
  280. * @return Return the operation status. When the return value is RT_EOK, the operation is successful.
  281. * When the return value is -RT_EEMPTY, it means the data queue is empty.
  282. */
  283. rt_err_t rt_data_queue_peek(struct rt_data_queue *queue,
  284. const void **data_ptr,
  285. rt_size_t *size)
  286. {
  287. rt_base_t level;
  288. RT_ASSERT(queue != RT_NULL);
  289. RT_ASSERT(queue->magic == DATAQUEUE_MAGIC);
  290. if (queue->is_empty)
  291. {
  292. return -RT_EEMPTY;
  293. }
  294. level = rt_spin_lock_irqsave(&(queue->spinlock));
  295. *data_ptr = queue->queue[queue->get_index].data_ptr;
  296. *size = queue->queue[queue->get_index].data_size;
  297. rt_spin_unlock_irqrestore(&(queue->spinlock), level);
  298. return RT_EOK;
  299. }
  300. RTM_EXPORT(rt_data_queue_peek);
  301. /**
  302. * @brief This function will reset the data queue.
  303. *
  304. * @note Calling this function will wake up all threads on the data queue
  305. * that are hanging and waiting.
  306. *
  307. * @param queue is a pointer to the data queue object.
  308. */
  309. void rt_data_queue_reset(struct rt_data_queue *queue)
  310. {
  311. rt_base_t level;
  312. RT_ASSERT(queue != RT_NULL);
  313. RT_ASSERT(queue->magic == DATAQUEUE_MAGIC);
  314. level = rt_spin_lock_irqsave(&(queue->spinlock));
  315. queue->get_index = 0;
  316. queue->put_index = 0;
  317. queue->is_empty = 1;
  318. queue->is_full = 0;
  319. rt_spin_unlock_irqrestore(&(queue->spinlock), level);
  320. rt_enter_critical();
  321. /* wakeup all suspend threads */
  322. /* resume on pop list */
  323. rt_susp_list_resume_all_irq(&queue->suspended_pop_list, RT_ERROR,
  324. &(queue->spinlock));
  325. /* resume on push list */
  326. rt_susp_list_resume_all_irq(&queue->suspended_push_list, RT_ERROR,
  327. &(queue->spinlock));
  328. rt_exit_critical();
  329. rt_schedule();
  330. }
  331. RTM_EXPORT(rt_data_queue_reset);
  332. /**
  333. * @brief This function will deinit the data queue.
  334. *
  335. * @param queue is a pointer to the data queue object.
  336. *
  337. * @return Return the operation status. When the return value is RT_EOK, the operation is successful.
  338. */
  339. rt_err_t rt_data_queue_deinit(struct rt_data_queue *queue)
  340. {
  341. rt_base_t level;
  342. RT_ASSERT(queue != RT_NULL);
  343. RT_ASSERT(queue->magic == DATAQUEUE_MAGIC);
  344. /* wakeup all suspend threads */
  345. rt_data_queue_reset(queue);
  346. level = rt_spin_lock_irqsave(&(queue->spinlock));
  347. queue->magic = 0;
  348. rt_spin_unlock_irqrestore(&(queue->spinlock), level);
  349. rt_free(queue->queue);
  350. return RT_EOK;
  351. }
  352. RTM_EXPORT(rt_data_queue_deinit);
  353. /**
  354. * @brief This function will get the number of data in the data queue.
  355. *
  356. * @param queue is a pointer to the data queue object.
  357. *
  358. * @return Return the number of data in the data queue.
  359. */
  360. rt_uint16_t rt_data_queue_len(struct rt_data_queue *queue)
  361. {
  362. rt_base_t level;
  363. rt_int16_t len;
  364. RT_ASSERT(queue != RT_NULL);
  365. RT_ASSERT(queue->magic == DATAQUEUE_MAGIC);
  366. if (queue->is_empty)
  367. {
  368. return 0;
  369. }
  370. level = rt_spin_lock_irqsave(&(queue->spinlock));
  371. if (queue->put_index > queue->get_index)
  372. {
  373. len = queue->put_index - queue->get_index;
  374. }
  375. else
  376. {
  377. len = queue->size + queue->put_index - queue->get_index;
  378. }
  379. rt_spin_unlock_irqrestore(&(queue->spinlock), level);
  380. return len;
  381. }
  382. RTM_EXPORT(rt_data_queue_len);