0

I am searching for a ringbuffer-implementation in C in userspace, so I can use it in my library.

Because I need a ringbuffer with

  • non-blocked write (=overwrite oldest data)
  • blocked read if empty

I searched a while and remembered I have used wait_event_interruptible & wake_up_interruptible to do something like this in kernel-mode.

But what is used in user-space so I maybe can search for a ringbuffer in combination with that method? I don't want to re-invent the wheel - there are many ringbuffer-solutions around.

Thanks in advance & with kind regards!

EDIT:

It seems that maybe pthread_cond_wait could be an equivalent of wait_event_interruptible.

Martin L.
  • 3,006
  • 6
  • 36
  • 60
  • Will you read/write the ringbuffer within a single process, or from multiple processes? Also, what operating system, or should it be portable? – Some programmer dude Feb 05 '13 at 09:11
  • I have a shared libary and would like to write into the buffer from the main-thread and read from it in another (p)thread (the shared library writes into a file in the main-thread, and should pass the data also to the second thread for further processing). The OS is an Android-device. – Martin L. Feb 05 '13 at 09:15
  • @MartinM. this is the exact solution which i also need!!! – akp Feb 05 '13 at 09:17
  • This looks like what you need: http://stackoverflow.com/questions/4405721/c-c-lock-free-or-nonblocking-ring-buffer-that-overwrites-oldest-data – Oguz Meteer Feb 05 '13 at 09:24
  • How do you want to handle buffer overflow? Writer blocks? Writer blindly overwrites, also overwriting data reader is just reading? Program is aborted? – hyde Feb 05 '13 at 09:33
  • The writer should override, but sure, that data should be safe in the read (lock + memcpy / block the writer for that microsecond?). I will analyze the link of Oguz at first. – Martin L. Feb 05 '13 at 09:37
  • Hm, the link of Oguz is not exactly what I needed, nor is there a real solution :/ – Martin L. Feb 05 '13 at 09:40

2 Answers2

6

Adding another answer with some code, which isn't 1:1 match to pseudocde in my other answer. Marking this as wiki answer, in case someone want's to add comments or do other improvements. C phtread mutex+condition variable implementation of very simple ringbuffer:

#include <stdio.h>
#include <pthread.h>

#define RINGBUFFER_SIZE (5)
int ringbuffer[RINGBUFFER_SIZE];
unsigned reader_unread = 0;
unsigned writer_next = 0;
pthread_mutex_t ringbuffer_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t ringbuffer_written_cond = PTHREAD_COND_INITIALIZER;

void process_code(int ch) {
    int counter;
    printf("Processing code %d", ch);
    for(counter=5; counter>0; --counter) {
        putchar('.');
        fflush(stdout);
        sleep(1);
    }
    printf("done.\n");

}

void *reader() {
    pthread_mutex_lock(&ringbuffer_mutex);
    for(;;) {
        if (reader_unread == 0) {
            pthread_cond_wait(&ringbuffer_written_cond, &ringbuffer_mutex);
        }
        if (reader_unread > 0) {

            int ch;
            int pos = writer_next - reader_unread;
            if (pos < 0) pos += RINGBUFFER_SIZE;
            ch = ringbuffer[pos];
            --reader_unread;

            if (ch == EOF) break;

            pthread_mutex_unlock(&ringbuffer_mutex);
            process_code(ch);
            pthread_mutex_lock(&ringbuffer_mutex);
        }
    }
    pthread_mutex_unlock(&ringbuffer_mutex);

    puts("READER THREAD GOT EOF");
    return NULL;
}

void *writer() {
    int ch;
    do {
        int overflow = 0;
        ch = getchar();

        pthread_mutex_lock(&ringbuffer_mutex);

        ringbuffer[writer_next] = ch;

        ++writer_next;
        if (writer_next == RINGBUFFER_SIZE) writer_next = 0;

        if (reader_unread < RINGBUFFER_SIZE) ++reader_unread;
        else overflow = 1;

        pthread_cond_signal(&ringbuffer_written_cond);
        pthread_mutex_unlock(&ringbuffer_mutex);

        if (overflow) puts("WARNING: OVERFLOW!");

    } while(ch != EOF);

    puts("WRITER THREAD GOT EOF");
    return NULL;
}

int main(void)
{
    pthread_t reader_thread, writer_thread;

    puts("Starting threads. Type text and press enter, or type ctrl-d at empty line to quit.");
    pthread_create(&reader_thread, NULL, reader, NULL);
    pthread_create(&writer_thread, NULL, writer, NULL);

    pthread_join(writer_thread, NULL);
    pthread_join(reader_thread, NULL);

    return 0;
}
hyde
  • 60,639
  • 21
  • 115
  • 176
  • Hello, I found a working solution today and I will post it in 1.5 weeks. I'm in holiday at the moment :) but your code seems to be a little easier and is maybe better. – Martin L. Feb 07 '13 at 20:15
  • 1
    I'm using this library now: https://code.google.com/p/ring-buff/ . It is a BLOCKING solution, but I think I could modify it e.g. with a timeout or something like that. Thank you very much for your help! – Martin L. Feb 17 '13 at 14:32
  • 1
    I have now removed the "ring-buff"-library and I'm using your code now. It's perfect, thanks again! – Martin L. Feb 28 '13 at 21:13
0

With pthreads, standard way is to use a mutex and use a condition variable for waiting in one thread until woken up by another.

Pseudocode, where writer will block briefly but never for indeterminate time, and buffer overflow is handled by throwing away unread data:

Writer write:

acquire new data to write
lock mutex
get current writing position in buffer
compare to current reading position and check for overflow
    in case of overflow, update reading position (oldest data lost)
write new data to buffer
update writing position
do wakeup on condition variable
unlock mutex

Reader read:

lock mutex
loop:
    get current reading position in buffer
    compare to current writing position in buffer
    if there's new data, break loop
    wait (possibly with timeout) on condition variable
    goto loop:
copy data from buffer
update reading position
unlock mutex
process copied data

Obviously above, writer may briefly block on mutex, but because reader will hold the mutex only briefly (this assumes data in buffer is fairly short), this is probably not a problem.

Important details about understanding above code: condition variable and mutex work as a pair. Waiting on condition variable will unlock the mutex, and once waken up, will continue only after it can re-lock the mutex. So reader will not actually continue until writer unlocks the mutex.

It's important to check buffer positions again when condition variable wait returns, and not blindly trust that wakeup was done by writer which just put more data to it.

hyde
  • 60,639
  • 21
  • 115
  • 176
  • Thank you very much for your explanation. I'm really new to C (used pthread yesterday the first time), so I would need days over days to implement this safely. I searched for hours again (beside my real job) but found no complete code for this. Maybe anyone has already a complete code for this? Thanks again. – Martin L. Feb 05 '13 at 15:29
  • maybe one can use and extend this one with mutex and triggers: https://github.com/dhess/c-ringbuf ? – Martin L. Feb 05 '13 at 15:37
  • @MartinM. I think my pseudocode translates pretty directly to C, and if you're going to use pthreads, that'd be a good way to learn it. I'll see if I have time to write some C later (no promises), I wrote pseudocode since it's been a while, and it's much easier to write correct pseudocode ;) – hyde Feb 05 '13 at 16:00
  • Thank you :) It is definitly a good start for me. I found a piece of code which maybe does what I need (with sem_wait instead of wait_event) and I will also give feedback if it works at the end. Maybe we can post a good working solution here at the end for the others. – Martin L. Feb 05 '13 at 16:08
  • For semaphores, a related question: http://stackoverflow.com/questions/70773/pthread-cond-wait-versus-semaphore – hyde Feb 05 '13 at 19:53
  • Hehe, this was already bookmarked. I will try and eventually rewrite the locking-section if the code works. – Martin L. Feb 05 '13 at 21:59
  • Hm, no, my code does not work. I would be really thankful if you could find time & nerves to transfer your example into real code. – Martin L. Feb 06 '13 at 14:23
  • You've probably solved your problem already, but today I got around to brushing up my pthread knowledge, and wrote that another answer with working C code, which I marked as community wiki as well. Feel free to upvote both and also accept one of the answers... :) – hyde Feb 07 '13 at 19:47