Statistics
| Branch: | Tag: | Revision:

root / gnuradio-core / src / lib / runtime / gr_buffer.cc @ c1721c76

History | View | Annotate | Download (9.5 kB)

1
/* -*- c++ -*- */
2
/*
3
 * Copyright 2004,2009,2010 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
#include <boost/math/common_factor_rt.hpp>
35
36
static long s_buffer_count = 0;                // counts for debugging storage mgmt
37
static long s_buffer_reader_count = 0;
38
39
// ----------------------------------------------------------------------------
40
//                        Notes on storage management
41
//
42
// Pretty much all the fundamental classes are now using the
43
// shared_ptr stuff for automatic reference counting.  To ensure that
44
// no mistakes are made, we make the constructors for classes private,
45
// and then provide a free factory function that returns a smart
46
// pointer to the desired class.
47
//
48
// gr_buffer and gr_buffer_reader are no exceptions.  However, they
49
// both want pointers to each other, and unless we do something, we'll
50
// never delete any of them because of the circular structure.
51
// They'll always have a reference count of at least one.  We could
52
// use boost::weak_ptr's from gr_buffer to gr_buffer_reader but that
53
// introduces it's own problems.  (gr_buffer_reader's destructor needs
54
// to call gr_buffer::drop_reader, but has no easy way to get a
55
// shared_ptr to itself.)
56
//
57
// Instead, we solve this problem by having gr_buffer hold a raw
58
// pointer to gr_buffer_reader in its d_reader vector.
59
// gr_buffer_reader's destructor calls gr_buffer::drop_reader, so
60
// we're never left with an dangling pointer.  gr_buffer_reader still
61
// has a shared_ptr to the buffer ensuring that the buffer doesn't go
62
// away under it.  However, when the reference count of a
63
// gr_buffer_reader goes to zero, we can successfully reclaim it.
64
// ----------------------------------------------------------------------------
65
66
67
/*
68
 * Compute the minimum number of buffer items that work (i.e.,
69
 * address space wrap-around works).  To work is to satisfy this
70
 * contraint for integer buffer_size and k:
71
 *
72
 *     type_size * nitems == k * page_size
73
 */
74
static long
75
minimum_buffer_items (long type_size, long page_size)
76
{
77
  return page_size / boost::math::gcd (type_size, page_size);
78
}
79
80
81
gr_buffer::gr_buffer (int nitems, size_t sizeof_item, gr_block_sptr link)
82
  : d_base (0), d_bufsize (0), d_vmcircbuf (0),
83
    d_sizeof_item (sizeof_item), d_link(link),
84
    d_write_index (0), d_abs_write_offset(0), d_done (false),
85
    d_last_min_items_read(0)
86
{
87
  if (!allocate_buffer (nitems, sizeof_item))
88
    throw std::bad_alloc ();
89
90
  s_buffer_count++;
91
}
92
93
gr_buffer_sptr 
94
gr_make_buffer (int nitems, size_t sizeof_item, gr_block_sptr link)
95
{
96
  return gr_buffer_sptr (new gr_buffer (nitems, sizeof_item, link));
97
}
98
99
gr_buffer::~gr_buffer ()
100
{
101
  delete d_vmcircbuf;
102
  assert (d_readers.size() == 0);
103
  s_buffer_count--;
104
}
105
106
/*!
107
 * sets d_vmcircbuf, d_base, d_bufsize.
108
 * returns true iff successful.
109
 */
110
bool
111
gr_buffer::allocate_buffer (int nitems, size_t sizeof_item)
112
{
113
  int        orig_nitems = nitems;
114
  
115
  // Any buffersize we come up with must be a multiple of min_nitems.
116
117
  int granularity = gr_vmcircbuf_sysconfig::granularity ();
118
  int min_nitems =  minimum_buffer_items (sizeof_item, granularity);
119
120
  // Round-up nitems to a multiple of min_nitems.
121
122
  if (nitems % min_nitems != 0)
123
    nitems = ((nitems / min_nitems) + 1) * min_nitems;
124
125
  // If we rounded-up a whole bunch, give the user a heads up.
126
  // This only happens if sizeof_item is not a power of two.
127
128
  if (nitems > 2 * orig_nitems && nitems * (int) sizeof_item > granularity){
129
    std::cerr << "gr_buffer::allocate_buffer: warning: tried to allocate\n"
130
              << "   " << orig_nitems << " items of size "
131
              << sizeof_item << ". Due to alignment requirements\n"
132
              << "   " << nitems << " were allocated.  If this isn't OK, consider padding\n"
133
              << "   your structure to a power-of-two bytes.\n"
134
              << "   On this platform, our allocation granularity is " << granularity << " bytes.\n";
135
  }
136
137
  d_bufsize = nitems;
138
  d_vmcircbuf = gr_vmcircbuf_sysconfig::make (d_bufsize * d_sizeof_item);
139
  if (d_vmcircbuf == 0){
140
    std::cerr << "gr_buffer::allocate_buffer: failed to allocate buffer of size "
141
              << d_bufsize * d_sizeof_item / 1024 << " KB\n";
142
    return false;
143
  }
144
145
  d_base = (char *) d_vmcircbuf->pointer_to_first_copy ();
146
  return true;
147
}
148
149
150
int
151
gr_buffer::space_available ()
152
{
153
  if (d_readers.empty ())
154
    return d_bufsize - 1;        // See comment below
155
156
  else {
157
158
    // Find out the maximum amount of data available to our readers
159
160
    int        most_data = d_readers[0]->items_available ();
161
    uint64_t min_items_read = d_readers[0]->nitems_read();
162
    for (size_t i = 1; i < d_readers.size (); i++) {
163
      most_data = std::max (most_data, d_readers[i]->items_available ());
164
      min_items_read = std::min(min_items_read, d_readers[i]->nitems_read());
165
    }
166
167
    if(min_items_read != d_last_min_items_read) {
168
      prune_tags(d_last_min_items_read);
169
      d_last_min_items_read = min_items_read;
170
    }
171
172
    // The -1 ensures that the case d_write_index == d_read_index is
173
    // unambiguous.  It indicates that there is no data for the reader
174
175
    return d_bufsize - most_data - 1;
176
  }
177
}
178
179
void *
180
gr_buffer::write_pointer ()
181
{
182
  return &d_base[d_write_index * d_sizeof_item];
183
}
184
185
void
186
gr_buffer::update_write_pointer (int nitems)
187
{
188
  gruel::scoped_lock guard(*mutex());
189
  d_write_index = index_add (d_write_index, nitems);
190
  d_abs_write_offset += nitems;
191
}
192
193
void
194
gr_buffer::set_done (bool done)
195
{
196
  gruel::scoped_lock guard(*mutex());
197
  d_done = done;
198
}
199
200
gr_buffer_reader_sptr
201
gr_buffer_add_reader (gr_buffer_sptr buf, int nzero_preload, gr_block_sptr link)
202
{
203
  if (nzero_preload < 0)
204
    throw std::invalid_argument("gr_buffer_add_reader: nzero_preload must be >= 0");
205
206
  gr_buffer_reader_sptr r (new gr_buffer_reader (buf,
207
                                                 buf->index_sub(buf->d_write_index,
208
                                                                nzero_preload),
209
                                                 link));
210
  buf->d_readers.push_back (r.get ());
211
212
  return r;
213
}
214
215
void
216
gr_buffer::drop_reader (gr_buffer_reader *reader)
217
{
218
  // isn't C++ beautiful...    GAG!
219
220
  std::vector<gr_buffer_reader *>::iterator result =
221
    std::find (d_readers.begin (), d_readers.end (), reader);
222
223
  if (result == d_readers.end ())
224
    throw std::invalid_argument ("gr_buffer::drop_reader");    // we didn't find it...
225
226
  d_readers.erase (result);
227
}
228
229
void
230
gr_buffer::add_item_tag(const gr_tag_t &tag)
231
{
232
  gruel::scoped_lock guard(*mutex());
233
  d_item_tags.push_back(tag);
234
}
235
236
void
237
gr_buffer::prune_tags(uint64_t max_time)
238
{
239
  /* NOTE: this function _should_ lock the mutex before editing
240
     d_item_tags. In practice, this function is only called at
241
     runtime by min_available_space in gr_block_executor.cc,
242
     which locks the mutex itself.
243
     
244
     If this function is used elsewhere, remember to lock the
245
     buffer's mutex al la the scoped_lock line below.
246
  */
247
  //gruel::scoped_lock guard(*mutex());
248
  std::deque<gr_tag_t>::iterator itr = d_item_tags.begin();
249
250
  uint64_t item_time;
251
252
  // Since tags are not guarenteed to be in any particular order,
253
  // we need to erase here instead of pop_front. An erase in the
254
  // middle invalidates all iterators; so this resets the iterator
255
  // to find more. Mostly, we wil be erasing from the front and
256
  // therefore lose little time this way.
257
  while(itr != d_item_tags.end()) {
258
    item_time = (*itr).offset;
259
    if(item_time < max_time) {
260
      d_item_tags.erase(itr);
261
      itr = d_item_tags.begin();
262
    }
263
    else
264
      itr++;
265
  }
266
}
267
268
long
269
gr_buffer_ncurrently_allocated ()
270
{
271
  return s_buffer_count;
272
}
273
274
// ----------------------------------------------------------------------------
275
276
gr_buffer_reader::gr_buffer_reader(gr_buffer_sptr buffer, unsigned int read_index,
277
                                   gr_block_sptr link)
278
  : d_buffer(buffer), d_read_index(read_index), d_abs_read_offset(0), d_link(link)
279
{
280
  s_buffer_reader_count++;
281
}
282
283
gr_buffer_reader::~gr_buffer_reader ()
284
{
285
  d_buffer->drop_reader(this);
286
  s_buffer_reader_count--;
287
}
288
   
289
int
290
gr_buffer_reader::items_available () const
291
{
292
  return d_buffer->index_sub (d_buffer->d_write_index, d_read_index);
293
}
294
295
const void *
296
gr_buffer_reader::read_pointer ()
297
{
298
  return &d_buffer->d_base[d_read_index * d_buffer->d_sizeof_item];
299
}
300
301
void
302
gr_buffer_reader::update_read_pointer (int nitems)
303
{
304
  gruel::scoped_lock guard(*mutex());
305
  d_read_index = d_buffer->index_add (d_read_index, nitems);
306
  d_abs_read_offset += nitems;
307
}
308
309
void
310
gr_buffer_reader::get_tags_in_range(std::vector<gr_tag_t> &v,
311
                                    uint64_t abs_start,
312
                                    uint64_t abs_end)
313
{
314
  gruel::scoped_lock guard(*mutex());
315
316
  v.resize(0);
317
  std::deque<gr_tag_t>::iterator itr = d_buffer->get_tags_begin();
318
  
319
  uint64_t item_time;
320
  while(itr != d_buffer->get_tags_end()) {
321
    item_time = (*itr).offset;
322
323
    if((item_time >= abs_start) && (item_time < abs_end)) {
324
      v.push_back(*itr);
325
    }
326
327
    itr++;
328
  }
329
}
330
331
long
332
gr_buffer_reader_ncurrently_allocated ()
333
{
334
  return s_buffer_reader_count;
335
}