dataqueue.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500
  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. */
  12. #include <rthw.h>
  13. #include <rtdevice.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_spin_lock_init(&(queue->spinlock));
  54. rt_list_init(&(queue->suspended_push_list));
  55. rt_list_init(&(queue->suspended_pop_list));
  56. queue->queue = (struct rt_data_item *)rt_malloc(sizeof(struct rt_data_item) * size);
  57. if (queue->queue == RT_NULL)
  58. {
  59. return -RT_ENOMEM;
  60. }
  61. return RT_EOK;
  62. }
  63. RTM_EXPORT(rt_data_queue_init);
  64. /**
  65. * @brief This function will write data to the data queue. If the data queue is full,
  66. * the thread will suspend for the specified amount of time.
  67. *
  68. * @param queue is a pointer to the data queue object.
  69. * .
  70. * @param data_ptr is the buffer pointer of the data to be written.
  71. *
  72. * @param size is the size in bytes of the data to be written.
  73. *
  74. * @param timeout is the waiting time.
  75. *
  76. * @return Return the operation status. When the return value is RT_EOK, the operation is successful.
  77. * When the return value is -RT_ETIMEOUT, it means the specified time out.
  78. */
  79. rt_err_t rt_data_queue_push(struct rt_data_queue *queue,
  80. const void *data_ptr,
  81. rt_size_t data_size,
  82. rt_int32_t timeout)
  83. {
  84. rt_base_t level;
  85. rt_thread_t thread;
  86. rt_err_t result;
  87. RT_ASSERT(queue != RT_NULL);
  88. RT_ASSERT(queue->magic == DATAQUEUE_MAGIC);
  89. /* current context checking */
  90. RT_DEBUG_SCHEDULER_AVAILABLE(timeout != 0);
  91. result = RT_EOK;
  92. thread = rt_thread_self();
  93. level = rt_spin_lock_irqsave(&(queue->spinlock));
  94. while (queue->is_full)
  95. {
  96. /* queue is full */
  97. if (timeout == 0)
  98. {
  99. result = -RT_ETIMEOUT;
  100. goto __exit;
  101. }
  102. /* reset thread error number */
  103. thread->error = RT_EOK;
  104. /* suspend thread on the push list */
  105. rt_thread_suspend_with_flag(thread, RT_UNINTERRUPTIBLE);
  106. rt_list_insert_before(&(queue->suspended_push_list), &(thread->tlist));
  107. /* start timer */
  108. if (timeout > 0)
  109. {
  110. /* reset the timeout of thread timer and start it */
  111. rt_timer_control(&(thread->thread_timer),
  112. RT_TIMER_CTRL_SET_TIME,
  113. &timeout);
  114. rt_timer_start(&(thread->thread_timer));
  115. }
  116. /* enable interrupt */
  117. rt_spin_unlock_irqrestore(&(queue->spinlock), level);
  118. /* do schedule */
  119. rt_schedule();
  120. /* thread is waked up */
  121. result = thread->error;
  122. level = rt_spin_lock_irqsave(&(queue->spinlock));
  123. if (result != RT_EOK) goto __exit;
  124. }
  125. queue->queue[queue->put_index].data_ptr = data_ptr;
  126. queue->queue[queue->put_index].data_size = data_size;
  127. queue->put_index += 1;
  128. if (queue->put_index == queue->size)
  129. {
  130. queue->put_index = 0;
  131. }
  132. queue->is_empty = 0;
  133. if (queue->put_index == queue->get_index)
  134. {
  135. queue->is_full = 1;
  136. }
  137. /* there is at least one thread in suspended list */
  138. if (!rt_list_isempty(&(queue->suspended_pop_list)))
  139. {
  140. /* get thread entry */
  141. thread = rt_list_entry(queue->suspended_pop_list.next,
  142. struct rt_thread,
  143. tlist);
  144. /* resume it */
  145. rt_thread_resume(thread);
  146. rt_spin_unlock_irqrestore(&(queue->spinlock), level);
  147. /* perform a schedule */
  148. rt_schedule();
  149. return result;
  150. }
  151. __exit:
  152. rt_spin_unlock_irqrestore(&(queue->spinlock), level);
  153. if ((result == RT_EOK) && queue->evt_notify != RT_NULL)
  154. {
  155. queue->evt_notify(queue, RT_DATAQUEUE_EVENT_PUSH);
  156. }
  157. return result;
  158. }
  159. RTM_EXPORT(rt_data_queue_push);
  160. /**
  161. * @brief This function will pop data from the data queue. If the data queue is empty,the thread
  162. * will suspend for the specified amount of time.
  163. *
  164. * @note When the number of data in the data queue is less than lwm(low water mark), will
  165. * wake up the thread waiting for write data.
  166. *
  167. * @param queue is a pointer to the data queue object.
  168. *
  169. * @param data_ptr is the buffer pointer of the data to be fetched.
  170. *
  171. * @param size is the size in bytes of the data to be fetched.
  172. *
  173. * @param timeout is the waiting time.
  174. *
  175. * @return Return the operation status. When the return value is RT_EOK, the operation is successful.
  176. * When the return value is -RT_ETIMEOUT, it means the specified time out.
  177. */
  178. rt_err_t rt_data_queue_pop(struct rt_data_queue *queue,
  179. const void **data_ptr,
  180. rt_size_t *size,
  181. rt_int32_t timeout)
  182. {
  183. rt_base_t level;
  184. rt_thread_t thread;
  185. rt_err_t result;
  186. RT_ASSERT(queue != RT_NULL);
  187. RT_ASSERT(queue->magic == DATAQUEUE_MAGIC);
  188. RT_ASSERT(data_ptr != RT_NULL);
  189. RT_ASSERT(size != RT_NULL);
  190. /* current context checking */
  191. RT_DEBUG_SCHEDULER_AVAILABLE(timeout != 0);
  192. result = RT_EOK;
  193. thread = rt_thread_self();
  194. level = rt_spin_lock_irqsave(&(queue->spinlock));
  195. while (queue->is_empty)
  196. {
  197. /* queue is empty */
  198. if (timeout == 0)
  199. {
  200. result = -RT_ETIMEOUT;
  201. goto __exit;
  202. }
  203. /* reset thread error number */
  204. thread->error = RT_EOK;
  205. /* suspend thread on the pop list */
  206. rt_thread_suspend_with_flag(thread, RT_UNINTERRUPTIBLE);
  207. rt_list_insert_before(&(queue->suspended_pop_list), &(thread->tlist));
  208. /* start timer */
  209. if (timeout > 0)
  210. {
  211. /* reset the timeout of thread timer and start it */
  212. rt_timer_control(&(thread->thread_timer),
  213. RT_TIMER_CTRL_SET_TIME,
  214. &timeout);
  215. rt_timer_start(&(thread->thread_timer));
  216. }
  217. /* enable interrupt */
  218. rt_spin_unlock_irqrestore(&(queue->spinlock), level);
  219. /* do schedule */
  220. rt_schedule();
  221. /* thread is waked up */
  222. result = thread->error;
  223. level = rt_spin_lock_irqsave(&(queue->spinlock));
  224. if (result != RT_EOK)
  225. goto __exit;
  226. }
  227. *data_ptr = queue->queue[queue->get_index].data_ptr;
  228. *size = queue->queue[queue->get_index].data_size;
  229. queue->get_index += 1;
  230. if (queue->get_index == queue->size)
  231. {
  232. queue->get_index = 0;
  233. }
  234. queue->is_full = 0;
  235. if (queue->put_index == queue->get_index)
  236. {
  237. queue->is_empty = 1;
  238. }
  239. if (rt_data_queue_len(queue) <= queue->lwm)
  240. {
  241. /* there is at least one thread in suspended list */
  242. if (!rt_list_isempty(&(queue->suspended_push_list)))
  243. {
  244. /* get thread entry */
  245. thread = rt_list_entry(queue->suspended_push_list.next,
  246. struct rt_thread,
  247. tlist);
  248. /* resume it */
  249. rt_thread_resume(thread);
  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. struct rt_thread *thread;
  313. RT_ASSERT(queue != RT_NULL);
  314. RT_ASSERT(queue->magic == DATAQUEUE_MAGIC);
  315. level = rt_spin_lock_irqsave(&(queue->spinlock));
  316. queue->get_index = 0;
  317. queue->put_index = 0;
  318. queue->is_empty = 1;
  319. queue->is_full = 0;
  320. rt_spin_unlock_irqrestore(&(queue->spinlock), level);
  321. rt_enter_critical();
  322. /* wakeup all suspend threads */
  323. /* resume on pop list */
  324. while (!rt_list_isempty(&(queue->suspended_pop_list)))
  325. {
  326. /* disable interrupt */
  327. level = rt_spin_lock_irqsave(&(queue->spinlock));
  328. /* get next suspend thread */
  329. thread = rt_list_entry(queue->suspended_pop_list.next,
  330. struct rt_thread,
  331. tlist);
  332. /* set error code to -RT_ERROR */
  333. thread->error = -RT_ERROR;
  334. /*
  335. * resume thread
  336. * In rt_thread_resume function, it will remove current thread from
  337. * suspend list
  338. */
  339. rt_thread_resume(thread);
  340. /* enable interrupt */
  341. rt_spin_unlock_irqrestore(&(queue->spinlock), level);
  342. }
  343. /* resume on push list */
  344. while (!rt_list_isempty(&(queue->suspended_push_list)))
  345. {
  346. /* disable interrupt */
  347. level = rt_spin_lock_irqsave(&(queue->spinlock));
  348. /* get next suspend thread */
  349. thread = rt_list_entry(queue->suspended_push_list.next,
  350. struct rt_thread,
  351. tlist);
  352. /* set error code to -RT_ERROR */
  353. thread->error = -RT_ERROR;
  354. /*
  355. * resume thread
  356. * In rt_thread_resume function, it will remove current thread from
  357. * suspend list
  358. */
  359. rt_thread_resume(thread);
  360. /* enable interrupt */
  361. rt_spin_unlock_irqrestore(&(queue->spinlock), level);
  362. }
  363. rt_exit_critical();
  364. rt_schedule();
  365. }
  366. RTM_EXPORT(rt_data_queue_reset);
  367. /**
  368. * @brief This function will deinit the data queue.
  369. *
  370. * @param queue is a pointer to the data queue object.
  371. *
  372. * @return Return the operation status. When the return value is RT_EOK, the operation is successful.
  373. */
  374. rt_err_t rt_data_queue_deinit(struct rt_data_queue *queue)
  375. {
  376. rt_base_t level;
  377. RT_ASSERT(queue != RT_NULL);
  378. RT_ASSERT(queue->magic == DATAQUEUE_MAGIC);
  379. /* wakeup all suspend threads */
  380. rt_data_queue_reset(queue);
  381. level = rt_spin_lock_irqsave(&(queue->spinlock));
  382. queue->magic = 0;
  383. rt_spin_unlock_irqrestore(&(queue->spinlock), level);
  384. rt_free(queue->queue);
  385. return RT_EOK;
  386. }
  387. RTM_EXPORT(rt_data_queue_deinit);
  388. /**
  389. * @brief This function will get the number of data in the data queue.
  390. *
  391. * @param queue is a pointer to the data queue object.
  392. *
  393. * @return Return the number of data in the data queue.
  394. */
  395. rt_uint16_t rt_data_queue_len(struct rt_data_queue *queue)
  396. {
  397. rt_base_t level;
  398. rt_int16_t len;
  399. RT_ASSERT(queue != RT_NULL);
  400. RT_ASSERT(queue->magic == DATAQUEUE_MAGIC);
  401. if (queue->is_empty)
  402. {
  403. return 0;
  404. }
  405. level = rt_spin_lock_irqsave(&(queue->spinlock));
  406. if (queue->put_index > queue->get_index)
  407. {
  408. len = queue->put_index - queue->get_index;
  409. }
  410. else
  411. {
  412. len = queue->size + queue->put_index - queue->get_index;
  413. }
  414. rt_spin_unlock_irqrestore(&(queue->spinlock), level);
  415. return len;
  416. }
  417. RTM_EXPORT(rt_data_queue_len);