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 */
10struct 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   pipe_condvar change;
20   pipe_mutex mutex;
21};
22
23
24struct util_ringbuffer *util_ringbuffer_create( unsigned dwords )
25{
26   struct util_ringbuffer *ring = CALLOC_STRUCT(util_ringbuffer);
27   if (ring == NULL)
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   pipe_condvar_init(ring->change);
39   pipe_mutex_init(ring->mutex);
40   return ring;
41
42fail:
43   FREE(ring->buf);
44   FREE(ring);
45   return NULL;
46}
47
48void util_ringbuffer_destroy( struct util_ringbuffer *ring )
49{
50   pipe_condvar_destroy(ring->change);
51   pipe_mutex_destroy(ring->mutex);
52   FREE(ring->buf);
53   FREE(ring);
54}
55
56/**
57 * Return number of free entries in the ring
58 */
59static 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 */
67static INLINE boolean util_ringbuffer_empty( const struct util_ringbuffer *ring )
68{
69   return util_ringbuffer_space(ring) == ring->mask;
70}
71
72void 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   pipe_mutex_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      pipe_condvar_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   pipe_condvar_signal(ring->change);
106   pipe_mutex_unlock(ring->mutex);
107}
108
109enum 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   pipe_mutex_lock(ring->mutex);
121
122   /* Get next ring entry:
123    */
124   if (wait) {
125      while (util_ringbuffer_empty(ring))
126         pipe_condvar_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
154out:
155   /* Signal change:
156    */
157   pipe_condvar_signal(ring->change);
158   pipe_mutex_unlock(ring->mutex);
159   return ret;
160}
161