dataqueue.c 13 KB

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