The shared buffers which libshbuf
provides are intended to be a better IPC replacement for classic unix pipes. They offer the following advantages:
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.
(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.
For an elaborate example see the source codes for shbuftest.c
.
A shbuf client which writes data in a loop to the buffer should usually look like the following code excerpt:
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); [...]
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.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.)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.shbuf_post_select()
after a select()
on the buffer. Otherwise the next select()
-call will succeed immediately too.select()
.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.
Since version 0.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.