rb_send¶
NAME¶
rb_write(3), rb_send(3), rb_writev(3), rb_sendv(3), rb_write_claim(3), rb_write_commit(3), rb_send_claim(3), rb_send_commit(3), rb_write_commit_claim(3), rb_send_commit_claim(3) - write data to a ring buffer.
SYNOPSIS¶
#include <rb.h>
rb_write(struct rb *rb, const void *buffer, size_t count);
long rb_writev(struct rb *rb, const struct rb_iovec *vec, int iovcnt);
long rb_write_claim(struct rb *rb, void **buffer, size_t *count,
size_t *object_size);
long rb_write_commit(struct rb *rb, size_t count);
long rb_write_commit_claim(struct rb *rb, void **buffer, size_t *count,
size_t *object_size);
rb_send(struct rb *rb, const void *buffer, size_t count, enum rb_flags flags);
long rb_sendv(struct rb *rb, const struct rb_iovec *vec, int iovcnt,
enum rb_flags flags);
rb_send_claim(struct rb *rb, void **buffer, size_t *count,
size_t *object_size, enum rb_flags flags);
long rb_send_commit(struct rb *rb, size_t count, enum rb_flags flags);
long rb_send_commit_claim(struct rb *rb, void **buffer, size_t *count,
size_t *object_size, enum rb_flags flags);
DESCRIPTION¶
rb_write(3) will write count elements from buffer into rb ring buffer. Function may write less elements than requested, if buffer gets full during write. 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 write a single object to the rb. In that case count tells how big, in bytes, buffer is. If there is not enough space in rb buffer, to hold whole buffer, function will not write anything and return with error.
If rb was created with rb_growable and rb gets full during write, internal buffer will be increased in size by factor of 2. rb will not get increased past hard limit configured with rb_set_hard_max_count(3). If rb can no longer increase size, function will start behaving as it was no longer growable.
If rb is multi-thread aware, and there is no space in rb, caller thread will be blocked until someone reads from rb. Function will block until all count elements have been written to ring buffer, or until error occurs that is. After successful read, function will wake potential thread blocked at read operation.
rb_send(3) behaves in the same way but also accepts flags to alter buffer behavior for one single call. Possible flags are:
flag |
description |
|---|---|
rb_dontwait |
Write data normally but do not block if buffer is full. Instead return error. |
rb_writev(3) instead of accepting single buffer, takes vector of buffers instead. This is gather “output”. When rb is not dynamic, this is equivalent of just calling rb_write(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 being read from a single buffer is gathered from multiple buffers. This means only single dynamic object will be created in ring buffer from all of passed buffers. Function works in all-or-nothing way, either all buffers are written or error is returned. Write is performed atomically, it is guaranteed that after function return success, buffers will create one continuous area. Buffers are processed in array order, vec[0] will be fully written before proceeding to vec[1] and so on.
rb_sendv(3) behaves the same way, but also accepts flags to alter per call behavior.
rb_write_claim(3) doesn’t write anything to the rb buffer, but instead gives you all the information needed for you to perform the write. On successful return, buffer will be pointing at first writable byte, and you can write starting at offset 0. count will tell you how many elements you can write into 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 write mutex, so no one else will be able to write from buffer until you call rb_write_commit(3) and release mutex.
flags argument accepts only rb_dontwait flag (only in multi-thread mode).
rb_write_commit(3) must be called after rb_write_claim(3) with count being number of elements (not bytes) you wrote into the buffer. If rb is multi-thread aware, this will also wake up thread blocked in read operation.
rb_write_commit_claim(3) can be used to commit data to ring buffer as you would do with rb_write_commit(3), but also immediately claims new buffer as you would do with rb_write_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 writes to buffer while making sure no one else disturbs you in the middle.
Function takes same parameters as rb_write_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_write_commit(3). As output it defines size of returned buffer as with rb_write_claim(3).
NOTES¶
If rb has been created with both rb_multithread and rb_growable, and grow event is triggered, threads locked in rb_read(3) may return EAGAIN error until grow operation is completed.
Rationale. To grow buffer, we must have ownership of all fields, since this operation modifies variables used by both reading and writing operations. For that, growing thread must have both write and read locks. To grab the read lock without the risk of deadlocks, growing thread will start waking up blocked read threads until it can acquire read lock. Those woken threads will most likely be returning EAGAIN errors until grow operation is completed and order is restored.
RETURN VALUE¶
rb_write(3) and rb_send(3) will return number of elements actually written to the rb buffer. rb_write_commit(3) and rb_write_claim(3) return 0 on success.
rb_write(3) and rb_send(3) will return number of elements actually written to the rb buffer. rb_write_commit(3), rb_write_claim(3), rb_send_commit(3), rb_send_claim(3), rb_write_commit_claim(3) and rb_send_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 written to the buffer.
ERRORS¶
All functions may return one of these on error
- EINVAL
Any of the input parameter is invalid
- EAGAIN
Ring buffer is full and function returned without writing anything. If rb is dynamic, this will be returned when whole message cannot fit into rb.
When rb was created with rb_dynamic, these additional errors may appear
- EMSGSIZE
You tried to put too long message on buffer, and buffer cannot hold information about size. You must increase object_size during initialization.
When rb was created with rb_growable, these additional errors may appear
- ENOMEM
Error allocating more memory during growing operation. rb is still in valid state. You may want to call rb_set_hard_max_count(3) with current rb size to not get this error anymore.
When rb was created with rb_multithread, these additional errors may appear
- EAGAIN
rb_clear(3) has been called with request to zero out all ring buffer memory and no data has yet been written to rb.
- ECANCELED
Other thread called rb_stop(3) and no data has been written.
EXAMPLES¶
Note that these examples do not have error handling for simplicity.
Simple write. Assuming ring buffer holds simple integers.
int wr_buf[128];
long nwritten;
nwritten = rb_write(rb, wr_buf, sizeof(wr_buf));
Write but force non blocking operation
int wr_buf[128];
long nwritten;
nwritten = rb_send(rb, wr_buf, sizeof(wr_buf), rb_dontwait);
Claim buffer, and read data from serial line into ring buffer. Thanks to claim/commit we don’t have to create any intermediate buffer and do double copying.
long nread;
void *buffer;
size_t count, object_size;
rb_write_claim(rb, &buffer, &count, &object_size, 0);
nread = read(serial_fd, buffer, count * object_size);
/* tell rb, how many bytes we actually used, read() may return
* less than we asked it to read */
rb_write_commit(rb, nread / object_size);
Perform gather write on dynamic ring buffer
char rdbuf[32];
char a[] = "test ";
char b[] = "data ";
char c[] = "to ";
char d[] = "write\n";
struct rb_iovec iov[] = {
{ .base = a, .len = strlen(a) },
{ .base = b, .len = strlen(b) },
{ .base = c, .len = strlen(c) },
{ .base = d, .len = strlen(d) },
};
rb_writev(rb, iov, rb_array_size(iov));
rb_read(rb, rdbuf, sizeof(rdbuf));
printf("%s", rdbuf);
/* will print "test data to write" */
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)