Skip to content

Latest commit

 

History

History
73 lines (58 loc) · 2.93 KB

spsc.md

File metadata and controls

73 lines (58 loc) · 2.93 KB

SPSC - single producer, single consumer

This lock-free queue is safe to use between one producer thread and one consumer thread. SPSC lock-free options:

  1. circular_fifo: Set the size of the queue in the constructor.

The SPSC is a powerful building block from which you can create more lock-free complicated queue structures if number of producers and consumers are known at creation time.

SPSC Naive Example The raw and "unsafe" way to create the queue is to just "create it". This makes it harder to use right as the producer and consumer threads must only touch "their" parts of the API or the queue would not be thread-safe.

using spsc_queue_type = spsc::circular_fifo<string>;
auto q_size = 1000;
auto spsc_queue = spsc_queue_type(q_size); 
//  through spsc_queue the FULL producer/consumer API is available, 
//  which can be dangerous

A better example

A better way to use the spsc queue is through the q/q_api.hpp. A constructor method will generate the queue that is embedded in a std::pair with template-enforced facade which only allows the safe APIs for the p (producer) and c (consumer) specific APIs.

This makes it easy and safe, as you share only the p or c part with a dedicated thread. See q/q_api.hpp for details on how this is implemented.

// somewhere
template<typename T>
void sendData(T producer) { ..... }

// somewhere
template<typename T>
void receiveData(T consumer) { ..... }

auto queue = queue_api::CreateQueue<spsc_queue_type>(q_size);
auto senderQ = std::get<queue_api::index::sender>(queue);
std::thread producer_thread(sendData, std::ref(senderQ));

auto receiverQ = std::get<queue_api::index::receiver>(queue);
std::thread consumer_thread(receiveData, std::ref(receiverQ));
...
// producer_thread.join();
// consumer_tread.join();

See a runnable example at examples/spsc_main.cpp See some benchmarking at benchmark/spsc_benchmark.cpp

SPSC API

From queue_api::Base (see Curiously recurring template pattern) we have

     // qref is the actual queue instance, regardless of type. 
      bool empty() const;
      bool full() const;
      size_t capacity() const;
      size_t capacity_free() const;
      size_t size() const;
      bool lock_free() const;
      size_t usage() const;å

From queue_api::Sender: public Base

    bool push(Element& item);

From queue_api::Receiver: public Base

    bool pop(Element& item);
    bool wait_and_pop(Element& item, const milliseconds wait_ms) { return sfinae::wait_and_pop(... }

SPSC Usage

Please see the examples/spsc_main.cpp for example usage.