Newer
Older
libkc / modules / src / kc_queue.c
  1. /**
  2. * @file kc_queue.c
  3. * @brief キューモジュール
  4. * @copyright 2003 - 2023 Nomura Kei
  5. */
  6. #include <stdlib.h>
  7. #include <stdint.h>
  8. #include <string.h>
  9. #include <errno.h>
  10. #include <stdatomic.h>
  11. #include <limits.h>
  12.  
  13. #include <kc_lock_guard.h>
  14. #include <kc_memory.h>
  15. #include <kc_threads.h>
  16. #include <kc_queue.h>
  17. #include <kc_list.h>
  18.  
  19. /**
  20. * KcQueue 管理情報
  21. */
  22. typedef struct
  23. {
  24. mtx_t mutex; //!< ロック用j
  25. cnd_t not_empty; //!< 条件変数(Empty)
  26. cnd_t not_full; //!< 条件変数(Full)
  27. int queue_size; //!< キューサイズ
  28. KcList *list; //!< キュー内部で利用するリスト
  29. } KcQueueInfo;
  30.  
  31. // =============================================================================
  32. // プロトタイプ宣言
  33. // =============================================================================
  34. static int KcQueue_size(struct KcQueue_ *queue);
  35. static bool KcQueue_is_empty(struct KcQueue_ *queue);
  36. static bool KcQueue_contains(struct KcQueue_ *queue, const void *element, size_t size);
  37. static bool KcQueue_offer(struct KcQueue_ *queue, const void *element, size_t size);
  38. static bool KcQueue_poll(struct KcQueue_ *queue, void *element, size_t *size);
  39. static void *KcQueue_peek(struct KcQueue_ *queue, size_t *size);
  40. static void KcQueue_put(struct KcQueue_ *queue, const void *element, size_t size);
  41. static void KcQueue_take(struct KcQueue_ *queue, void *element, size_t *size);
  42. static void KcQueue_clear(struct KcQueue_ *queue);
  43. static void KcQueue_cleanup_info(struct KcQueue_ *queue);
  44.  
  45. // =============================================================================
  46. // new
  47. // =============================================================================
  48. /**
  49. * Queue を構築します。
  50. *
  51. * @param size キューのサイズ
  52. * @return Queue
  53. */
  54. KcQueue *KcQueue_new(int size)
  55. {
  56. // KcQueue の管理構造
  57. // +--------------+
  58. // | KcQueue |
  59. // | ... |
  60. // | _info -----------+
  61. // +--------------+ |
  62. // | <_info> | <---+
  63. // | list |
  64. // +--------------+
  65. KcQueue *queue = (KcQueue *)malloc(sizeof(KcQueue) + sizeof(KcQueueInfo));
  66. KcList *list = KcList_new_LinkedList();
  67. if ((queue != NULL) && (list != NULL))
  68. {
  69. queue->size = KcQueue_size;
  70. queue->is_empty = KcQueue_is_empty;
  71. queue->contains = KcQueue_contains;
  72. queue->offer = KcQueue_offer;
  73. queue->poll = KcQueue_poll;
  74. queue->peek = KcQueue_peek;
  75. queue->put = KcQueue_put;
  76. queue->take = KcQueue_take;
  77. queue->clear = KcQueue_clear;
  78. queue->cleanup_info = KcQueue_cleanup_info;
  79. queue->_info = (queue + 1);
  80.  
  81. KcQueueInfo *info = (KcQueueInfo *)queue->_info;
  82. info->list = list;
  83. info->queue_size = (size == 0) ? INT_MAX : size;
  84. mtx_init(&(info->mutex), mtx_plain);
  85. cnd_init(&(info->not_empty));
  86. cnd_init(&(info->not_full));
  87. }
  88. else
  89. { // 何れかのメモリ確保に失敗したら、メモリを解放する。
  90. free(queue);
  91. queue = NULL;
  92. free(list);
  93. list = NULL;
  94. }
  95. return queue;
  96. }
  97.  
  98. // =============================================================================
  99. // delete
  100. // =============================================================================
  101. /**
  102. * Queue をします。
  103. *
  104. * @param queue 破棄するキュー
  105. */
  106. void KcQueue_delete(KcQueue *queue)
  107. {
  108. queue->cleanup_info(queue);
  109. free(queue);
  110. }
  111.  
  112. // =============================================================================
  113. // size
  114. // =============================================================================
  115. /**
  116. * キューに格納されている要素の数を返します。
  117. *
  118. * @param queue 対象キュー
  119. * @return 対象キュー内の要素数
  120. */
  121. static int KcQueue_size(struct KcQueue_ *queue)
  122. {
  123. KcQueueInfo *info = (KcQueueInfo *)queue->_info;
  124. size_t size = 0;
  125. mtx_lock(&(info->mutex)); // ===== Lock Start ===============
  126. size = info->list->size(info->list);
  127. mtx_unlock(&(info->mutex)); // ===== Lock End ===============
  128. return (int)size;
  129. }
  130.  
  131. // =============================================================================
  132. // is_empty
  133. // =============================================================================
  134. /**
  135. * キューに要素がない場合に true を返します。
  136. *
  137. * @param queue 対象キュー
  138. * @return 対象キューに要素が含まれていない場合は true
  139. */
  140. static bool KcQueue_is_empty(struct KcQueue_ *queue)
  141. {
  142. KcQueueInfo *info = (KcQueueInfo *)queue->_info;
  143. bool is_empty = true;
  144. mtx_lock(&(info->mutex)); // ===== Lock Start ===============
  145. is_empty = info->list->is_empty(info->list);
  146. mtx_unlock(&(info->mutex)); // ===== Lock End ===============
  147. return is_empty;
  148. }
  149.  
  150. // =============================================================================
  151. // contains
  152. // =============================================================================
  153. /**
  154. * 指定の要素が対象キューに含まれている場合に true を返します。
  155. *
  156. * @param queue 対象キュー
  157. * @param element 対象キュー内にあるかどうか判定される要素
  158. * @param size 要素のサイズ
  159. * @return 指定された要素が対象キュー内にある場合は true
  160. */
  161. static bool KcQueue_contains(struct KcQueue_ *queue, const void *element, size_t size)
  162. {
  163. KcQueueInfo *info = (KcQueueInfo *)queue->_info;
  164. bool is_contains = false;
  165. mtx_lock(&(info->mutex)); // ===== Lock Start ===============
  166. is_contains = info->list->contains(info->list, element, size);
  167. mtx_unlock(&(info->mutex)); // ===== Lock End ===============
  168. return is_contains;
  169. }
  170.  
  171. // =============================================================================
  172. // offer
  173. // =============================================================================
  174. /**
  175. * キューに要素を追加します。
  176. *
  177. * @param queue 対象キュー
  178. * @param element 追加する要素
  179. * @param size 要素のサイズ
  180. * @return true/false (追加成功/失敗)
  181. */
  182. static bool KcQueue_offer(struct KcQueue_ *queue, const void *element, size_t size)
  183. {
  184. KcQueueInfo *info = (KcQueueInfo *)queue->_info;
  185. bool is_success = false;
  186. mtx_lock(&(info->mutex)); // ===== Lock Start ===============
  187. int now_size = (int)info->list->size(info->list);
  188. if (now_size < info->queue_size)
  189. {
  190. is_success = info->list->add(info->list, -1, element, size);
  191. if (is_success)
  192. {
  193. cnd_signal(&(info->not_empty));
  194. }
  195. }
  196. mtx_unlock(&(info->mutex)); // ===== Lock End ===============
  197. return is_success;
  198. }
  199.  
  200. // =============================================================================
  201. // poll
  202. // =============================================================================
  203. /**
  204. * キューより要素を取り出します。
  205. *
  206. * @param queue 対象キュー
  207. * @param element 取り出された要素が格納されます。
  208. * @param size element のバッファサイズを指定します。また、取り出された要素のサイズが格納されます。
  209. * @return true/false (要素の取り出し成功/失敗[要素がない])
  210. */
  211. static bool KcQueue_poll(struct KcQueue_ *queue, void *element, size_t *size)
  212. {
  213. KcQueueInfo *info = (KcQueueInfo *)queue->_info;
  214. bool is_success = false;
  215. mtx_lock(&(info->mutex)); // ===== Lock Start ===============
  216. bool is_empty = info->list->is_empty(info->list);
  217. if (!is_empty)
  218. {
  219. is_success = info->list->remove(info->list, 0, element, size);
  220. // is_success は常に true
  221. cnd_signal(&(info->not_full));
  222. }
  223. mtx_unlock(&(info->mutex)); // ===== Lock End ===============
  224. return is_success;
  225. }
  226.  
  227. // =============================================================================
  228. // peek
  229. // =============================================================================
  230. /**
  231. * キューより要素を取得しますが、削除しません。
  232. *
  233. * @param queue 対象キュー
  234. * @param size 取り出された要素のサイズが格納されます。
  235. * @return 要素
  236. */
  237. static void *KcQueue_peek(struct KcQueue_ *queue, size_t *size)
  238. {
  239. KcQueueInfo *info = (KcQueueInfo *)queue->_info;
  240. void *value = NULL;
  241. mtx_lock(&(info->mutex)); // ===== Lock Start ===============
  242. value = info->list->get(info->list, 0, size);
  243. mtx_unlock(&(info->mutex)); // ===== Lock End ===============
  244. return value;
  245. }
  246.  
  247. // =============================================================================
  248. // put
  249. // =============================================================================
  250. /**
  251. * キューに要素を追加します。
  252. * キューが一杯の状態で追加できない場合、追加できるまでブロックされます。
  253. *
  254. * @param queue 対象キュー
  255. * @param element 追加する要素
  256. * @param size 要素のサイズ
  257. */
  258. static void KcQueue_put(struct KcQueue_ *queue, const void *element, size_t size)
  259. {
  260. KcQueueInfo *info = (KcQueueInfo *)queue->_info;
  261. mtx_lock(&(info->mutex)); // ===== Lock Start ===============
  262. while ((int)info->list->size(info->list) == info->queue_size)
  263. {
  264. cnd_wait(&(info->not_full), &(info->mutex));
  265. }
  266. info->list->add(info->list, -1, element, size);
  267. cnd_signal(&(info->not_empty));
  268. mtx_unlock(&(info->mutex)); // ===== Lock End ===============
  269. }
  270.  
  271. // =============================================================================
  272. // take
  273. // =============================================================================
  274. /**
  275. * キューより要素を取り出します。
  276. * 必要に応じて、要素が利用可能になるまでブロックされます。
  277. *
  278. * @param queue 対象キュー
  279. * @param element 取り出された要素が格納されます。
  280. * @param size element のバッファサイズを指定します。また、取り出された要素のサイズが格納されます。
  281. */
  282. void KcQueue_take(struct KcQueue_ *queue, void *element, size_t *size)
  283. {
  284. KcQueueInfo *info = (KcQueueInfo *)queue->_info;
  285. mtx_lock(&(info->mutex)); // ===== Lock Start ===============
  286. while (info->list->is_empty(info->list))
  287. {
  288. cnd_wait(&(info->not_empty), &(info->mutex));
  289. }
  290. info->list->remove(info->list, 0, element, size);
  291. cnd_signal(&(info->not_full));
  292. mtx_unlock(&(info->mutex)); // ===== Lock End ===============
  293. }
  294.  
  295. // =============================================================================
  296. // clear
  297. // =============================================================================
  298. /**
  299. * すべての要素をキューより削除します。
  300. *
  301. * @param queue 対象キュー
  302. */
  303. void KcQueue_clear(struct KcQueue_ *queue)
  304. {
  305. KcQueueInfo *info = (KcQueueInfo *)queue->_info;
  306. mtx_lock(&(info->mutex)); // ===== Lock Start ===============
  307. info->list->clear(info->list);
  308. mtx_unlock(&(info->mutex)); // ===== Lock End ===============
  309. }
  310.  
  311. // =============================================================================
  312. // clearnup_info
  313. // =============================================================================
  314. /**
  315. * クリア
  316. *
  317. * @param queue 対象キュー
  318. */
  319. static void KcQueue_cleanup_info(struct KcQueue_ *queue)
  320. {
  321. KcQueueInfo *info = (KcQueueInfo *)queue->_info;
  322. KcList_delete(info->list);
  323. mtx_destroy(&(info->mutex));
  324. cnd_destroy(&(info->not_empty));
  325. cnd_destroy(&(info->not_full));
  326. }