Skip to content

File spsc.hpp

File List > include > loon > spsc.hpp

Go to the documentation of this file

// Copyright (c) 2026 Jorge Suarez-Rivaya
// SPDX-License-Identifier: MIT

#pragma once

#include <atomic>
#include <cstddef>
#include <new>

#ifndef CACHE_LINE_SIZE
#if defined(__cpp_lib_hardware_interference_size)
#define CACHE_LINE_SIZE std::hardware_destructive_interference_size
#else
#define CACHE_LINE_SIZE 64
#endif
#endif

namespace loon {

template <typename T, size_t N>
class SpscQueue {
  static_assert(N > 0, "SpscQueue capacity must be greater than 0");
  static_assert(std::atomic<size_t>::is_always_lock_free, "SpscQueue requires lock-free atomics");

 public:
  [[nodiscard]] bool push(const T& value) {
    auto write = write_idx_.load(std::memory_order_relaxed);
    if (write - read_idx_cache_ == N) {
      read_idx_cache_ = read_idx_.load(std::memory_order_acquire);
      if (write - read_idx_cache_ == N)
        return false;
    }
    data_[write % N] = value;
    write_idx_.store(write + 1, std::memory_order_release);
    return true;
  }

  [[nodiscard]] bool pop(T& value) {
    auto read = read_idx_.load(std::memory_order_relaxed);
    if (write_idx_cache_ == read) {
      write_idx_cache_ = write_idx_.load(std::memory_order_acquire);
      if (write_idx_cache_ == read)
        return false;
    }
    value = data_[read % N];
    read_idx_.store(read + 1, std::memory_order_release);
    return true;
  }

  size_t capacity() const { return N; }

  [[nodiscard]] bool empty() const {
    return write_idx_.load(std::memory_order_acquire) == read_idx_.load(std::memory_order_acquire);
  }

  [[nodiscard]] bool full() const {
    return write_idx_.load(std::memory_order_acquire) - read_idx_.load(std::memory_order_acquire) ==
           N;
  }

 private:
  T data_[N];
  alignas(CACHE_LINE_SIZE) std::atomic<size_t> write_idx_{0}; // Producer-owned
  alignas(CACHE_LINE_SIZE) size_t write_idx_cache_{0};        // Producer's cache of read_idx_
  alignas(CACHE_LINE_SIZE) std::atomic<size_t> read_idx_{0};  // Consumer-owned
  alignas(CACHE_LINE_SIZE) size_t read_idx_cache_{0};         // Consumer's cache of write_idx_
};

} // namespace loon