bofc manual pages




rb_overview - quick overview of librb ring buffer library  


librb - library that provides fast, easy to use ring buffer implementation. Its interface is very similar to read/write functions from POSIX. Basic usage can be done with only 4 functions:

#include <librb.h>

struct rb *rb_new(size_t count, size_t object_size, unsigned long flags);
int rb_destroy(struct rb *rb);
long rb_read(struct rb *rb, void *buffer, size_t count);
long rb_write(struct rb *rb, const void *buffer, size_t count);

Additinal functions are provided for better control over buffer

struct rb *rb_init(size_t count, size_t object_size, unsigned long flags, void *mem);
int rb_cleanup(struct rb *rb);
long rb_recv(struct rb *rb, void *buffer, size_t count, unsigned long flags);
long rb_send(struct rb *rb, const void *buffer, size_t count, unsigned long flags);
int rb_clear(struct rb *rb, int clear);
long rb_count(struct rb *rb);
long rb_space(struct rb *rb);
int rb_stop(struct rb *rb);
size_t rb_header_size(void);
const char *rb_version(char *major, char *minor, char *patch);

Convenience functions available on POSIX compliant systems.

long rb_posix_read(struct rb *rb, int fd, size_t count);
long rb_posix_write(struct rb *rb, int fd, size_t count);
long rb_posix_recv(struct rb *rb, int fd, size_t count, unsigned long flags);
long rb_posix_send(struct rb *rb, int fd, size_t count, unsigned long flags);  


librb is a simple ring buffer implementation that operates on objects rather than raw bytes. See rb_new(3) to know what that means. By default library operates in single-thread mode which is way faster than multi-thread mode but naturally will break if concurrent thread will try to access rb object. To relief user from pesky synchronization process, library can optionally be thread safe and thread aware by passing O_MULTITHREAD flag to rb_new(3). In multi-thread mode if there are no resources available while reading or writting, caller thread gets locked and doesn't use any resources until data is available. This behaviour can be altered to work in non-blocking mode, so calls from read and write will return immediately when there are not enough resources. malloc and free are called only in new and destory functions. You can also use buffer and provide own mem pointer to memory that buffer should work on (like an array on stack), in such case there will be no dynamic allocations performed by library.

As this library is focused on speed, user can create buffer with only power of two count (n^2). Thanks to this solution there are much less conditional jumps. Altough user is limited to number of elements that can be stored in buffer, single element can be any size. Thanks to that, ring buffer can be used with raw data (like from 8bit adc when object size is 1 byte) or bigger raw data (like from 12bit adc when object size is 2 bytes) or even more sophisticated types like structures with any number of parameters when object size is set to sizeof(struct s).

When using POSIX calls when O_MULTITHREAD is enabled extra care must be taken as improper use may cause deadlocks or application processing events very slow. Here's how these POSIX functions work. Since read() and write() functions may block (no data in kernel buffer at the time or buffer is full), rb implements special locking mechanism to prevent situations when first thread reads half of the frame, then syscall blocks and another threads reads second half of the frame. Now two threads have one half of one frame, and second half of next frame, and none of the frames are valid. Upon entering any of the POSIX function, entering thread will lock read or write mutex, meaning when thread 1 enters any read function, no other thread will be able to read until thread 1 completly finishes its read. Now you can see the problem, thread 1 may go in and perform read() on super slow socket and it will block threads that may also want to read into same rb object but from different, much faster socket. This situation, of course, can occur only when multiple threads uses same rb object.  


Please note, that example is missing error handling for simplicity.

    #include <rb.h>
    #include <stdio.h>

    int main(void)
        char i;
        char data[] = "abcdefghij";
        struct rb *rb;

        /* initialize ring buffer with 32 1-byte elements */
        rb = rb_new(32, 1, 0);

        /* add data to buffer one byte at a time */
        for (i = '0'; i <= '9'; ++i)
            rb_write(rb, &i, 1);

        /* add data in a single burst */
        rb_write(rb, data, sizeof(data) - 1);

        /* print data in packets of 8 bytes */
        for (;;)
            int bytes_read;
            char buf[8 + 1] = {0};

            bytes_read = rb_read(rb, buf, 8);

            if (bytes_read == -1)
                if (errno == EAGAIN) /* nothing left to read */

                perror("read failed");

            printf("%s\n", buf);

        /* clean up */

        return 0;


rb_overview(7), rb_new(3), rb_init(3), rb_destroy(3), rb_cleanup(3), rb_discard(3), rb_stop(3), rb_stop_signal(3), rb_read(3), rb_recv(3), rb_write(3), rb_send(3), rb_posix_read(3), rb_posix_recv(3), rb_posix_write(3), rb_posix_send(3), rb_clear(3), rb_count(3), rb_space(3), rb_header_size(3), rb_array_size(3), rb_version(3)


23 October 2018 (v1.1.0)