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\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.