2
votes

I've got a producer and a consumer. The producer writes fixed size items on a given shared memory area, and the consumer retrieves them.

The producer can be noticeably slower or faster than the consumer, randomly.

What we want is that

  1. If the producer is running faster than the consumer, when it fills the circular buffer, it keeps writing on the oldest frames (other than the one that the consumer is consuming, of course - I stress this point, producer and consumer must be synchronized in the solution, because they are unrelated processes).

  2. If, instead, the consumer is faster than the producer, it must wait for a new frame and consume it when it's there.

I found implementations of producer/consumers with circular buffers, but only ones that didn't respect the first request (ie, if the circular buffer is full, they wait for the consumer to finish, while what I want is to overwrite the oldest frames).

I'd prefer not to roll my own (prone to bugs) solution, but use a pre-canned, tested one. Can someone point me to a good C implementation? (C++ is also ok).

Many thanks.

2
Sounds like you're just trying to remove a restriction that someone had to code into the implementations you looked at. If you just delete whatever code is stopping the producer from overwriting data in its buffer, I'd think it would work just like what you're looking for. - Carl Norum
@Carl - as well as letting the head overwrite the tail, you would also need to advance the tail pointer, otherwise what you're doing is equivalent to just emptying the whole buffer. - ChrisW
@Carl - No, just permitting the producer to overwrite old buffers removing the semaphore would mean overwriting the item the consumer is consuming. What I need is a synchronized producer consumer implementation. - janesconference
You can just copy out the data that the consumer needs, and have it do stuff with the copy, then the original in the buffer is free to be overwritten. What you'd then be overwriting would be the thing it would have next consumed (the oldest item currently in the buffer). - Greg Rogers
@Greg: no, I can't do another copy. Moreover, copying costs time. what if the producer writes on the original buffer while I'm copying it? While consumer does stuff on one item, be it copying it or whatever, the producer simply mustn't touch it. - janesconference

2 Answers

0
votes

Basically when the consumers are slow, it means that no one is using the buffer, so there is no difference between dropping the new frames and overriding the old frames. So maybe the following code can help. The producerRTLock cannot lock the buffer because there are consumers using the bufffer and therefore at the application level you can indicate to drop the frames.

class SampleSynchronizer {

  mutex mux;

  condition_variable con_cond;
  unsigned int con_num;

  condition_variable pro_cond;
  bool prod;

public:

  SampleSynchronizer(): con_num(0), prod(false) {
  } 

  void consumerLock() {
    unique_lock<mutex> locker(mux);
    while(prod)
      pro_cond.wait(locker);

    con_num++;
  }

  void consumerUnlock() {
    lock_guard<mutex> locker(mux);
    con_num--;
    con_cond.notify_one();
  }

  void producerLock() {
    unique_lock<mutex> locker(mux);
    while(con_num > 0)
      con_cond.wait(locker);

    prod = true;
  }

  bool producerRTLock() {
    lock_guard<mutex> locker(mux);
    if(con_num > 0)
      return false;

    prod = true;
    return true;
  }

  void producerUnlock() {
    lock_guard<mutex> locker(mux);
    prod = false;
    pro_cond.notify_all();
  }

};
-1
votes

Sounds like you want to use a double ended queue which is provided in C++'s Standard Template Library (STL). But that uses C++, not C. If your producer and consumer are different threads then you'll also need a mutex to protect the queue structure.