1
\$\begingroup\$

Summary

The code operates three memory mapped file segments:

  • just finished
  • currently active
  • next to write

Potentially slow operations of closing just finished and opening next to write are done in a dedicated background thread.

Concurrent writers know how much they need for a single operation and request that much from the segmented buffer at a time.

The writer that fails to check out the required size finalizes the currently active file (writing a marker and requesting background thread to close the file and prepare one more).

Source code on github: audit_buffer.hpp, the respective benchmark with usage demonstration: audit_benchmarks.cpp

The central part (which I am primarily concerned about) is below. I would appreciate general feedback on the implementation (specifically, code simplification and speedup), and also ideas on getting rid of the spin-lock.

/**
 * Reserves the  requred space in the buffer, jumping to the next segment if required
 */
std::shared_ptr<char> reserve(size_t required_size) {
    if (unlikely(required_size > filesize_ - 1))
        throw std::runtime_error("too much memory requested");

    std::shared_ptr<log_segment> segment;
    size_t pos = 0u;
    do {
        auto const segment_idx = segment_idx_.load(std::memory_order_acquire);
        auto const psegment = &segments_[segment_idx % segments_.size()];

        { // effectively std::atomic_load with custom spinlock
            guard_t guard {mutex_};
            segment = *psegment;
        }

        if (likely(segment)) {
            pos = segment->reserve(required_size);
            auto const last_pos = segment->size() - 1;
            if (unlikely(pos < last_pos && (pos + required_size >= last_pos))) {
                // it's this request that crossed the segment capacity

                { // effectively std::atomic_store with custom spinlock
                    guard_t guard {mutex_};
                    psegment->reset();
                }

                request_next_segment_();

                *(segment->memory() + pos) = 0x19; // End-of-Media
                segment.reset();

                // publish the next segment
                segment_idx_.store(segment_idx + 1, std::memory_order_release);
            }
            else if (unlikely(pos >= last_pos)) {
                segment.reset();
            }
        }
    } while (unlikely(!segment));

    return {segment, segment->memory() + pos}; // aliased shared_ptr to hold the segment alive
}
\$\endgroup\$

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Browse other questions tagged or ask your own question.