Imported Upstream version 3.0.4
[debian/gnuradio] / usrp / host / lib / circular_buffer.h
1 /* -*- c++ -*- */
2 /*
3  * Copyright 2006 Free Software Foundation, Inc.
4  * 
5  * This file is part of GNU Radio.
6  *
7  * GNU Radio is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3, or (at your option)
10  * any later version.
11  * 
12  * GNU Radio is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with GNU Radio; see the file COPYING.  If not, write to
19  * the Free Software Foundation, Inc., 51 Franklin Street,
20  * Boston, MA 02110-1301, USA.
21  */
22
23 #ifndef _CIRCULAR_BUFFER_H_
24 #define _CIRCULAR_BUFFER_H_
25
26 #include "mld_threads.h"
27 #include <stdexcept>
28
29 #define DO_DEBUG 0
30
31 template <class T> class circular_buffer
32 {
33 private:
34 // the buffer to use
35   T* d_buffer;
36
37 // the following are in Items (type T)
38   UInt32 d_bufLen_I, d_readNdx_I, d_writeNdx_I;
39   UInt32 d_n_avail_write_I, d_n_avail_read_I;
40
41 // stuff to control access to class internals
42   mld_mutex_ptr d_internal;
43   mld_condition_ptr d_readBlock, d_writeBlock;
44
45 // booleans to decide how to control reading, writing, and aborting
46   bool d_doWriteBlock, d_doFullRead, d_doAbort;
47
48   void delete_mutex_cond () {
49     if (d_internal) {
50       delete d_internal;
51       d_internal = NULL;
52     }
53     if (d_readBlock) {
54       delete d_readBlock;
55       d_readBlock = NULL;
56     }
57     if (d_writeBlock) {
58       delete d_writeBlock;
59       d_writeBlock = NULL;
60     }
61   };
62
63 public:
64   circular_buffer (UInt32 bufLen_I,
65                    bool doWriteBlock = true, bool doFullRead = false) {
66     if (bufLen_I == 0)
67       throw std::runtime_error ("circular_buffer(): "
68                                 "Number of items to buffer must be > 0.\n");
69     d_bufLen_I = bufLen_I;
70     d_buffer = (T*) new T[d_bufLen_I];
71     d_doWriteBlock = doWriteBlock;
72     d_doFullRead = doFullRead;
73     d_internal = NULL;
74     d_readBlock = d_writeBlock = NULL;
75     reset ();
76 #if DO_DEBUG
77     fprintf (stderr, "c_b(): buf len (items) = %ld, "
78              "doWriteBlock = %s, doFullRead = %s\n", d_bufLen_I,
79              (d_doWriteBlock ? "true" : "false"),
80              (d_doFullRead ? "true" : "false"));
81 #endif
82   };
83
84   ~circular_buffer () {
85     delete_mutex_cond ();
86     delete [] d_buffer;
87   };
88
89   inline UInt32 n_avail_write_items () {
90     d_internal->lock ();
91     UInt32 retVal = d_n_avail_write_I;
92     d_internal->unlock ();
93     return (retVal);
94   };
95
96   inline UInt32 n_avail_read_items () {
97     d_internal->lock ();
98     UInt32 retVal = d_n_avail_read_I;
99     d_internal->unlock ();
100     return (retVal);
101   };
102
103   inline UInt32 buffer_length_items () {return (d_bufLen_I);};
104   inline bool do_write_block () {return (d_doWriteBlock);};
105   inline bool do_full_read () {return (d_doFullRead);};
106
107   void reset () {
108     d_doAbort = false;
109     bzero (d_buffer, d_bufLen_I * sizeof (T));
110     d_readNdx_I = d_writeNdx_I = d_n_avail_read_I = 0;
111     d_n_avail_write_I = d_bufLen_I;
112     delete_mutex_cond ();
113     d_internal = new mld_mutex ();
114     d_readBlock = new mld_condition ();
115     d_writeBlock = new mld_condition ();
116   };
117
118 /*
119  * enqueue: add the given buffer of item-length to the queue,
120  *     first-in-first-out (FIFO).
121  *
122  * inputs:
123  *     buf: a pointer to the buffer holding the data
124  *
125  *     bufLen_I: the buffer length in items (of the instantiated type)
126  *
127  * returns:
128  *    -1: on overflow (write is not blocking, and data is being
129  *                     written faster than it is being read)
130  *     0: if nothing to do (0 length buffer)
131  *     1: if success
132  *     2: in the process of aborting, do doing nothing
133  *
134  * will throw runtime errors if inputs are improper:
135  *     buffer pointer is NULL
136  *     buffer length is larger than the instantiated buffer length
137  */
138
139   int enqueue (T* buf, UInt32 bufLen_I) {
140 #if DO_DEBUG
141     fprintf (stderr, "enqueue: buf = %X, bufLen = %ld.\n",
142              (unsigned int)buf, bufLen_I);
143 #endif
144     if (bufLen_I > d_bufLen_I) {
145       fprintf (stderr, "cannot add buffer longer (%ld"
146                ") than instantiated length (%ld"
147                ").\n", bufLen_I, d_bufLen_I);
148       throw std::runtime_error ("circular_buffer::enqueue()");
149     }
150
151     if (bufLen_I == 0)
152       return (0);
153     if (!buf)
154       throw std::runtime_error ("circular_buffer::enqueue(): "
155                                 "input buffer is NULL.\n");
156     d_internal->lock ();
157     if (d_doAbort) {
158       d_internal->unlock ();
159       return (2);
160     }
161     if (bufLen_I > d_n_avail_write_I) {
162       if (d_doWriteBlock) {
163         while (bufLen_I > d_n_avail_write_I) {
164 #if DO_DEBUG
165           fprintf (stderr, "enqueue: #len > #a, waiting.\n");
166 #endif
167           d_internal->unlock ();
168           d_writeBlock->wait ();
169           d_internal->lock ();
170           if (d_doAbort) {
171             d_internal->unlock ();
172 #if DO_DEBUG
173             fprintf (stderr, "enqueue: #len > #a, aborting.\n");
174 #endif
175             return (2);
176           }
177 #if DO_DEBUG
178           fprintf (stderr, "enqueue: #len > #a, done waiting.\n");
179 #endif
180         }
181       } else {
182         d_n_avail_read_I = d_bufLen_I - bufLen_I;
183         d_n_avail_write_I = bufLen_I;
184 #if DO_DEBUG
185         fprintf (stderr, "circular_buffer::enqueue: overflow\n");
186 #endif
187         return (-1);
188       }
189     }
190     UInt32 n_now_I = d_bufLen_I - d_writeNdx_I, n_start_I = 0;
191     if (n_now_I > bufLen_I)
192       n_now_I = bufLen_I;
193     else if (n_now_I < bufLen_I)
194       n_start_I = bufLen_I - n_now_I;
195     bcopy (buf, &(d_buffer[d_writeNdx_I]), n_now_I * sizeof (T));
196     if (n_start_I) {
197       bcopy (&(buf[n_now_I]), d_buffer, n_start_I * sizeof (T));
198       d_writeNdx_I = n_start_I;
199     } else
200       d_writeNdx_I += n_now_I;
201     d_n_avail_read_I += bufLen_I;
202     d_n_avail_write_I -= bufLen_I;
203     d_readBlock->signal ();
204     d_internal->unlock ();
205     return (1);
206   };
207
208 /*
209  * dequeue: removes from the queue the number of items requested, or
210  *     available, into the given buffer on a FIFO basis.
211  *
212  * inputs:
213  *     buf: a pointer to the buffer into which to copy the data
214  *
215  *     bufLen_I: pointer to the number of items to remove in items
216  *         (of the instantiated type)
217  *
218  * returns:
219  *     0: if nothing to do (0 length buffer)
220  *     1: if success
221  *     2: in the process of aborting, do doing nothing
222  *
223  * will throw runtime errors if inputs are improper:
224  *     buffer pointer is NULL
225  *     buffer length pointer is NULL
226  *     buffer length is larger than the instantiated buffer length
227  */
228
229
230   int dequeue (T* buf, UInt32* bufLen_I) {
231 #if DO_DEBUG
232     fprintf (stderr, "dequeue: buf = %X, *bufLen = %ld.\n",
233              (unsigned int)buf, *bufLen_I);
234 #endif
235     if (!bufLen_I)
236       throw std::runtime_error ("circular_buffer::dequeue(): "
237                                 "input bufLen pointer is NULL.\n");
238     if (!buf)
239       throw std::runtime_error ("circular_buffer::dequeue(): "
240                                 "input buffer pointer is NULL.\n");
241     UInt32 l_bufLen_I = *bufLen_I;
242     if (l_bufLen_I == 0)
243       return (0);
244     if (l_bufLen_I > d_bufLen_I) {
245       fprintf (stderr, "cannot remove buffer longer (%ld"
246                ") than instantiated length (%ld"
247                ").\n", l_bufLen_I, d_bufLen_I);
248       throw std::runtime_error ("circular_buffer::dequeue()");
249     }
250
251     d_internal->lock ();
252     if (d_doAbort) {
253       d_internal->unlock ();
254       return (2);
255     }
256     if (d_doFullRead) {
257       while (d_n_avail_read_I < l_bufLen_I) {
258 #if DO_DEBUG
259         fprintf (stderr, "dequeue: #a < #len, waiting.\n");
260 #endif
261         d_internal->unlock ();
262         d_readBlock->wait ();
263         d_internal->lock ();
264         if (d_doAbort) {
265           d_internal->unlock ();
266 #if DO_DEBUG
267           fprintf (stderr, "dequeue: #a < #len, aborting.\n");
268 #endif
269           return (2);
270         }
271 #if DO_DEBUG
272         fprintf (stderr, "dequeue: #a < #len, done waiting.\n");
273 #endif
274      }
275     } else {
276       while (d_n_avail_read_I == 0) {
277 #if DO_DEBUG
278         fprintf (stderr, "dequeue: #a == 0, waiting.\n");
279 #endif
280         d_internal->unlock ();
281         d_readBlock->wait ();
282         d_internal->lock ();
283         if (d_doAbort) {
284           d_internal->unlock ();
285 #if DO_DEBUG
286           fprintf (stderr, "dequeue: #a == 0, aborting.\n");
287 #endif
288           return (2);
289         }
290 #if DO_DEBUG
291         fprintf (stderr, "dequeue: #a == 0, done waiting.\n");
292 #endif
293       }
294     }
295     if (l_bufLen_I > d_n_avail_read_I)
296       l_bufLen_I = d_n_avail_read_I;
297     UInt32 n_now_I = d_bufLen_I - d_readNdx_I, n_start_I = 0;
298     if (n_now_I > l_bufLen_I)
299       n_now_I = l_bufLen_I;
300     else if (n_now_I < l_bufLen_I)
301       n_start_I = l_bufLen_I - n_now_I;
302     bcopy (&(d_buffer[d_readNdx_I]), buf, n_now_I * sizeof (T));
303     if (n_start_I) {
304       bcopy (d_buffer, &(buf[n_now_I]), n_start_I * sizeof (T));
305       d_readNdx_I = n_start_I;
306     } else
307       d_readNdx_I += n_now_I;
308     *bufLen_I = l_bufLen_I;
309     d_n_avail_read_I -= l_bufLen_I;
310     d_n_avail_write_I += l_bufLen_I;
311     d_writeBlock->signal ();
312     d_internal->unlock ();
313     return (1);
314   };
315
316   void abort () {
317     d_internal->lock ();
318     d_doAbort = true;
319     d_writeBlock->signal ();
320     d_readBlock->signal ();
321     d_internal->unlock ();
322   };
323 };
324
325 #endif /* _CIRCULAR_BUFFER_H_ */