spscqueue

repository·master·Indexed 23 days ago

https://github.com/rigtorp/spscqueue

A high-performance, wait-free, and lock-free single-producer single-consumer (SPSC) fixed-size queue implemented in C++11. It is designed to reduce cache coherency traffic and supports custom allocators, including C++17's allocate_at_least for efficient use of huge pages.

Tokens
1K
Snippets
2
Records
3
Agent score
29%

What's inside spscqueue

  1. Use SPSCQueue for single-producer single-consumer communication

    master

    SPSCQueue is a wait-free and lock-free fixed-size queue designed for C++11. It is optimized for scenarios where exactly one thread performs enqueue operations (the producer) and exactly one thread performs dequeue operations (the consumer).

    Warning: Any other usage pattern (e.g., multiple producers or multiple consumers) is invalid and will lead to undefined behavior.

    SPSCQueue<int> q(1);
    auto t = std::thread([&] {
      while (!q.front());
      std::cout << *q.front() << std::endl;
      q.pop();
    });
    q.push(1);
    t.join();
  2. Use huge pages with SPSCQueue via custom allocators

    master

    SPSCQueue supports custom allocators following the standard allocator interface. If C++17 is enabled, it also supports the P0401R3 proposal (allocate_at_least), allowing for efficient use of huge pages without wasting allocated space.

    Because huge page APIs are platform-dependent, the library does not provide a built-in huge page allocator. You must provide your own. Below is an example of a huge page allocator for Linux using mmap and MAP_HUGETLB.

    #include <sys/mman.h>
    
    template <typename T> struct Allocator {
      using value_type = T;
    
      struct AllocationResult {
        T *ptr;
        size_t count;
      };
    
      size_t roundup(size_t n) { return (((n - 1) >> 21) + 1) << 21; }
    
      AllocationResult allocate_at_least(size_t n) {
        size_t count = roundup(sizeof(T) * n);
        auto p = static_cast<T *>(mmap(nullptr, count, PROT_READ | PROT_WRITE,
                                       MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB,
                                       -1, 0));
        if (p == MAP_FAILED) {
          throw std::bad_alloc();
        }
        return {p, count / sizeof(T)};
      }
    
      void deallocate(T *p, size_t n) { munmap(p, roundup(sizeof(T) * n)); }
    };
  3. SPSCQueue API Reference

    master

    The following methods are available for managing the queue. Note that for pop(), you must ensure the queue is non-empty by checking front() first.

    Construction

    • SPSCQueue<T>(size_t capacity): Creates a queue of type T with the specified capacity. Capacity must be at least 1.

    Enqueue Operations (Producer)

    • void emplace(Args &&... args): Enqueues an item using in-place construction. Blocks if the queue is full.
    • bool try_emplace(Args &&... args): Tries to enqueue an item using in-place construction. Returns true on success, false if full.
    • void push(const T &v): Enqueues an item using copy construction. Blocks if the queue is full.
    • template <typename P> void push(P &&v): Enqueues an item using move construction. Blocks if the queue is full.
    • bool try_push(const T &v): Tries to enqueue an item using copy construction. Returns true on success, false if full.
    • template <typename P> bool try_push(P &&v): Tries to enqueue an item using move construction. Returns true on success, false if full.

    Dequeue Operations (Consumer)

    • T *front(): Returns a pointer to the front item. Returns nullptr if the queue is empty.
    • void pop(): Dequeues the first item. Requirement: You must ensure the queue is non-empty before calling (i.e., front() must not be nullptr). Requires T to be std::nothrow_destructible.

    Inspection

    • size_t size(): Returns the number of items currently in the queue.
    • bool empty(): Returns true if the queue is empty.