#ifndef fooshbufhfoo #define fooshbufhfoo /* $Id: shbuf.h,v 1.4 2002/05/02 17:02:21 poettering Exp $ * * This file is part of libshbuf. * * asd is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your * option) any later version. * * asd 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 General Public License * for more details. * * You should have received a copy of the GNU General Public License * along with libshbuf; if not, write to the Free Software Foundation, * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ #include /** * \mainpage Shared Buffer Library libshbuf * * \section intro Introduction * * The shared buffers which libshbuf provides are intended to * be a better IPC replacement for classic unix pipes. They offer the * following advantages: * * * * \section overview Technical overview * * The library makes use of SYSV shared memory, semaphores an message * queues. Additionaly it spawns a background thread using POSIX * pthreads, alltough this is not shown in any way in the API. * * Two shared memory blocks are created. One for control, which * contains some information about the fill levels of the buffer, and * one for the buffer itself. A semaphore is used for properly locking * acces to the control block. A message queue is used for notifying * changes to the other side of the buffer. * * The library is not thread safe, but this should be easy to fix as * no API changes are needed for accomplishing this. * * \section usage Usage * * (This part is a bit terse, I know. Just the most important things to know here.) * * It is OK to manipulate the status block of the shared buffer * directly. You can use shbuf_get_status() for getting a * pointer to it. You are urged to lock access to the status block * with shbuf_status_lock() and * shbuf_status_unlock() when accessing it, for both * writing and reading, or you will cause corruption. * * Please note: Only the status block may be locked, not the buffer * itself. This is a feature, not a bug. Locks should be kept for a * minimal period of time. Thus only the status fields are secured * with a mutual exclusion as their manipulation should take a very * short timespan. * * The functions shbuf_reset(), * shbuf_zero(), shbuf_get_read_pointer(), * shbuf_inc_read_pointer(), * shbuf_get_write_pointer(), * shbuf_inc_write_pointer(), * shbuf_get_read_pointer(), * shbuf_is_full(), shbuf_is_empty(), * shbuf_read() are shbuf_write()() more or * less simple shortcuts for some of the most often used accesses to * the status block. Most of the time they should suffice for * accessing the shared buffer. * * \section example Code Example * * For an elaborate example see the source codes for \link * shbuftest.c shbuftest.c \endlink. * * A shbuf client which writes data in a loop to the buffer should * usually look like the following code excerpt: * * \code * shbuf *sb; * * [...] * * // Open the shared buffer (some error checking for sb != 0 should follow.) * sb = shbuf_open(4711); * * // Enable notifying mode (error checking...) * shbuf_notify_enable(sb, 1); * * [...] * * // Do as long as there is still a server connected... * while (shbuf_connected(sb) == 1) { * unsigned char *p; * unsigned long l; * * // Try to get a pointer to some available data * if (!(p = shbuf_get_write_pointer(sb, &l))) { * * // No data available, we wait for a notification from * // the provider. A select() on shbuf_get_select_fd(sb) * // does nearly the same, but may listen on multiple * // file descriptors. * * if (shbuf_wait(sb) < 0) * break; * * continue; * } * * // Error check * if (p == (unsigned char*) -1) * break; * * // Now do some work on the l bytes referenced by p * [...] * * // Tell the shared buffer, that we handled r bytes, which may be * // removed from the buffer. * if (shbuf_inc_write_pointer(sb, r) < 0) * break; * * // Notify the other side about th changes * if (shbuf_notify(sb) < 0) * break; * } * * // Cleanup * shbuf_free(sb); * * [...] * \endcode * * \section notes Assorted Notes * * \li There is no need to handle all bytes * shbuf_get_write_pointer() tells us to be available at * once. You may manipulate the shared buffer between * shbuf_get_write_pointer() and * shbuf_inc_write_pointer() as you wish, but it is bad * style to do this to memory outside of the area * shbuf_get_write_pointer() told you. * * \li shbuf_get_write_pointer() does not modify the shared * buffer in any way, so you may repeat calls to this function even * when a call already succeeded. (This might be useful, for example, * when you want to wait for at least 1024 available bytes in the * buffer.) * * \li A select() of * shbuf_get_select_fd(sb) does not succeed mandatorily * when data is available in the shared buffer. The call will succeed * only when the other side notified us about changes since the last * call to shbuf_post_select() or * shbuf_wait(). In fact the notification system has no * connection to the fill level of the shared buffer. Thus you should * always check the fill levels first and only when this results in * no data being available, call select() or * shbuf_wait() to wait for new data. * * \li Always call shbuf_post_select() after a * select() on the buffer. Otherwise the next * select()-call will succeed immediately too. * * \li Regardless of being client or provider of a shared * buffer or being "writer" or "reader" of the buffer the * notifications from the other side may be checked with the * READ-argument (argument #2) of select(). * * \section sysindep System Independency * * At the moment there is only a Linux tested version of this library * available. It should be easy to port this library to any other * system supporting SysV IPC and POSIX Pthreads. It should be even * portable to operating systems with a completely different IPC or * threading API: the relevant function calls in the library aren't * visible through the API, so it should be easy to replace them * through functionally equivalent system specific ones. Thus even MS * Windows might be a target OS for this library. * * \section backlog Backlog * * Since version 0.2 libshbuf features a facility I dubbed * "backlog". It is sometimes needed for keeping some already read * data in the buffer for being able to rewind to a specific position * later. You may set the desired backlog size in bytes with * shbuf_set_backlog_target(). The desired backlog size * defaults to zero. The current backlog size may be queried with * shbuf_rewind(). It may be smaller or larger than the * desired target. You may call shbuf_rewind() for making * use of the backlog. */ /** struct of a shbuf object */ struct _shbuf; /** typedef of a shbuf object */ typedef struct _shbuf shbuf; /** * Structure encapsulating the status of a shared buffer. It contains * some indexes which specify the current fill level of the buffer. * * Typical buffer layout: * *
 *                   <---backlog--> <---length---->
 * +----------------+--------------+---------------+------------------+
 * |                |              |               |                  |
 * |                |   BACKLOG    |   DATA        |                  |
 * |                |              |               |                  |
 * +----------------+--------------+---------------+------------------+
 * ^                ^              ^               ^                  ^
 * 0                |              read_idx        |               size
 *                  read_idx-backlog               |
 *                                                 read_idx+length
 * 
* * Note: In reality it is not so simple as it looks in this graphic, * since you always need to think about buffer wrap arounds. * */ typedef struct { unsigned long read_idx; /**< Where to read the next bytes from */ unsigned long length; /**< Amount of bytes currently in the buffer */ unsigned long backlog; /**< Length of current backlog in bytes */ unsigned long backlog_target; /**< Try to keep so many bytes as backlog */ unsigned long write_count; /**< Simple counter, incremented on every write */ unsigned long read_count; /**< Simple counter, incremented on every read */ int ignore_read_inc; /**< Boolean; when this flag is set, the next * call to shbuf_inc_read_pointer() * will be ignored and this flag will * be disabled again. */ int ignore_write_inc; /**< Corresponding here */ } shbuf_status; /** * Creates a new shared buffer and returns a shbuf object * encapsulating it. The calling process becomes the provider of the * buffer. * * @param key Desired key, 0 for automatic selection * @param size The length of the shbuf in byte * @return Pointer to new shbuf object, or NULL on failure */ shbuf* shbuf_create(key_t key, unsigned long size); /** * Opens an existing shared buffer created by a call to * shbuf_create() and returns a shbuf object * encapsulating it. The calling process becomes the client of the * shbuf. * * @param key The key of the shbuf * @return Pointer to the new shbug object, or NULL on failure * @see shbuf_create */ shbuf* shbuf_open(key_t key); /** * Frees an opened shared buffer and removes it, when the calling * process is the provider. The shbuf object is freed. * * @param sb Pointer to the shbuf object to be removed */ void shbuf_free(shbuf* sb); /** * Sets the Unix access mode of the shared buffer. It defaults to 0700 * on creation. * * @param sb Pointer to the shbuf object to be changed * @param mode the Unix access mode * @return 0 on success, -1 on failure */ int shbuf_access(shbuf* sb, int mode); /** * Returns the shared memory key of the shbuf object. * * @param sb Pointer to the shbuf object * @return The shared memory key */ key_t shbuf_get_key(shbuf* sb); /** * Returns the size of the shared buffer in bytes. * * @param sb Pointer to the shbuf object * @return The size in bytes */ unsigned long shbuf_get_size(shbuf* sb); /** * Returns a pointer to the shared buffer. It is valid until the shbuf * object is freed. * * @param sb Pointer to the shbuf object * @return A pointer to the shared buffer */ unsigned char* shbuf_get_pointer(shbuf* sb); /** * Returns a pointer to the shbuf_status object attached * to the shared buffer. It is valid until the shbuf object is freed. * * @param sb Pointer to the shbuf object * @return A pointer to the shbuf_status structure */ shbuf_status* shbuf_get_status(shbuf* sb); /** * Enables or disabled notifying mode. When this is enabled, the * process may wait for changes in the buffer with standard * select() calls. * * @param sb Pointer to the shbuf object * @param b Notifying mode is enabled when nonzero, it is disabled * when zero * @return 0 on success, -1 on failure */ int shbuf_notify_enable(shbuf* sb, int b); /** * Notifies the other side of the shared buffer. A call to this * function will be ignored when the other side hasn't enabled * notifying mode. * * @param sb Pointer to the shbuf object * @return 0 on success, -1 on failure */ int shbuf_notify(shbuf* sb); /** * Waits for a notification from the other side of the shared * buffer. This call doesn't do anything more than calling * select() with the correct parameters. * * @param sb Pointer to the shbuf object * @return 0 on success, -1 on failure */ int shbuf_wait(shbuf* sb); /** * Returns the file descriptor the program may select() * on for waiting for changes in the buffer. * * @param sb Pointer to the shbuf object * @return The file descriptor */ int shbuf_get_select_fd(shbuf* sb); /** * Cleans up a notifying shbuf. Should be called immediately after a * select() call for. Otherwise the following call to * select() for the file descriptor will succeed * immediately. * * @param sb Pointer to the shbuf object * @return 0 on success, -1 on failure */ int shbuf_post_select(shbuf *sb); /** * Lock the shbuf_status structure attached to the shbuf * object. This should be done before accessing any of the member * fields. This may block when another process has locked the shared * buffer. These locks are not recursive! * * @param sb Pointer to the shbuf object to be locked * @return 0 on success, -1 on failure */ int shbuf_status_lock(shbuf* sb); /** * Unlock the shbuf_status structure attached to the shbuf * object. This should be done after accessing any of the member * fields. * * @param sb Pointer to the shbuf object to be unlocked * @return 0 on success, -1 on failure */ int shbuf_status_unlock(shbuf* sb); /** * Reset the status structure of the shared buffer. This may be used * for emptying the buffer at once. * * @param sb Pointer to the shbuf object * @return 0 on success, -1 on failure */ int shbuf_reset(shbuf* sb); /** * Similar to shbuf_reset(), but zeroes the shared * buffered completely. * * @param sb Pointer to the shbuf object * @return 0 on success, -1 on failure */ int shbuf_zero(shbuf* sb); /** * Sets the desired backlog size in bytes. * * @param sb Pointer to th shbuf object * @param bl The desired backlog size, clipped when too large * @return 0 on success, -1 on failure */ int shbuf_set_backlog_target(shbuf *sb, unsigned long bl); /** * Returns the current desired backlog size in bytes. This defaults to * 0 on newly created shbuf objects. * * @param sb Pointer to th shbuf object * @return (unsigned long) -1 on error, backlog target size in bytes * on success */ unsigned long shbuf_get_backlog_target(shbuf *sb); /** * Returns a pointer to the next data which may be read from the * buffer. * * @param sb Pointer to the shbuf object * @param l Pointer to an unsigned long, where the amount * of bytes to be read will be stored. May be NULL. When no data may * be read this will be filled be zero. * @return Pointer to the next bytes to be read, NULL when none * are availabled, (unsigned char*) -1 on error. */ unsigned char* shbuf_get_read_pointer(shbuf *sb, unsigned long *l); /** * Increments the read index of the shared buffer by a given amount of * bytes. * * @param sb Pointer to the shbuf object * @param r Amount of bytes, the index shall be incremented by. * @return 0 on success, -1 on failure */ int shbuf_inc_read_pointer(shbuf *sb, unsigned long r); /** * Returns a pointer to the next data which may be written to the * buffer. * * @param sb Pointer to the shbuf object * @param l Pointer to an unsigned long, where the amount * of bytes to be written will be stored. May be NULL. When no data may * be written this will be filled be zero. * @return Pointer to the next bytes to be written, NULL when none * are availabled, (unsigned char*) -1 on error. */ unsigned char* shbuf_get_write_pointer(shbuf *sb, unsigned long *l); /** * Increments the write index of the shared buffer by a given amount of * bytes. * * @param sb Pointer to the shbuf object * @param r Amount of bytes, the index shall be incremented by. * @return 0 on success, -1 on failure */ int shbuf_inc_write_pointer(shbuf *sb, unsigned long r); /** * Checks whether a client as well as a provider are connected to the * the shared buffer. * * @param sb Pointer to the shbuf object * @return Nonzero when both sides are connected, otherwise zero */ int shbuf_connected(shbuf *sb); /** * Checks whether the shared buffer is full. * * @param sb Pointer to the shbuf object * @return 1 when full, -1 on error, 0 otherwise */ int shbuf_is_full(shbuf *sb); /** * Checks whether the shared buffer is empty. * * @param sb Pointer to the shbuf object * @return 1 when empty, -1 on error, 0 otherwise */ int shbuf_is_empty(shbuf *sb); /** * Tries to rewind the read index or returns the size of the current * backlog. * * @param sb Pointer to the shbuf object * @param v Specifies how many bytes the read pointer shall be * rewinded. If zero, the length of the current backlog is * returned. If (unsigned long) -1 the maximum possible rewinding is * tried. * @return The number of bytes the read pointer could be * rewinded. This may (and usually is) a smaller value than v. If v * was zero, the maxium possible rewinding is returned. (unsigned * long) -1 is returned on error. * */ unsigned long shbuf_rewind(shbuf *sb, unsigned long v); /** * A function similar to the C library's read(). Reads at * most l bytes from the shared buffer to * c. If not data is available the function * blocks. Note: Don't use this function regularly as * most of the time the better solution are seperated calls to * shbuf_get_read_pointer() and * shbuf_inc_read_pointer(): This function uses a * memcpy() for copying the data from the shared buffer * to c, which is superflous is most cases as it might be * a better idea to use the data from the shared buffer directly. * * @param sb Pointer to the shbuf object * @param c Where to read the data to * @param l How many bytes to read at most? * @return The number of read bytes or -1 on failure */ signed long shbuf_read(shbuf *sb, unsigned char*c, signed long l); /** * A function similar to the C library's write(). Writes * at most l bytes from c to the shared * buffer. If no data can be written the function * blocks. Note: Don't use this function regularly as * most of the time the better solution are seperated calls to * shbuf_get_write_pointer() and * shbuf_inc_write_pointer(): This function uses a * memcpy() for copying the data from c to * the shared buffer, which is superflous is most cases as it might be * a better idea to use the data from the shared buffer directly. * * @param sb Pointer to the shbuf object * @param c Where to write the data from * @param l How many bytes to write at most? * @return The number of written bytes or -1 on failure */ signed long shbuf_write(shbuf *sb, unsigned char*c, signed long l); /** \example shbuftest.c A simple testing and example program for libshbuf. */ #endif