简体   繁体   中英

Circular-/Ring-Buffer with blocking read and non-blocking write?

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 .

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;
}

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM