3 * Copyright 2004 Free Software Foundation, Inc.
5 * This file is part of GNU Radio
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)
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.
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.
27 #include <gr_buffer.h>
28 #include <gr_vmcircbuf.h>
35 static long s_buffer_count = 0; // counts for debugging storage mgmt
36 static long s_buffer_reader_count = 0;
38 // ----------------------------------------------------------------------------
39 // Notes on storage management
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.
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.)
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 // ----------------------------------------------------------------------------
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:
71 * type_size * nitems == k * page_size
74 minimum_buffer_items (long type_size, long page_size)
76 return page_size / gr_gcd (type_size, page_size);
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)
85 if (!allocate_buffer (nitems, sizeof_item))
86 throw std::bad_alloc ();
92 gr_make_buffer (int nitems, size_t sizeof_item, gr_block_sptr link)
94 return gr_buffer_sptr (new gr_buffer (nitems, sizeof_item, link));
97 gr_buffer::~gr_buffer ()
100 assert (d_readers.size() == 0);
105 * sets d_vmcircbuf, d_base, d_bufsize.
106 * returns true iff successful.
109 gr_buffer::allocate_buffer (int nitems, size_t sizeof_item)
111 int orig_nitems = nitems;
113 // Any buffersize we come up with must be a multiple of min_nitems.
115 int granularity = gr_vmcircbuf_sysconfig::granularity ();
116 int min_nitems = minimum_buffer_items (sizeof_item, granularity);
118 // Round-up nitems to a multiple of min_nitems.
120 if (nitems % min_nitems != 0)
121 nitems = ((nitems / min_nitems) + 1) * min_nitems;
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.
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";
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";
143 d_base = (char *) d_vmcircbuf->pointer_to_first_copy ();
149 gr_buffer::space_available ()
151 if (d_readers.empty ())
152 return d_bufsize - 1; // See comment below
156 // Find out the maximum amount of data available to our readers
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 ());
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
165 return d_bufsize - most_data - 1;
170 gr_buffer::write_pointer ()
172 return &d_base[d_write_index * d_sizeof_item];
176 gr_buffer::update_write_pointer (int nitems)
178 scoped_lock guard(*mutex());
179 d_write_index = index_add (d_write_index, nitems);
183 gr_buffer::set_done (bool done)
185 scoped_lock guard(*mutex());
189 gr_buffer_reader_sptr
190 gr_buffer_add_reader (gr_buffer_sptr buf, int nzero_preload, gr_block_sptr link)
192 if (nzero_preload < 0)
193 throw std::invalid_argument("gr_buffer_add_reader: nzero_preload must be >= 0");
195 gr_buffer_reader_sptr r (new gr_buffer_reader (buf,
196 buf->index_sub(buf->d_write_index,
199 buf->d_readers.push_back (r.get ());
205 gr_buffer::drop_reader (gr_buffer_reader *reader)
207 // isn't C++ beautiful... GAG!
209 std::vector<gr_buffer_reader *>::iterator result =
210 std::find (d_readers.begin (), d_readers.end (), reader);
212 if (result == d_readers.end ())
213 throw std::invalid_argument ("gr_buffer::drop_reader"); // we didn't find it...
215 d_readers.erase (result);
219 gr_buffer_ncurrently_allocated ()
221 return s_buffer_count;
224 // ----------------------------------------------------------------------------
226 gr_buffer_reader::gr_buffer_reader(gr_buffer_sptr buffer, unsigned int read_index,
228 : d_buffer(buffer), d_read_index(read_index), d_link(link)
230 s_buffer_reader_count++;
233 gr_buffer_reader::~gr_buffer_reader ()
235 d_buffer->drop_reader(this);
236 s_buffer_reader_count--;
240 gr_buffer_reader::items_available () const
242 return d_buffer->index_sub (d_buffer->d_write_index, d_read_index);
246 gr_buffer_reader::read_pointer ()
248 return &d_buffer->d_base[d_read_index * d_buffer->d_sizeof_item];
252 gr_buffer_reader::update_read_pointer (int nitems)
254 scoped_lock guard(*mutex());
255 d_read_index = d_buffer->index_add (d_read_index, nitems);
259 gr_buffer_reader_ncurrently_allocated ()
261 return s_buffer_reader_count;