#include "os/os_thread.h" #include "pipe/p_defines.h" #include "util/u_ringbuffer.h" #include "util/u_math.h" #include "util/u_memory.h" /* Generic ringbuffer: */ struct util_ringbuffer { struct util_packet *buf; unsigned mask; /* Can this be done with atomic variables?? */ unsigned head; unsigned tail; pipe_condvar change; mtx_t mutex; }; struct util_ringbuffer *util_ringbuffer_create( unsigned dwords ) { struct util_ringbuffer *ring = CALLOC_STRUCT(util_ringbuffer); if (!ring) return NULL; assert(util_is_power_of_two(dwords)); ring->buf = MALLOC( dwords * sizeof(unsigned) ); if (ring->buf == NULL) goto fail; ring->mask = dwords - 1; cnd_init(&ring->change); (void) mtx_init(&ring->mutex, mtx_plain); return ring; fail: FREE(ring->buf); FREE(ring); return NULL; } void util_ringbuffer_destroy( struct util_ringbuffer *ring ) { cnd_destroy(&ring->change); mtx_destroy(&ring->mutex); FREE(ring->buf); FREE(ring); } /** * Return number of free entries in the ring */ static inline unsigned util_ringbuffer_space( const struct util_ringbuffer *ring ) { return (ring->tail - (ring->head + 1)) & ring->mask; } /** * Is the ring buffer empty? */ static inline boolean util_ringbuffer_empty( const struct util_ringbuffer *ring ) { return util_ringbuffer_space(ring) == ring->mask; } void util_ringbuffer_enqueue( struct util_ringbuffer *ring, const struct util_packet *packet ) { unsigned i; /* XXX: over-reliance on mutexes, etc: */ mtx_lock(&ring->mutex); /* make sure we don't request an impossible amount of space */ assert(packet->dwords <= ring->mask); /* Wait for free space: */ while (util_ringbuffer_space(ring) < packet->dwords) cnd_wait(&ring->change, &ring->mutex); /* Copy data to ring: */ for (i = 0; i < packet->dwords; i++) { /* Copy all dwords of the packet. Note we're abusing the * typesystem a little - we're being passed a pointer to * something, but probably not an array of packet structs: */ ring->buf[ring->head] = packet[i]; ring->head++; ring->head &= ring->mask; } /* Signal change: */ cnd_signal(&ring->change); pipe_mutex_unlock(ring->mutex); } enum pipe_error util_ringbuffer_dequeue( struct util_ringbuffer *ring, struct util_packet *packet, unsigned max_dwords, boolean wait ) { const struct util_packet *ring_packet; unsigned i; int ret = PIPE_OK; /* XXX: over-reliance on mutexes, etc: */ mtx_lock(&ring->mutex); /* Get next ring entry: */ if (wait) { while (util_ringbuffer_empty(ring)) cnd_wait(&ring->change, &ring->mutex); } else { if (util_ringbuffer_empty(ring)) { ret = PIPE_ERROR_OUT_OF_MEMORY; goto out; } } ring_packet = &ring->buf[ring->tail]; /* Both of these are considered bugs. Raise an assert on debug builds. */ if (ring_packet->dwords > ring->mask + 1 - util_ringbuffer_space(ring) || ring_packet->dwords > max_dwords) { assert(0); ret = PIPE_ERROR_BAD_INPUT; goto out; } /* Copy data from ring: */ for (i = 0; i < ring_packet->dwords; i++) { packet[i] = ring->buf[ring->tail]; ring->tail++; ring->tail &= ring->mask; } out: /* Signal change: */ cnd_signal(&ring->change); pipe_mutex_unlock(ring->mutex); return ret; }