srcs/toolbox/rb.c

00001 /* 
00002  * Copyright (c) 2005-2010 by KoanLogic s.r.l.
00003  */
00004 
00005 #include <sys/types.h>
00006 #include <sys/mman.h>
00007 #include <stdlib.h>
00008 #include <string.h>
00009 #include <unistd.h>
00010 #include <u/toolbox/rb.h>
00011 #include <u/toolbox/misc.h>
00012 #include <u/toolbox/carpal.h>
00013 
00014 #if defined(HAVE_SYSCONF) && defined(_SC_PAGE_SIZE)
00015   #define u_vm_page_sz  sysconf(_SC_PAGE_SIZE)
00016 #elif defined (HAVE_SYSCONF) && defined(_SC_PAGESIZE)
00017   #define u_vm_page_sz  sysconf(_SC_PAGESIZE)
00018 #elif defined(HAVE_GETPAGESIZE)
00019   #define u_vm_page_sz  getpagesize()
00020 #else
00021   #error "don't know how to get page size.  reconfigure with --no_ringbuffer."
00022 #endif
00023 
00024 struct u_rb_s
00025 {
00026     char *base;     /* base address of the mmap'd region */
00027     size_t sz;      /* ring buffer size */
00028     size_t wr_off;  /* write offset */
00029     size_t rd_off;  /* read offset */
00030     int opts;       /* options */
00031 };
00032 
00033 static char *write_addr (u_rb_t *rb);
00034 static void write_incr (u_rb_t *rb, size_t cnt);
00035 static char *read_addr (u_rb_t *rb);
00036 static void read_incr (u_rb_t *rb, size_t cnt);
00037 static size_t round_sz (size_t sz);
00038 
00112 int u_rb_create (size_t hint_sz, int opts, u_rb_t **prb)
00113 {
00114     int fd = -1;
00115     u_rb_t *rb = NULL;
00116     char path[] = "/tmp/rb-XXXXXX";
00117 
00118     dbg_err_sif ((rb = u_zalloc(sizeof(u_rb_t))) == NULL);
00119     dbg_err_sif ((fd = mkstemp(path)) == -1);
00120     dbg_err_sif (u_remove(path));
00121  
00122     /* round the supplied size to a page multiple (mmap is quite picky
00123      * about page boundary alignement) */
00124     rb->sz = round_sz(hint_sz);
00125     rb->wr_off = 0;
00126     rb->rd_off = 0;
00127     rb->opts = opts;
00128  
00129     dbg_err_sif (ftruncate(fd, rb->sz) == -1);
00130 
00131     /* mmap 2*rb->sz bytes. this is just a commodity map that will be 
00132      * discarded by the two following "half" maps.  we use it just to let the 
00133      * system choose a suitable base address (one that we are sure it's 
00134      * a multiple of the page size) that we can safely reuse later on when
00135      * pretending to MAP_FIXED */
00136     rb->base = mmap(NULL, rb->sz << 1, PROT_NONE, 
00137             MAP_ANON | MAP_PRIVATE, -1, 0);
00138     dbg_err_sif (rb->base == MAP_FAILED);
00139 
00140     /* POSIX: "The mapping established by mmap() shall replace any previous 
00141      * mappings for those whole pages containing any part of the address space 
00142      * of the process starting at pa and continuing for len bytes."
00143      * So, next two mappings replace in-toto the first 'rb->base' map which
00144      * does not need to be explicitly munmap'd */
00145  
00146     /* first half of the mmap'd region.  use MAP_SHARED to "twin" the two
00147      * mmap'd regions: each byte stored at a given offset in the first half
00148      * will show up at the same offset in the second half and viceversa */
00149     dbg_err_sif (mmap(rb->base, rb->sz, PROT_READ | PROT_WRITE, 
00150             MAP_FIXED | MAP_SHARED, fd, 0) != rb->base);
00151   
00152     /* second half is first's twin: they are attached to the same file 
00153      * descriptor 'fd', hence their pairing is handled at the OS level */
00154     dbg_err_sif (mmap(rb->base + rb->sz, rb->sz, PROT_READ | PROT_WRITE,
00155             MAP_FIXED | MAP_SHARED, fd, 0) != rb->base + rb->sz);
00156  
00157     /* dispose the file descriptor */
00158     dbg_err_sif (close(fd) == -1);
00159 
00160     *prb = rb;
00161 
00162     return 0;
00163 err:
00164     u_rb_free(rb);
00165     U_CLOSE(fd);
00166     return -1;
00167 }
00168  
00178 void u_rb_free (u_rb_t *rb)
00179 {
00180     nop_return_if (rb == NULL, );
00181 
00182     /* "All pages containing a part of the indicated range are unmapped",
00183      * hence the following single munmap with a double length should be ok for 
00184      * both previous (contiguous) mmap's */
00185     dbg_return_sif (rb->base && (munmap(rb->base, rb->sz << 1) == -1), );
00186     u_free(rb);
00187 
00188     return;
00189 }
00190 
00201 size_t u_rb_size (u_rb_t *rb)
00202 {
00203     return rb->sz;
00204 }
00205 
00221 ssize_t u_rb_write (u_rb_t *rb, const void *b, size_t b_sz)
00222 {
00223     size_t to_be_written;
00224 
00225     dbg_return_if (rb == NULL, -1);
00226     dbg_return_if (b == NULL, -1);
00227     dbg_return_if (b_sz > u_rb_size(rb), -1);
00228 
00229     nop_goto_if (!(to_be_written = U_MIN(u_rb_avail(rb), b_sz)), end);
00230 
00231     memcpy(write_addr(rb), b, to_be_written);
00232     write_incr(rb, to_be_written);
00233 
00234     /* fall through */
00235 end:
00236     return to_be_written;
00237 }
00238 
00256 ssize_t u_rb_read (u_rb_t *rb, void *b, size_t b_sz)
00257 {
00258     size_t to_be_read;
00259 
00260     dbg_return_if (b == NULL, -1);
00261     dbg_return_if (rb == NULL, -1);
00262     dbg_return_if (b_sz > u_rb_size(rb), -1);
00263 
00264     /* if there is nothing ready to be read go out immediately */
00265     nop_goto_if (!(to_be_read = U_MIN(u_rb_ready(rb), b_sz)), end);
00266 
00267     memcpy(b, read_addr(rb), to_be_read);
00268     read_incr(rb, to_be_read);
00269 
00270     /* fall through */
00271 end:
00272     return to_be_read;
00273 }
00274 
00295 void *u_rb_fast_read (u_rb_t *rb, size_t *pb_sz)
00296 {
00297     void *data = NULL;
00298 
00299     dbg_return_if (rb == NULL, NULL);
00300     dbg_return_if (!(rb->opts & U_RB_OPT_USE_CONTIGUOUS_MEM), NULL);
00301     dbg_return_if (pb_sz == NULL, NULL);
00302     dbg_return_if (*pb_sz > u_rb_size(rb), NULL);
00303 
00304     /* if there is nothing ready to be read go out immediately */
00305     nop_goto_if (!(*pb_sz = U_MIN(u_rb_ready(rb), *pb_sz)), end);
00306 
00307     data = read_addr(rb);
00308     read_incr(rb, *pb_sz);
00309 
00310     /* fall through */
00311 end:
00312     return data;
00313 }
00314 
00325 int u_rb_clear (u_rb_t *rb)
00326 {
00327     dbg_return_if (rb == NULL, -1);
00328     rb->wr_off = rb->rd_off = 0;
00329     return 0;
00330 }
00331  
00342 size_t u_rb_ready (u_rb_t *rb)
00343 {
00344     return rb->wr_off - rb->rd_off;
00345 }
00346  
00358 size_t u_rb_avail (u_rb_t *rb)
00359 {
00360     return rb->sz - u_rb_ready(rb);
00361 }
00362 
00367 /* round requested size to a multiple of PAGESIZE */
00368 static size_t round_sz (size_t sz)
00369 {
00370     size_t pg_sz = (size_t) u_vm_page_sz;
00371 
00372     return !sz ? pg_sz : (((sz - 1) / pg_sz) + 1) * pg_sz;
00373 }
00374 
00375 /* address for write op */
00376 static char *write_addr (u_rb_t *rb)
00377 {
00378     return rb->base + rb->wr_off;
00379 }
00380  
00381 /* shift the write pointer */
00382 static void write_incr (u_rb_t *rb, size_t cnt)
00383 {
00384     rb->wr_off += cnt;
00385 }
00386  
00387 /* address for next read op */
00388 static char *read_addr (u_rb_t *rb)
00389 {
00390     return rb->base + rb->rd_off;
00391 }
00392  
00393 /* shift the read pointer of 'cnt' positions */
00394 static void read_incr (u_rb_t *rb, size_t cnt)
00395 {
00396     /* 
00397      * assume 'rb' and 'cnt' sanitized 
00398      */
00399     rb->rd_off += cnt;
00400 
00401     /* When the read offset is advanced into the second virtual-memory region, 
00402      * both offsets - read and write - are decremented by the length of the 
00403      * underlying buffer */
00404     if (rb->rd_off >= rb->sz)
00405     {
00406         rb->rd_off -= rb->sz;
00407         rb->wr_off -= rb->sz;
00408     }
00409 
00410     return;
00411 }

←Products
© 2005-2010 - KoanLogic S.r.l. - All rights reserved