/* * Copyright (C) 2010-2020 Red Hat, Inc. * * Author: Angus Salkeld * * This file is part of libqb. * * libqb is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 2.1 of the License, or * (at your option) any later version. * * libqb is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with libqb. If not, see . */ #ifndef QB_RB_H_DEFINED #define QB_RB_H_DEFINED /* *INDENT-OFF* */ #ifdef __cplusplus extern "C" { #endif /* *INDENT-ON* */ #include #include /** * @file qbrb.h * This implements a ring buffer that works in "chunks", not bytes. * So you write/read a complete chunk or not at all. * There are two types of ring buffer: normal and overwrite. * Overwrite will reclaim the oldest chunks inorder to make way for new ones, * the normal version will refuse to write a new chunk if the ring buffer * is full. * * This implementation is capable of working across processes, but one process * must only write and the other process read. * * The read process will do the following: * @code * rb = qb_rb_open("test2", 2000, QB_RB_FLAG_SHARED_PROCESS|QB_RB_FLAG_CREATE); * for (i = 0; i < 200; i++) { * try_read_again: * l = qb_rb_chunk_read(rb, (void *)out, 32, 1000); * if (l < 0) { * goto try_read_again; * } * } * ... * qb_rb_close(rb); * * @endcode * * The write process will do the following: * @code * rb = qb_rb_open("test2", 2000, QB_RB_FLAG_SHARED_PROCESS); * for (i = 0; i < 200; i++) { * try_write_again: * l = qb_rb_chunk_write(rb, &v, sizeof(v)); * if (l < sizeof(v)) { * goto try_write_again; * } * } * ... * qb_rb_close(rb); * @endcode * * @author Angus Salkeld */ /** * Create a ring buffer (rather than open and existing one). * @see qb_rb_open() */ #define QB_RB_FLAG_CREATE 0x01 /** * New calls to qb_rb_chunk_write() will call qb_rb_chunk_reclaim() * if there is not enough space. * If this is not set then new writes will be refused. * @see qb_rb_open() */ #define QB_RB_FLAG_OVERWRITE 0x02 /** * The ringbuffer will be shared between pthreads not processes. * This effects the type of locks/semaphores that are used. * @see qb_rb_open() */ #define QB_RB_FLAG_SHARED_THREAD 0x04 /** * The ringbuffer will be shared between processes. * This effects the type of locks/semaphores that are used. * @see qb_rb_open() */ #define QB_RB_FLAG_SHARED_PROCESS 0x08 /** * Don't use semaphores, only atomic ops. * This mean that the timeout passed into qb_rb_chunk_read() * will be ignored. */ #define QB_RB_FLAG_NO_SEMAPHORE 0x10 struct qb_ringbuffer_s; typedef struct qb_ringbuffer_s qb_ringbuffer_t; /** * Create the ring buffer with the given type. * * This creates allocates a ring buffer in shared memory. * * @param name the unique name of this ringbuffer. * @param size the requested size. * @param flags or'ed flags * @param shared_user_data_size size for a shared data area. * @note the actual size will be rounded up to the next page size. * @return a new ring buffer or NULL if there was a problem. * @see QB_RB_FLAG_CREATE, QB_RB_FLAG_OVERWRITE, QB_RB_FLAG_SHARED_THREAD, QB_RB_FLAG_SHARED_PROCESS */ qb_ringbuffer_t *qb_rb_open(const char *name, size_t size, uint32_t flags, size_t shared_user_data_size); /** * Dereference the ringbuffer and, if we are the last user, destroy it. * * All files, mmaped memory, semaphores and locks will be destroyed. * * @param rb ringbuffer instance */ void qb_rb_close(qb_ringbuffer_t * rb); /** * Get the name of the ringbuffer. * @param rb ringbuffer instance * @return name. */ char *qb_rb_name_get(qb_ringbuffer_t * rb); /** * Get a point to user shared data area. * * @note this is of size "shared_user_data_size" passed into qb_rb_open() * * @param rb ringbuffer instance * @return pointer to shared data. */ void *qb_rb_shared_user_data_get(qb_ringbuffer_t * rb); /** * Write a chunk to the ring buffer. * * This simply calls qb_rb_chunk_alloc() and then * qb_rb_chunk_commit(). * * @param rb ringbuffer instance * @param data (in) the data to write * @param len (in) the size of the chunk. * @return the amount of bytes actually buffered (either len or -1). * * @see qb_rb_chunk_alloc() * @see qb_rb_chunk_commit() */ ssize_t qb_rb_chunk_write(qb_ringbuffer_t * rb, const void *data, size_t len); /** * Allocate space for a chunk of the given size. * * If type == QB_RB_FLAG_OVERWRITE and NULL is returned, memory corruption of * the memory file has occurred. The ringbuffer should be destroyed. * If type == QB_RB_NORMAL then when there is not enough space it will * return NULL. * * @param rb ringbuffer instance * @param len (in) the size to allocate. * @return pointer to chunk to write to, or NULL (if no space). * * @see qb_rb_chunk_alloc() */ void *qb_rb_chunk_alloc(qb_ringbuffer_t * rb, size_t len); /** * Finalize the chunk. * @param rb ringbuffer instance * @param len (in) the size of the chunk. */ int32_t qb_rb_chunk_commit(qb_ringbuffer_t * rb, size_t len); /** * Read (without reclaiming) the last chunk. * * This function is a way of accessing the next chunk without a memcpy(). * You can read the chunk data in place. * * @note This function will not "pop" the chunk, you will need to call * qb_rb_chunk_reclaim(). * @param rb ringbuffer instance * @param data_out (out) a pointer to the next chunk to read (not copied). * @param ms_timeout (in) time to wait for new data. * * @return the size of the chunk (0 if buffer empty). */ ssize_t qb_rb_chunk_peek(qb_ringbuffer_t * rb, void **data_out, int32_t ms_timeout); /** * Reclaim the oldest chunk. * You will need to call this if using qb_rb_chunk_peek(). * @param rb ringbuffer instance */ void qb_rb_chunk_reclaim(qb_ringbuffer_t * rb); /** * Read the oldest chunk into data_out. * * This is the same as qb_rb_chunk_peek() memcpy() and qb_rb_chunk_reclaim(). * * @param rb ringbuffer instance * @param data_out (in/out) the chunk will be memcpy'ed into this. * @param len (in) the size of data_out. * @param ms_timeout the amount od time to wait for new data. * @return the size of the chunk, or error. */ ssize_t qb_rb_chunk_read(qb_ringbuffer_t * rb, void *data_out, size_t len, int32_t ms_timeout); /** * Get the reference count. * * @param rb ringbuffer instance * @return the number of references */ int32_t qb_rb_refcount_get(qb_ringbuffer_t * rb); /** * The amount of free space in the ring buffer. * * @note Some of this space will be consumed by the chunk headers. * @param rb ringbuffer instance */ ssize_t qb_rb_space_free(qb_ringbuffer_t * rb); /** * The total amount of data in the buffer. * * @note This includes the chunk headers (8 bytes per chunk). * @param rb ringbuffer instance */ ssize_t qb_rb_space_used(qb_ringbuffer_t * rb); /** * The total number of chunks in the buffer. * * @param rb ringbuffer instance */ ssize_t qb_rb_chunks_used(qb_ringbuffer_t * rb); /** * Write the contents of the Ring Buffer to file. * @param fd open file to write the ringbuffer data to. * @param rb ringbuffer instance * @see qb_rb_create_from_file() */ ssize_t qb_rb_write_to_file(qb_ringbuffer_t * rb, int32_t fd); /** * Load the saved ring buffer from file into temporary memory. * @param fd file with saved ringbuffer data. * @param flags same flags as passed into qb_rb_open() * @return new ringbuffer instance * @see qb_rb_write_to_file() */ qb_ringbuffer_t *qb_rb_create_from_file(int32_t fd, uint32_t flags); /** * Like 'chown', it changes the owner and group of the ringbuffer's * resources. * @param owner uid of the owner to change to * @param group gid of the group to change to * @param rb ringbuffer instance * @return status (0 = ok, -errno for error) */ int32_t qb_rb_chown(qb_ringbuffer_t * rb, uid_t owner, gid_t group); /** * Like 'chmod', it changes the mode of the ringbuffer's resources. * @param mode mode to change to * @param rb ringbuffer instance * @retval 0 == ok * @retval -errno for error */ int32_t qb_rb_chmod(qb_ringbuffer_t * rb, mode_t mode); /* *INDENT-OFF* */ #ifdef __cplusplus } #endif /* __cplusplus */ /* *INDENT-ON* */ #endif /* QB_RB_H_DEFINED */