dataqueue.c 13 KB

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