1760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
2760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org#include "os/os_thread.h"
3760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org#include "pipe/p_defines.h"
4760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org#include "util/u_ringbuffer.h"
5760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org#include "util/u_math.h"
6760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org#include "util/u_memory.h"
7760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
8760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org/* Generic ringbuffer:
9760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org */
10760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.orgstruct util_ringbuffer
11760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org{
12760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org   struct util_packet *buf;
13760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org   unsigned mask;
14760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
15760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org   /* Can this be done with atomic variables??
16760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org    */
17760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org   unsigned head;
18760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org   unsigned tail;
19760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org   pipe_condvar change;
20760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org   pipe_mutex mutex;
21760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org};
22760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
23760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
24760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.orgstruct util_ringbuffer *util_ringbuffer_create( unsigned dwords )
25760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org{
26760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org   struct util_ringbuffer *ring = CALLOC_STRUCT(util_ringbuffer);
27760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org   if (ring == NULL)
28760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org      return NULL;
29760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
30760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org   assert(util_is_power_of_two(dwords));
31760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
32760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org   ring->buf = MALLOC( dwords * sizeof(unsigned) );
33760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org   if (ring->buf == NULL)
34760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org      goto fail;
35760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
36760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org   ring->mask = dwords - 1;
37760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
38760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org   pipe_condvar_init(ring->change);
39760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org   pipe_mutex_init(ring->mutex);
40760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org   return ring;
41760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
42760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.orgfail:
43760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org   FREE(ring->buf);
44760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org   FREE(ring);
45760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org   return NULL;
46760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org}
47760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
48760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.orgvoid util_ringbuffer_destroy( struct util_ringbuffer *ring )
49760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org{
50760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org   pipe_condvar_destroy(ring->change);
51760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org   pipe_mutex_destroy(ring->mutex);
52760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org   FREE(ring->buf);
53760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org   FREE(ring);
54760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org}
55760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
56760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org/**
57760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org * Return number of free entries in the ring
58760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org */
59760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.orgstatic INLINE unsigned util_ringbuffer_space( const struct util_ringbuffer *ring )
60760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org{
61760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org   return (ring->tail - (ring->head + 1)) & ring->mask;
62760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org}
63760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
64760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org/**
65760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org * Is the ring buffer empty?
66760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org */
67760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.orgstatic INLINE boolean util_ringbuffer_empty( const struct util_ringbuffer *ring )
68760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org{
69760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org   return util_ringbuffer_space(ring) == ring->mask;
70760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org}
71760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
72760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.orgvoid util_ringbuffer_enqueue( struct util_ringbuffer *ring,
73760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                              const struct util_packet *packet )
74760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org{
75760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org   unsigned i;
76760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
77760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org   /* XXX: over-reliance on mutexes, etc:
78760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org    */
79760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org   pipe_mutex_lock(ring->mutex);
80760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
81760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org   /* make sure we don't request an impossible amount of space
82760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org    */
83760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org   assert(packet->dwords <= ring->mask);
84760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
85760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org   /* Wait for free space:
86760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org    */
87760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org   while (util_ringbuffer_space(ring) < packet->dwords)
88760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org      pipe_condvar_wait(ring->change, ring->mutex);
89760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
90760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org   /* Copy data to ring:
91760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org    */
92760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org   for (i = 0; i < packet->dwords; i++) {
93760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
94760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org      /* Copy all dwords of the packet.  Note we're abusing the
95760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org       * typesystem a little - we're being passed a pointer to
96760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org       * something, but probably not an array of packet structs:
97760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org       */
98760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org      ring->buf[ring->head] = packet[i];
99760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org      ring->head++;
100760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org      ring->head &= ring->mask;
101760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org   }
102760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
103760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org   /* Signal change:
104760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org    */
105760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org   pipe_condvar_signal(ring->change);
106760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org   pipe_mutex_unlock(ring->mutex);
107760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org}
108760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
109760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.orgenum pipe_error util_ringbuffer_dequeue( struct util_ringbuffer *ring,
110760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                                         struct util_packet *packet,
111760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                                         unsigned max_dwords,
112760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org                                         boolean wait )
113760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org{
114760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org   const struct util_packet *ring_packet;
115760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org   unsigned i;
116760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org   int ret = PIPE_OK;
117760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
118760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org   /* XXX: over-reliance on mutexes, etc:
119760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org    */
120760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org   pipe_mutex_lock(ring->mutex);
121760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
122760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org   /* Get next ring entry:
123760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org    */
124760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org   if (wait) {
125760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org      while (util_ringbuffer_empty(ring))
126760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org         pipe_condvar_wait(ring->change, ring->mutex);
127760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org   }
128760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org   else {
129760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org      if (util_ringbuffer_empty(ring)) {
130760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org         ret = PIPE_ERROR_OUT_OF_MEMORY;
131760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org         goto out;
132760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org      }
133760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org   }
134760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
135760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org   ring_packet = &ring->buf[ring->tail];
136760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
137760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org   /* Both of these are considered bugs.  Raise an assert on debug builds.
138760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org    */
139760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org   if (ring_packet->dwords > ring->mask + 1 - util_ringbuffer_space(ring) ||
140760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org       ring_packet->dwords > max_dwords) {
141760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org      assert(0);
142760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org      ret = PIPE_ERROR_BAD_INPUT;
143760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org      goto out;
144760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org   }
145760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
146760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org   /* Copy data from ring:
147760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org    */
148760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org   for (i = 0; i < ring_packet->dwords; i++) {
149760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org      packet[i] = ring->buf[ring->tail];
150760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org      ring->tail++;
151760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org      ring->tail &= ring->mask;
152760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org   }
153760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org
154760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.orgout:
155760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org   /* Signal change:
156760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org    */
157760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org   pipe_condvar_signal(ring->change);
158760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org   pipe_mutex_unlock(ring->mutex);
159760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org   return ret;
160760fd893ba809a7a5daa25c2749ff502f7186e83kbr@chromium.org}
161