queue.hpp 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. // SPDX-License-Identifier: MIT
  2. // SPDX-FileCopyrightText: 2022 Ivan Baidakou
  3. #pragma once
  4. #include "message.hpp"
  5. #include <cassert>
  6. #include <cstddef>
  7. #include <limits>
  8. #include <tuple>
  9. #include <type_traits>
  10. #include <utility>
  11. namespace rotor_light {
  12. struct ItemQueueBase;
  13. /** \struct ItemGuard
  14. * \brief Message removal helper after message delivery
  15. */
  16. struct ItemGuard {
  17. ItemGuard() : message{nullptr} {}
  18. /** records message for removal */
  19. ItemGuard(Message *message_, ItemQueueBase *item_queue_)
  20. : message{message_}, item_queue{item_queue_} {}
  21. ItemGuard(const ItemGuard &) = delete;
  22. ItemGuard(ItemGuard &&) = delete;
  23. /** copy-assignment operator */
  24. ItemGuard &operator=(const ItemGuard &) = default;
  25. /** returns true if the guard holds a message */
  26. inline operator bool() { return message; }
  27. /** derefers to a message */
  28. inline Message &operator*() { return *message; }
  29. /** guarded message */
  30. Message *message;
  31. /** message origin/queue */
  32. ItemQueueBase *item_queue;
  33. };
  34. /** \struct ItemQueueBase
  35. * \brief base class for queues.
  36. *
  37. * Item = Message
  38. */
  39. struct ItemQueueBase {
  40. /** initializes queue with the message size and maximum amount of messsages */
  41. ItemQueueBase(size_t item_size, size_t items_count);
  42. ItemQueueBase(const ItemQueueBase &) = delete;
  43. ItemQueueBase(ItemQueueBase &&) = delete;
  44. /** returns the next stored message if there is one; otherwise nullptr
  45. * is returned */
  46. Message *next();
  47. /** constructs in-place new message by forwarding args into
  48. * message ctor.
  49. *
  50. * returns pointer to the newly created message or nullptr
  51. * if the queue is full
  52. */
  53. template <typename MessageType, typename... Args>
  54. MessageType *put(Args... args) {
  55. static_assert(std::is_base_of<Message, MessageType>::value,
  56. "type should be inherited from Message");
  57. assert((sizeof(MessageType) <= item_size) &&
  58. "no storage for the message, increase ItemSize");
  59. if (items_size == items_count) {
  60. return nullptr;
  61. }
  62. auto ptr = buff_ptr + item_size * free_index;
  63. if (free_index == items_size - 1) {
  64. free_index = 0;
  65. } else {
  66. ++free_index;
  67. }
  68. auto message = new (ptr) MessageType(std::forward<Args>(args)...);
  69. message->type = MessageType::type_id;
  70. ++items_count;
  71. return message;
  72. }
  73. /** deletes the last message from the queue */
  74. void release();
  75. protected:
  76. friend struct Message;
  77. /** records buffer pointer */
  78. void post_constructor(char *buff);
  79. private:
  80. char *buff_ptr;
  81. Index item_size;
  82. Index items_size;
  83. volatile Index items_count;
  84. volatile Index free_index;
  85. volatile Index occupied_index;
  86. };
  87. /** \struct ItemQueue
  88. * \brief convenient helper for storing fixed amount of messages
  89. */
  90. template <typename Storage, size_t ItemsCount>
  91. struct ItemQueue : ItemQueueBase {
  92. static_assert(Storage::item_size > 0, "queue size have to be positive");
  93. ItemQueue() : ItemQueueBase(Storage::item_size, ItemsCount) {
  94. post_constructor(reinterpret_cast<char *>(&storage));
  95. }
  96. private:
  97. using Item = typename Storage::Item;
  98. Item storage[ItemsCount];
  99. };
  100. /** \struct QueueBase
  101. * \brief base class for meta-queue (list of queues, ordered by priorities))
  102. */
  103. struct QueueBase {
  104. /** records storage of all sub-queues and their amount */
  105. QueueBase(ItemQueueBase **queues, size_t queue_count);
  106. QueueBase(const QueueBase &) = delete;
  107. QueueBase(QueueBase &&) = delete;
  108. /** constructs in-place new message by forwarding args into
  109. * message ctor.
  110. *
  111. * returns `true` if message is successfully created
  112. **/
  113. template <typename MessageType, typename... Args>
  114. bool put(size_t queue_index, Args... args) {
  115. assert((queue_index < queue_count) && "valid queue/priority");
  116. return queues[queue_index]->put<MessageType>(std::forward<Args>(args)...);
  117. }
  118. /** returns item-guard for the next message. Queues priorities are taken
  119. * into the account */
  120. ItemGuard next();
  121. private:
  122. ItemQueueBase **queues;
  123. uint8_t queue_count;
  124. };
  125. /** \struct Queue
  126. * \brief conveninent helper for meta-queue (list of queues, ordered by
  127. * priorities)
  128. */
  129. template <typename Storage, size_t... Counts> struct Queue : QueueBase {
  130. static_assert(Storage::item_size > sizeof(Message),
  131. "not enough storage for a message");
  132. /** tuple alias to store different queues */
  133. using Queues = std::tuple<ItemQueue<Storage, Counts>...>;
  134. /** the total amount of sub-queues */
  135. static constexpr size_t Size = std::tuple_size<Queues>::value;
  136. static_assert(Size > 0, "there should be at least one queue");
  137. Queue() : QueueBase(queues, Size) { fill_queue<0>(); }
  138. private:
  139. template <size_t Index> void fill_queue() {
  140. auto &queue = std::get<Index>(queues_storage);
  141. queues[Index] = &queue;
  142. if constexpr (Index + 1 < Size) {
  143. fill_queue<Index + 1>();
  144. }
  145. }
  146. Queues queues_storage;
  147. ItemQueueBase *queues[Size];
  148. };
  149. } // namespace rotor_light