rb_read_commit¶
NAME¶
rb_read(3), rb_recv(3), rb_read_claim(3), rb_read_commit(3), rb_recv_claim(3), rb_recv_commit(3), rb_read_commit_claim(3), rb_recv_commit_claim(3), rb_readv(3), rb_recvv(3) - read data from a ring buffer.
SYNOPSIS¶
#include <rb.h>
long rb_read(struct rb *rb, void *buffer, size_t count);
long rb_readv(struct rb *rb, const struct rb_iovec *vec, int iovcnt);
int rb_read_claim(struct rb *rb, void **buffer, size_t *count,
size_t *object_size);
int rb_read_commit(struct rb *rb, size_t count);
int rb_read_commit_claim(struct rb *rb, void **buffer, size_t *count,
size_t *object_size)
long rb_recv(struct rb *rb, void *buffer, size_t count, enum rb_flags flags);
long rb_recvv(struct rb *rb, const struct rb_iovec *vec, int iovcnt,
enum rb_flags flags);
int rb_recv_claim(struct rb *rb, void **buffer, size_t *count,
size_t *object_size, enum rb_flags flags);
int rb_recv_commit(struct rb *rb, size_t count, enum rb_flags flags);
int rb_recv_commit_claim(struct rb *rb, void **buffer, size_t *count,
size_t *object_size, enum rb_flags flags);
DESCRIPTION¶
rb_read(3) will read count objects from rb buffer and copy them to buffer. buffer must be big enough to hold count elements of size that was configured during rb initialization (via object_size). Hence, buffer should be at least (count * rb->object_size) big. Function may read less elements than requested if there was not enough data in rb. count can be 0, in those cases no data will be written, but function may still perform some check and return errors.
If rb was created with rb_dynamic flag, function will always read a single object from the rb. In that case count tells how big, in bytes, buffer is. If count is not big enough to hold whole frame, function will return error.
If rb is multi-thread aware, and there is no data in rb, caller thread will be blocked until someone writes to rb. Function still can read less than requested number of elements, and will not block until all count elements is read. If at least 1 element is read until buffer gets empty, function will not block. After successful read, function will wake potential thread blocked at write operation.
rb_recv(3) behaves in the same way but also accepts flags to alter buffer behavior for one single call. Possible flags are:
flag |
description |
|---|---|
rb_peek |
Read data normally, but do not remove it from the rb buffer. When that flag is passed, function will never block, and if there is no data on the buffer, error will be returned. |
rb_dontwait |
Read data normally but do not block if buffer is empty. Instead return error. |
rb_readv(3) instead of accepting single buffer, takes vector of buffers instead. This is scatter “input”. When rb is not dynamic, this is equivalent of just calling rb_read(3) in a loop, one time for each buffer in vec vector. For dynamic buffers, created with rb_dynamic flag, this will behave same as if you passed single buffer with the size of all vec[].len, but data instead of landing in a single buffer, will be scattered across buffers in vec. This means that only single dynamic object will be taken from the buffer. Last buffer can be larger than data in the rb buffer, in which case all buffers will be filled in, and last buffer will have less bytes. Function returns total number of bytes read, so you should do your own math if that information matters to you. When function returns success, it is guaranteed that single call will take single object from rb buffer and scatter the data without interrupts - in other words, reads are atomic.
rb_recvv(3) behaves the same but takes same flags as rb_recv(3) to alter per call behavior.
rb_read_claim(3) doesn’t read anything from the rb buffer, but instead gives you all the information needed for you to perform the read. On successful return, buffer will be pointing at first readable byte, and you can read starting at offset 0. count will tell you how many elements you can read from buffer. If rb is close to memory wrap, you may get less data than there is actually in the rb buffer. object_size defines size in bytes of a single object that can be read from buffer.
If rb is multi-thread aware, function will return with locked read mutex, so no one else will be able to read from buffer until you call rb_read_commit(3) and release mutex.
rb_read_commit(3) must be called after rb_read_claim(3) with count being number of elements (not bytes) you read from the buffer. If rb is multi-thread aware, this will also wake up thread blocked in write operation.
rb_read_commit_claim(3) can be used to commit data to ring buffer as you would do with rb_read_commit(3), but also immediately claims new buffer as you would do with rb_read_claim(3). Benefit of calling this function is that you can get new buffer without releasing the lock. This may be useful when you must perform multiple reads from buffer while making sure no one else disturbs you in the middle.
Function takes same parameters as rb_read_claim(3) but count is both input and output. As input arguments is defines how many bytes you commit to the buffer, same calling rb_read_commit(3). As output it defines size of returned buffer as with rb_read_claim(3).
RETURN VALUE¶
rb_read(3), rb_recv(3), rb_readv(3) and rb_recvv(3) will return number of elements actually read from ring buffer. rb_read_commit(3), rb_read_claim(3), rb_recv_commit(3), rb_recv_claim(3), rb_read_commit_claim(3) and rb_recv_commit_claim(3) return 0 on success.
All functions will return -1 on errors with errno variable set that will describe an error. If error is returned no data is removed from the buffer.
ERRORS¶
All functions may return one of these on error
- EINVAL
Any of the input parameter is invalid
- EAGAIN
Ring buffer is empty and function returned without reading anything.
When rb was created with rb_dynamic, these additional errors may appear
- ENOBUFS
Data is available on the buffer, but passed buffer is not big enough to hold whole message.
When rb is multi-thread aware, these additional error may appear
- EAGAIN
rb was created with rb_growable while being thread-aware and ring buffer is in process of increasing size. Read notes in rb_write(3) man page.
- ECANCELED
Other thread called rb_stop(3) and no data has been read.
EXAMPLES¶
Note that these examples do not have error handling for simplicity.
Simple read. Assuming ring buffer holds simple integers.
int rd_buf[128];
long nread;
nread = rb_read(rb, rd_buf, sizeof(rd_buf));
Read but force non blocking operation
int rd_buf[128];
long nread;
nread = rb_recv(rb, rd_buf, sizeof(rd_buf), rb_dontwait);
Claim buffer, and send data over serial line. Thanks to claim/commit we don’t have to create any intermediate buffer and do double copying.
long nwritten;
void *buffer;
size_t count, object_size;
rb_read_claim(rb, &buffer, &count, &object_size, 0);
nwritten = write(serial_fd, buffer, count * object_size);
/* tell rb, how many bytes we actually used, write() may return
* less than we asked it to send */
rb_read_commit(rb, nwritten / object_size);
Perform scatter read on dynamic ring buffer
char str[] = "test\0data\0to\0read\n\0";
char a[5], b[5], c[3], d[16];
struct rb_iovec iov[] = {
{ .base = a, .len = sizeof(a) },
{ .base = b, .len = sizeof(b) },
{ .base = c, .len = sizeof(c) },
{ .base = d, .len = sizeof(d) },
};
rb_write(rb, str, sizeof(str));
rb_readv(rb, iov, rb_array_size(iov));
printf("%s %s %s %s", a, b, c, d);
/* will print "test data to read" */
SEE ALSO¶
rb_new(3), rb_init(3), rb_destroy(3), rb_cleanup(3), rb_write(3), rb_send(3), rb_writev(3), rb_sendv(3), rb_read(3), rb_recv(3), rb_readv(3), rb_recvv(3), rb_read_claim(3), rb_read_commit(3), rb_write_claim(3), rb_write_commit(3), rb_clear(3), rb_discard(3), rb_count(3), rb_space(3), rb_stop(3), rb_peek_size(3), rb_set_hard_max_count(3)