Imported Upstream version 3.2.2
[debian/gnuradio] / gnuradio-core / src / lib / runtime / gr_buffer.cc
1 /* -*- c++ -*- */
2 /*
3  * Copyright 2004 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 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #include <gr_buffer.h>
28 #include <gr_vmcircbuf.h>
29 #include <gr_math.h>
30 #include <stdexcept>
31 #include <iostream>
32 #include <assert.h>
33 #include <algorithm>
34
35 static long s_buffer_count = 0;         // counts for debugging storage mgmt
36 static long s_buffer_reader_count = 0;
37
38 // ----------------------------------------------------------------------------
39 //                      Notes on storage management
40 //
41 // Pretty much all the fundamental classes are now using the
42 // shared_ptr stuff for automatic reference counting.  To ensure that
43 // no mistakes are made, we make the constructors for classes private,
44 // and then provide a free factory function that returns a smart
45 // pointer to the desired class.
46 //
47 // gr_buffer and gr_buffer_reader are no exceptions.  However, they
48 // both want pointers to each other, and unless we do something, we'll
49 // never delete any of them because of the circular structure.
50 // They'll always have a reference count of at least one.  We could
51 // use boost::weak_ptr's from gr_buffer to gr_buffer_reader but that
52 // introduces it's own problems.  (gr_buffer_reader's destructor needs
53 // to call gr_buffer::drop_reader, but has no easy way to get a
54 // shared_ptr to itself.)
55 //
56 // Instead, we solve this problem by having gr_buffer hold a raw
57 // pointer to gr_buffer_reader in its d_reader vector.
58 // gr_buffer_reader's destructor calls gr_buffer::drop_reader, so
59 // we're never left with an dangling pointer.  gr_buffer_reader still
60 // has a shared_ptr to the buffer ensuring that the buffer doesn't go
61 // away under it.  However, when the reference count of a
62 // gr_buffer_reader goes to zero, we can successfully reclaim it.
63 // ----------------------------------------------------------------------------
64
65
66 /*
67  * Compute the minimum number of buffer items that work (i.e.,
68  * address space wrap-around works).  To work is to satisfy this
69  * contraint for integer buffer_size and k:
70  *
71  *     type_size * nitems == k * page_size
72  */
73 static long
74 minimum_buffer_items (long type_size, long page_size)
75 {
76   return page_size / gr_gcd (type_size, page_size);
77 }
78
79
80 gr_buffer::gr_buffer (int nitems, size_t sizeof_item, gr_block_sptr link)
81   : d_base (0), d_bufsize (0), d_vmcircbuf (0),
82     d_sizeof_item (sizeof_item), d_link(link),
83     d_write_index (0), d_done (false)
84 {
85   if (!allocate_buffer (nitems, sizeof_item))
86     throw std::bad_alloc ();
87
88   s_buffer_count++;
89 }
90
91 gr_buffer_sptr 
92 gr_make_buffer (int nitems, size_t sizeof_item, gr_block_sptr link)
93 {
94   return gr_buffer_sptr (new gr_buffer (nitems, sizeof_item, link));
95 }
96
97 gr_buffer::~gr_buffer ()
98 {
99   delete d_vmcircbuf;
100   assert (d_readers.size() == 0);
101   s_buffer_count--;
102 }
103
104 /*!
105  * sets d_vmcircbuf, d_base, d_bufsize.
106  * returns true iff successful.
107  */
108 bool
109 gr_buffer::allocate_buffer (int nitems, size_t sizeof_item)
110 {
111   int   orig_nitems = nitems;
112   
113   // Any buffersize we come up with must be a multiple of min_nitems.
114
115   int granularity = gr_vmcircbuf_sysconfig::granularity ();
116   int min_nitems =  minimum_buffer_items (sizeof_item, granularity);
117
118   // Round-up nitems to a multiple of min_nitems.
119
120   if (nitems % min_nitems != 0)
121     nitems = ((nitems / min_nitems) + 1) * min_nitems;
122
123   // If we rounded-up a whole bunch, give the user a heads up.
124   // This only happens if sizeof_item is not a power of two.
125
126   if (nitems > 2 * orig_nitems && nitems * (int) sizeof_item > granularity){
127     std::cerr << "gr_buffer::allocate_buffer: warning: tried to allocate\n"
128               << "   " << orig_nitems << " items of size "
129               << sizeof_item << ". Due to alignment requirements\n"
130               << "   " << nitems << " were allocated.  If this isn't OK, consider padding\n"
131               << "   your structure to a power-of-two bytes.\n"
132               << "   On this platform, our allocation granularity is " << granularity << " bytes.\n";
133   }
134
135   d_bufsize = nitems;
136   d_vmcircbuf = gr_vmcircbuf_sysconfig::make (d_bufsize * d_sizeof_item);
137   if (d_vmcircbuf == 0){
138     std::cerr << "gr_buffer::allocate_buffer: failed to allocate buffer of size "
139               << d_bufsize * d_sizeof_item / 1024 << " KB\n";
140     return false;
141   }
142
143   d_base = (char *) d_vmcircbuf->pointer_to_first_copy ();
144   return true;
145 }
146
147
148 int
149 gr_buffer::space_available ()
150 {
151   if (d_readers.empty ())
152     return d_bufsize - 1;       // See comment below
153
154   else {
155
156     // Find out the maximum amount of data available to our readers
157
158     int most_data = d_readers[0]->items_available ();
159     for (unsigned int i = 1; i < d_readers.size (); i++)
160       most_data = std::max (most_data, d_readers[i]->items_available ());
161
162     // The -1 ensures that the case d_write_index == d_read_index is
163     // unambiguous.  It indicates that there is no data for the reader
164
165     return d_bufsize - most_data - 1;
166   }
167 }
168
169 void *
170 gr_buffer::write_pointer ()
171 {
172   return &d_base[d_write_index * d_sizeof_item];
173 }
174
175 void
176 gr_buffer::update_write_pointer (int nitems)
177 {
178   scoped_lock   guard(*mutex());
179   d_write_index = index_add (d_write_index, nitems);
180 }
181
182 void
183 gr_buffer::set_done (bool done)
184 {
185   scoped_lock   guard(*mutex());
186   d_done = done;
187 }
188
189 gr_buffer_reader_sptr
190 gr_buffer_add_reader (gr_buffer_sptr buf, int nzero_preload, gr_block_sptr link)
191 {
192   if (nzero_preload < 0)
193     throw std::invalid_argument("gr_buffer_add_reader: nzero_preload must be >= 0");
194
195   gr_buffer_reader_sptr r (new gr_buffer_reader (buf,
196                                                  buf->index_sub(buf->d_write_index,
197                                                                 nzero_preload),
198                                                  link));
199   buf->d_readers.push_back (r.get ());
200
201   return r;
202 }
203
204 void
205 gr_buffer::drop_reader (gr_buffer_reader *reader)
206 {
207   // isn't C++ beautiful...    GAG!
208
209   std::vector<gr_buffer_reader *>::iterator result =
210     std::find (d_readers.begin (), d_readers.end (), reader);
211
212   if (result == d_readers.end ())
213     throw std::invalid_argument ("gr_buffer::drop_reader");    // we didn't find it...
214
215   d_readers.erase (result);
216 }
217
218 long
219 gr_buffer_ncurrently_allocated ()
220 {
221   return s_buffer_count;
222 }
223
224 // ----------------------------------------------------------------------------
225
226 gr_buffer_reader::gr_buffer_reader(gr_buffer_sptr buffer, unsigned int read_index,
227                                    gr_block_sptr link)
228   : d_buffer(buffer), d_read_index(read_index), d_link(link)
229 {
230   s_buffer_reader_count++;
231 }
232
233 gr_buffer_reader::~gr_buffer_reader ()
234 {
235   d_buffer->drop_reader(this);
236   s_buffer_reader_count--;
237 }
238    
239 int
240 gr_buffer_reader::items_available () const
241 {
242   return d_buffer->index_sub (d_buffer->d_write_index, d_read_index);
243 }
244
245 const void *
246 gr_buffer_reader::read_pointer ()
247 {
248   return &d_buffer->d_base[d_read_index * d_buffer->d_sizeof_item];
249 }
250
251 void
252 gr_buffer_reader::update_read_pointer (int nitems)
253 {
254   scoped_lock   guard(*mutex());
255   d_read_index = d_buffer->index_add (d_read_index, nitems);
256 }
257
258 long
259 gr_buffer_reader_ncurrently_allocated ()
260 {
261   return s_buffer_reader_count;
262 }