# Producer-consumer with a twist (consumer is a reader) in C++

## Executive summary

The twist is that the consumers do not consume, they just read. The producer makes items continuously in one of two alternating slots. Readers read from the other slot. When producer comes up with a new item it waits until readers are done with their read slot (that would become the new write slot). The slots effectively alternate between write slot and read slot.

## The long story

Background: solving a practical problem - watching my son overnight using an infrared Raspberry Pi camera. Came up with a basic C++ web server. One thread takes pictures (the producer). It takes ~6 secs for a picture! As explained above, the pictures are kept in memory in 2 picture slots - one in which the current picture is taken and the other one from which one can only read. The readers are web browsers (> 50 msecs to serve a picture).

The code below works (as much as tested) but doesn't look pretty. Locks are abused and notify() is called way too many times. Is there for sure no deadlock/race condition?

I looked at myself reasoning about the problem and saw myself adding a mutex/condition variable to solve this or that... notify() called too often? Well, adding 'if (no readers)' is tempting but things can change underneath... so protect that with a mutex... patching... I'd like an approach that's crystal clear.

Feel free to scrap the code and come up with a different approach. This wheel should have been already invented somewhere. Use semaphores if that makes things clearer. Should I ask for more than 2 slots (as latency becomes an issue on slow links/readers) :)? Maybe later.

The idea is that callbacks are called with an index (slot) argument. For the duration of producer callback there should be a guarantee that no readers are using that index, etc. main() is just the test harness.

#include <mutex>
#include <condition_variable>
#include <atomic>
#include <iostream>
#include <unistd.h>

std::mutex global_mutex;
std::condition_variable cv[2];
std::mutex slot_mutex[2];
int write_slot{ 1 };

// When callback is called below only the producer uses the slot index
void produce(std::function<void(int index)>& callback)
{
{   // 'Swap' the read/write slots
std::lock_guard<std::mutex> guard(global_mutex);
write_slot = (write_slot + 1) % 2;
}

// Wait until no readers in the write slot
std::unique_lock<std::mutex> lock(slot_mutex[write_slot]);
cv[write_slot].wait(lock, []{ return reader_count[write_slot] == 0;} );

callback(write_slot);  // USE EXCLUSIVELY write_slot
}

// >1 reader (and no producer) can call callback below
{
// Busy wait to get the read slot
while (1)
{
{
std::lock_guard<std::mutex> guard(global_mutex);
}

bool slot_acquired = lock.try_lock();
if (slot_acquired)
{
break;
}
else
{
}
}

}

int main()
{
// 6 secs to take a picture
std::function<void(int)> producerFct = [](int index) { /* use index */; sleep(6); };

// Producer

// 50 msec to send the photo
std::function<void(int)> readerFct = [](int index) { /* use index */; usleep(50000); };