|
|
- #pragma once
-
- #pragma once
-
- #ifdef __cplusplus
- #include <cstdint>
- #include <cstddef>
- #include <iterator>
-
- #else
- #include <stdint.h>
- #include <stddef.h>
- #endif
-
-
- struct sl_log_transaction;
- struct sl_log_transaction_internals;
-
- #ifdef __cplusplus
- namespace sl {
- using cstyle_log_transaction = sl_log_transaction;
- using cstyle_log_transaction_internals = sl_log_transaction_internals;
-
- /**
- * @brief This class represents a logging transaction
- * @details
- * This class has the following guarantees:
- * - Can be built on the stack safely
- * - This class never allocates using the system allocator
- * - This class does not perform any system call
- * - Finalization cannot happen twice from the public interface
- * - This class may pad the log by up to <tt>std::hardware_destructive_interference</tt> bytes
- * - This class is both thread-safe and multiprocessing-safe to finalize
- *
- * @details
- * This class expects the finalization to occur in the same thread that wrote any data to the log.
- * @pre
- * This class expects the logging buffer to be at least 2 binary orders of magnitude bigger than the largest log
- * message. It is recommended that it is at least 6 binary orders of magnitude bigger than the largest log.
- * @post
- * This class leaves the log in a valid state in accordance to the defined log strategies.
- */
- class log_transaction final {
- char* local_start;
- size_t local_size;
- char* buffer_start;
- size_t buffer_size;
- void* buffer_initiator_page;
- bool is_finalized = false;
-
- void finalize_impl();
-
- /**
- * @brief A forward iterator to the current log transaction.
- */
- class iterator final {
- using iterator_category = std::forward_iterator_tag;
- using difference_type = std::ptrdiff_t;
- using value_type = char;
- using pointer = char*;
- using reference = char&;
-
- log_transaction& owner_ref;
- size_t index;
- public:
- explicit iterator(log_transaction& parent, size_t position = 0)
- : owner_ref(parent)
- , index(((parent.buffer_start - parent.local_start) + position)%parent.buffer_size)
- {}
- iterator& operator++() {
- index+=1;
- index%=owner_ref.buffer_size;
- return *this;
- }
- iterator operator++(int) {
- auto cpy = *this;
- index+=1;
- index%=owner_ref.buffer_size;
- return cpy;
- }
- difference_type operator-(iterator other) const {
- size_t forward, backward;
- forward = owner_ref.buffer_size - index;
- forward += other.index;
- backward = other.index - index;
- return static_cast<difference_type>(std::min(forward, backward));
- }
- reference operator*() {
- return owner_ref.buffer_start[index];
- }
- };
- public:
- /**
- * @brief Starts a transaction. You have to then write your logs between the start and the end of the
- * transaction, then finalize it.
- * @details
- * Guarantees are as follow:
- * - This constructor never allocates using the system allocator
- * - This constructor does not perform any system call
- * - This constructor may pad the log by up to <tt>std::hardware_destructive_interference</tt> bytes
- * - This constructor is both thread-safe and multiprocessing-safe
- *
- * @pre
- * We expect the provided <tt>internal_id</tt> to be valid and registered, if it is not, the transaction may look for a
- * log named "UNHANDLED_ERRORS" and log there instead silently.
- * @post
- * A started transaction MUST be finalized. The log can, upon initiation of the transaction, no longer be
- * pushed to its sink past the current sequence point of the transaction.
- *
- * @post
- * This class will finalize an un-finalized transaction
- * @param internal_id the logger id where the transaction is to happen
- * @param log_size the amount of bytes to write in the transaction.
- */
- explicit log_transaction(int internal_id, size_t log_size);
-
- log_transaction(const log_transaction&) = delete;
- log_transaction(log_transaction&&) = delete;
- void operator=(const log_transaction&) = delete;
- void operator=(log_transaction&&) = delete;
-
- /**
- * @brief Unsafely accesses the underlying buffer
- * @param index The index to the buffer to access
- * @return a reference to an underlying <tt>char</tt>, undefined behaviour is the index is invalid
- */
- char& operator[](size_t index) {
- return buffer_start[((buffer_start - local_start) + index)%buffer_size];
- }
-
- /**
- * @brief Obtains a forward iterator to the start of the writable range
- * @return a non-descript iterator to the beginning of the range
- */
- iterator begin() {
- return iterator(*this, 0);
- }
-
- /**
- * @brief Obtains a forward iterator to the end of the writable range
- * @return a non-descript iterator to one-past-the-end of the range
- */
- iterator end() {
- return iterator(*this, local_size);
- }
-
- /**
- * @brief Finalizes the transaction
- * @details
- * Finalizes a transaction, makes access to the finalized buffer undefined behaviour. If <tt>NDEBUG</tt> is
- * defined, accesses will lead to a near-nullptr dereference.
- *
- * @details
- * You are NOT protected against erroneous double finalization in debug mode. You are protected against double
- * finalization out of debug mode. This protection and these erroneous states do not interact with the
- * destructor, which will finalize transactions if not finalized yet.
- */
- void finalize();
-
- /**
- * @see{void finalize()}
- */
- ~log_transaction(){
- if(!is_finalized) finalize_impl();
- }
- };
- }
-
- #else
- typedef struct sl_log_transaction log_transaction;
- typedef struct sl_log_transaction_internals log_transaction_internals;
- #endif
-
- /**
- * @brief Represents a transaction in the log. Must be finalized for proper operation of the log.
- */
- struct sl_log_transaction {
- char* begin; /**< The start of the given writable span */
- char* end; /**< 1 element past the end of the writable span */
-
- /**
- * @brief Internal, do not touch
- * @internal this is a pointer to a registry_slab, hence the need for reference stability/pointer stability
- */
- sl_log_transaction_internals* internals;
- };
-
- #ifdef __cplusplus
- extern "C" {
- #endif
- /**
- * @brief Starts a transaction. You have to then write your logs between the start and the end of the transaction,
- * then finalize it.
- * @details
- * Guarantees are as follow:
- * - This function never allocates using the system allocator
- * - This function does not perform any system call
- * - This function may need to pad the log to be operable by C easily
- * - This function is both thread-safe and multiprocessing-safe
- *
- * @pre
- * We expect the provided internal_id to be valid and registered, if it is not, the transaction may look for a log
- * named "UNHANDLED_ERRORS" and log there instead silently.
- * @post
- * A started transaction MUST be finalized. The log can, upon initiation of the transaction, no longer be pushed to
- * its sink past the current sequence point of the transaction.
- * @param internal_id the logger id where the transaction is to happen
- * @param log_size the amount of bytes to write in the transaction.
- * @return a transaction of at least log_size bytes
- */
- sl_log_transaction sl_start_log_transaction(int internal_id, size_t log_size);
-
- /**
- * @brief Finalizes a transaction.
- * @details
- * Be cautious. Finalizing a finalized transaction should NEVER be done. It may wait forever, screw up your log, and
- * clog your log queue forever.
- * @pre
- * The transaction to finalize must never have been finalized before. If it has been finalized before, the
- * finalization may never terminate.
- * @post
- * All data written in the transaction is thenceforth scheduled to be written. The transaction state is now moot
- * and should not be accessed further, and should possibly be cleared.
- * @param target the transaction to finalize.
- */
- void sl_finalize_log_transaction(sl_log_transaction target);
-
- /**
- * @brief Clears a transaction object. This is not required but may help prevent bugs.
- * @param to_clear
- */
- inline void sl_clear_transaction(sl_log_transaction* to_clear) {
- #ifdef NDEBUG
- do {} while(false);
- #else
- #ifdef __cplusplus
- to_clear->begin = nullptr;
- to_clear->end = nullptr;
- to_clear->internals = nullptr;
- #else
- to_clear->begin = NULL;
- to_clear->end = NULL;
- to_clear->internals = NULL;
- #endif
- #endif
- }
- #ifdef __cplusplus
- }
- #endif
|