Home | History | Annotate | Download | only in util
      1 
      2 #include "os/os_thread.h"
      3 #include "pipe/p_defines.h"
      4 #include "util/u_ringbuffer.h"
      5 #include "util/u_math.h"
      6 #include "util/u_memory.h"
      7 
      8 /* Generic ringbuffer:
      9  */
     10 struct util_ringbuffer
     11 {
     12    struct util_packet *buf;
     13    unsigned mask;
     14 
     15    /* Can this be done with atomic variables??
     16     */
     17    unsigned head;
     18    unsigned tail;
     19    cnd_t change;
     20    mtx_t mutex;
     21 };
     22 
     23 
     24 struct util_ringbuffer *util_ringbuffer_create( unsigned dwords )
     25 {
     26    struct util_ringbuffer *ring = CALLOC_STRUCT(util_ringbuffer);
     27    if (!ring)
     28       return NULL;
     29 
     30    assert(util_is_power_of_two(dwords));
     31 
     32    ring->buf = MALLOC( dwords * sizeof(unsigned) );
     33    if (ring->buf == NULL)
     34       goto fail;
     35 
     36    ring->mask = dwords - 1;
     37 
     38    cnd_init(&ring->change);
     39    (void) mtx_init(&ring->mutex, mtx_plain);
     40    return ring;
     41 
     42 fail:
     43    FREE(ring->buf);
     44    FREE(ring);
     45    return NULL;
     46 }
     47 
     48 void util_ringbuffer_destroy( struct util_ringbuffer *ring )
     49 {
     50    cnd_destroy(&ring->change);
     51    mtx_destroy(&ring->mutex);
     52    FREE(ring->buf);
     53    FREE(ring);
     54 }
     55 
     56 /**
     57  * Return number of free entries in the ring
     58  */
     59 static inline unsigned util_ringbuffer_space( const struct util_ringbuffer *ring )
     60 {
     61    return (ring->tail - (ring->head + 1)) & ring->mask;
     62 }
     63 
     64 /**
     65  * Is the ring buffer empty?
     66  */
     67 static inline boolean util_ringbuffer_empty( const struct util_ringbuffer *ring )
     68 {
     69    return util_ringbuffer_space(ring) == ring->mask;
     70 }
     71 
     72 void util_ringbuffer_enqueue( struct util_ringbuffer *ring,
     73                               const struct util_packet *packet )
     74 {
     75    unsigned i;
     76 
     77    /* XXX: over-reliance on mutexes, etc:
     78     */
     79    mtx_lock(&ring->mutex);
     80 
     81    /* make sure we don't request an impossible amount of space
     82     */
     83    assert(packet->dwords <= ring->mask);
     84 
     85    /* Wait for free space:
     86     */
     87    while (util_ringbuffer_space(ring) < packet->dwords)
     88       cnd_wait(&ring->change, &ring->mutex);
     89 
     90    /* Copy data to ring:
     91     */
     92    for (i = 0; i < packet->dwords; i++) {
     93 
     94       /* Copy all dwords of the packet.  Note we're abusing the
     95        * typesystem a little - we're being passed a pointer to
     96        * something, but probably not an array of packet structs:
     97        */
     98       ring->buf[ring->head] = packet[i];
     99       ring->head++;
    100       ring->head &= ring->mask;
    101    }
    102 
    103    /* Signal change:
    104     */
    105    cnd_signal(&ring->change);
    106    mtx_unlock(&ring->mutex);
    107 }
    108 
    109 enum pipe_error util_ringbuffer_dequeue( struct util_ringbuffer *ring,
    110                                          struct util_packet *packet,
    111                                          unsigned max_dwords,
    112                                          boolean wait )
    113 {
    114    const struct util_packet *ring_packet;
    115    unsigned i;
    116    int ret = PIPE_OK;
    117 
    118    /* XXX: over-reliance on mutexes, etc:
    119     */
    120    mtx_lock(&ring->mutex);
    121 
    122    /* Get next ring entry:
    123     */
    124    if (wait) {
    125       while (util_ringbuffer_empty(ring))
    126          cnd_wait(&ring->change, &ring->mutex);
    127    }
    128    else {
    129       if (util_ringbuffer_empty(ring)) {
    130          ret = PIPE_ERROR_OUT_OF_MEMORY;
    131          goto out;
    132       }
    133    }
    134 
    135    ring_packet = &ring->buf[ring->tail];
    136 
    137    /* Both of these are considered bugs.  Raise an assert on debug builds.
    138     */
    139    if (ring_packet->dwords > ring->mask + 1 - util_ringbuffer_space(ring) ||
    140        ring_packet->dwords > max_dwords) {
    141       assert(0);
    142       ret = PIPE_ERROR_BAD_INPUT;
    143       goto out;
    144    }
    145 
    146    /* Copy data from ring:
    147     */
    148    for (i = 0; i < ring_packet->dwords; i++) {
    149       packet[i] = ring->buf[ring->tail];
    150       ring->tail++;
    151       ring->tail &= ring->mask;
    152    }
    153 
    154 out:
    155    /* Signal change:
    156     */
    157    cnd_signal(&ring->change);
    158    mtx_unlock(&ring->mutex);
    159    return ret;
    160 }
    161