A C++ library for logging very fast and without allocating.
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

248 lignes
8.2 KiB

  1. #pragma once
  2. #pragma once
  3. #ifdef __cplusplus
  4. #include <cstdint>
  5. #include <cstddef>
  6. #include <iterator>
  7. #else
  8. #include <stdint.h>
  9. #include <stddef.h>
  10. #endif
  11. struct sl_log_transaction;
  12. struct sl_log_transaction_internals;
  13. #ifdef __cplusplus
  14. namespace sl {
  15. using cstyle_log_transaction = sl_log_transaction;
  16. using cstyle_log_transaction_internals = sl_log_transaction_internals;
  17. /**
  18. * @brief This class represents a logging transaction
  19. * @details
  20. * This class has the following guarantees:
  21. * - Can be built on the stack safely
  22. * - This class never allocates using the system allocator
  23. * - This class does not perform any system call
  24. * - Finalization cannot happen twice from the public interface
  25. * - This class may pad the log by up to <tt>std::hardware_destructive_interference</tt> bytes
  26. * - This class is both thread-safe and multiprocessing-safe to finalize
  27. *
  28. * @details
  29. * This class expects the finalization to occur in the same thread that wrote any data to the log.
  30. * @pre
  31. * This class expects the logging buffer to be at least 2 binary orders of magnitude bigger than the largest log
  32. * message. It is recommended that it is at least 6 binary orders of magnitude bigger than the largest log.
  33. * @post
  34. * This class leaves the log in a valid state in accordance to the defined log strategies.
  35. */
  36. class log_transaction final {
  37. char* local_start;
  38. size_t local_size;
  39. char* buffer_start;
  40. size_t buffer_size;
  41. void* buffer_initiator_page;
  42. bool is_finalized = false;
  43. void finalize_impl();
  44. /**
  45. * @brief A forward iterator to the current log transaction.
  46. */
  47. class iterator final {
  48. using iterator_category = std::forward_iterator_tag;
  49. using difference_type = std::ptrdiff_t;
  50. using value_type = char;
  51. using pointer = char*;
  52. using reference = char&;
  53. log_transaction& owner_ref;
  54. size_t index;
  55. public:
  56. explicit iterator(log_transaction& parent, size_t position = 0)
  57. : owner_ref(parent)
  58. , index(((parent.buffer_start - parent.local_start) + position)%parent.buffer_size)
  59. {}
  60. iterator& operator++() {
  61. index+=1;
  62. index%=owner_ref.buffer_size;
  63. return *this;
  64. }
  65. iterator operator++(int) {
  66. auto cpy = *this;
  67. index+=1;
  68. index%=owner_ref.buffer_size;
  69. return cpy;
  70. }
  71. difference_type operator-(iterator other) const {
  72. size_t forward, backward;
  73. forward = owner_ref.buffer_size - index;
  74. forward += other.index;
  75. backward = other.index - index;
  76. return static_cast<difference_type>(std::min(forward, backward));
  77. }
  78. reference operator*() {
  79. return owner_ref.buffer_start[index];
  80. }
  81. };
  82. public:
  83. /**
  84. * @brief Starts a transaction. You have to then write your logs between the start and the end of the
  85. * transaction, then finalize it.
  86. * @details
  87. * Guarantees are as follow:
  88. * - This constructor never allocates using the system allocator
  89. * - This constructor does not perform any system call
  90. * - This constructor may pad the log by up to <tt>std::hardware_destructive_interference</tt> bytes
  91. * - This constructor is both thread-safe and multiprocessing-safe
  92. *
  93. * @pre
  94. * We expect the provided <tt>internal_id</tt> to be valid and registered, if it is not, the transaction may look for a
  95. * log named "UNHANDLED_ERRORS" and log there instead silently.
  96. * @post
  97. * A started transaction MUST be finalized. The log can, upon initiation of the transaction, no longer be
  98. * pushed to its sink past the current sequence point of the transaction.
  99. *
  100. * @post
  101. * This class will finalize an un-finalized transaction
  102. * @param internal_id the logger id where the transaction is to happen
  103. * @param log_size the amount of bytes to write in the transaction.
  104. */
  105. explicit log_transaction(int internal_id, size_t log_size);
  106. log_transaction(const log_transaction&) = delete;
  107. log_transaction(log_transaction&&) = delete;
  108. void operator=(const log_transaction&) = delete;
  109. void operator=(log_transaction&&) = delete;
  110. /**
  111. * @brief Unsafely accesses the underlying buffer
  112. * @param index The index to the buffer to access
  113. * @return a reference to an underlying <tt>char</tt>, undefined behaviour is the index is invalid
  114. */
  115. char& operator[](size_t index) {
  116. return buffer_start[((buffer_start - local_start) + index)%buffer_size];
  117. }
  118. /**
  119. * @brief Obtains a forward iterator to the start of the writable range
  120. * @return a non-descript iterator to the beginning of the range
  121. */
  122. iterator begin() {
  123. return iterator(*this, 0);
  124. }
  125. /**
  126. * @brief Obtains a forward iterator to the end of the writable range
  127. * @return a non-descript iterator to one-past-the-end of the range
  128. */
  129. iterator end() {
  130. return iterator(*this, local_size);
  131. }
  132. /**
  133. * @brief Finalizes the transaction
  134. * @details
  135. * Finalizes a transaction, makes access to the finalized buffer undefined behaviour. If <tt>NDEBUG</tt> is
  136. * defined, accesses will lead to a near-nullptr dereference.
  137. *
  138. * @details
  139. * You are NOT protected against erroneous double finalization in debug mode. You are protected against double
  140. * finalization out of debug mode. This protection and these erroneous states do not interact with the
  141. * destructor, which will finalize transactions if not finalized yet.
  142. */
  143. void finalize();
  144. /**
  145. * @see{void finalize()}
  146. */
  147. ~log_transaction(){
  148. if(!is_finalized) finalize_impl();
  149. }
  150. };
  151. }
  152. #else
  153. typedef struct sl_log_transaction log_transaction;
  154. typedef struct sl_log_transaction_internals log_transaction_internals;
  155. #endif
  156. /**
  157. * @brief Represents a transaction in the log. Must be finalized for proper operation of the log.
  158. */
  159. struct sl_log_transaction {
  160. char* begin; /**< The start of the given writable span */
  161. char* end; /**< 1 element past the end of the writable span */
  162. /**
  163. * @brief Internal, do not touch
  164. * @internal this is a pointer to a registry_slab, hence the need for reference stability/pointer stability
  165. */
  166. sl_log_transaction_internals* internals;
  167. };
  168. #ifdef __cplusplus
  169. extern "C" {
  170. #endif
  171. /**
  172. * @brief Starts a transaction. You have to then write your logs between the start and the end of the transaction,
  173. * then finalize it.
  174. * @details
  175. * Guarantees are as follow:
  176. * - This function never allocates using the system allocator
  177. * - This function does not perform any system call
  178. * - This function may need to pad the log to be operable by C easily
  179. * - This function is both thread-safe and multiprocessing-safe
  180. *
  181. * @pre
  182. * We expect the provided internal_id to be valid and registered, if it is not, the transaction may look for a log
  183. * named "UNHANDLED_ERRORS" and log there instead silently.
  184. * @post
  185. * A started transaction MUST be finalized. The log can, upon initiation of the transaction, no longer be pushed to
  186. * its sink past the current sequence point of the transaction.
  187. * @param internal_id the logger id where the transaction is to happen
  188. * @param log_size the amount of bytes to write in the transaction.
  189. * @return a transaction of at least log_size bytes
  190. */
  191. sl_log_transaction sl_start_log_transaction(int internal_id, size_t log_size);
  192. /**
  193. * @brief Finalizes a transaction.
  194. * @details
  195. * Be cautious. Finalizing a finalized transaction should NEVER be done. It may wait forever, screw up your log, and
  196. * clog your log queue forever.
  197. * @pre
  198. * The transaction to finalize must never have been finalized before. If it has been finalized before, the
  199. * finalization may never terminate.
  200. * @post
  201. * All data written in the transaction is thenceforth scheduled to be written. The transaction state is now moot
  202. * and should not be accessed further, and should possibly be cleared.
  203. * @param target the transaction to finalize.
  204. */
  205. void sl_finalize_log_transaction(sl_log_transaction target);
  206. /**
  207. * @brief Clears a transaction object. This is not required but may help prevent bugs.
  208. * @param to_clear
  209. */
  210. inline void sl_clear_transaction(sl_log_transaction* to_clear) {
  211. #ifdef NDEBUG
  212. do {} while(false);
  213. #else
  214. #ifdef __cplusplus
  215. to_clear->begin = nullptr;
  216. to_clear->end = nullptr;
  217. to_clear->internals = nullptr;
  218. #else
  219. to_clear->begin = NULL;
  220. to_clear->end = NULL;
  221. to_clear->internals = NULL;
  222. #endif
  223. #endif
  224. }
  225. #ifdef __cplusplus
  226. }
  227. #endif