My Project
 All Classes Files Functions Variables Enumerations Enumerator Friends Macros Pages
CircularBuffer.h
Go to the documentation of this file.
1 #ifndef GCP_UTIL_CIRCULARBUFFER_H
2 #define GCP_UTIL_CIRCULARBUFFER_H
3 
11 #include <vector>
12 #include <map>
13 
14 #include "gcp/util/common/Mutex.h"
15 #include "gcp/util/common/Exception.h"
16 
17 namespace gcp {
18  namespace util {
19 
20  class type;
21 
22  template<class type>
24  public:
25 
29  CircularBuffer(unsigned nFrame);
30 
34  virtual ~CircularBuffer();
35 
42  type* getFrame(unsigned int id, bool create);
43 
48  type* getNextFrame();
49 
57  type* dispatchNextFrame();
58 
62  unsigned int getNframesInQueue();
63 
64  protected:
65 
70  unsigned int id_;
71  type* frame_;
72  struct CircularBufferSlot* next_;
73  };
74 
78  std::vector<struct CircularBufferSlot> slots_;
79 
83  unsigned long nSlot_;
84 
85  private:
86 
90  Mutex guard_;
91 
95  std::map<unsigned int, struct CircularBufferSlot*> frameMap_;
96 
100  struct CircularBufferSlot* nextFreeSlot_;
101 
106  struct CircularBufferSlot* nextSendSlot_;
107 
111  struct CircularBufferSlot* lastSentSlot_;
112 
116  unsigned long nUsed_;
117 
121  struct CircularBufferSlot* findSlot(unsigned int id);
122 
126  struct CircularBufferSlot* getNextSlot();
127 
131  void clearSlot(CircularBufferSlot* slot);
132 
133  }; // End class CircularBuffer
134 
135  //=======================================================================
136  // Methods
137  //=======================================================================
138 
142  template<class type>
144  {
145  nSlot_ = nSlot;
146  nUsed_ = 0;
147 
148  // Initialize our index pointers to NULL
149 
150  nextFreeSlot_ = 0;
151  nextSendSlot_ = 0;
152  lastSentSlot_ = 0;
153 
154  // Check passed arguments
155 
156  if(nSlot_ < 1)
157  ThrowError("nSlot < 1");
158 
159  // Initialize each type to have a buffer large enough to
160  // accomodate a frame
161 
162  slots_.resize(nSlot_);
163 
164  // Turn the vector into a circular linked list for convenience of
165  // traversal.
166 
167  for(unsigned islot=0; islot < nSlot_-1; islot++)
168  slots_[islot].next_ = &slots_[islot+1];
169  slots_[nSlot_-1].next_ = &slots_[0];
170 
171  // And set both index pointers pointing to the first element.
172  // lastSentSlot we leave NULL, so we can tell when no frames have
173  // been sent.
174 
175  nextFreeSlot_ = &slots_[0];
176  nextSendSlot_ = &slots_[0];
177  }
178 
182  template<class type>
184 
189  template<class type>
191  {
192  // If there are no slots waiting to be dispatched, return NULL
193 
194  if(nUsed_ == 0)
195  return 0;
196 
197  // Else set the return pointer pointing to the next frame to be sent
198 
199  type* dfm = nextSendSlot_->frame_;
200 
201  // Remove this slot from the map of known slots. While it is being
202  // sent, we don't want anyone else to find it as a valid slot.
203 
204  frameMap_.erase(nextSendSlot_->id_);
205 
206  // Lock the frame. This protects against writers trying to modify
207  // the frame while the calling thread is attempting to send it.
208 
209  dfm->lock();
210 
211  // Only decrement the count of frames waiting to be sent if this
212  // call means we just finished sending a frame.
213 
214  if(lastSentSlot_ != 0) {
215  clearSlot(lastSentSlot_);
216  nUsed_--;
217  }
218 
219  // Set the last sent frame pointing to the one we are
220  // currently dispatching
221 
222  lastSentSlot_ = nextSendSlot_;
223 
224  // And increment the next send frame pointer
225 
226  nextSendSlot_ = nextSendSlot_->next_;
227 
228  return dfm;
229  }
230 
235  template<class type>
236  struct CircularBuffer<type>::CircularBufferSlot*
237  CircularBuffer<type>::getNextSlot()
238  {
239  struct CircularBufferSlot* slot = nextFreeSlot_;
240 
241  // If we have come full circle in the buffer, start eating our tail
242 
243  if(nUsed_ == nSlot_) {
244 
245  clearSlot(nextSendSlot_);
246  nextSendSlot_ = nextSendSlot_->next_;
247 
248  // Only increment nUsed if the buffer is not already full
249 
250  } else {
251  nUsed_++;
252  }
253 
254  // Set the index pointer pointing to the next free slot
255 
256  nextFreeSlot_ = nextFreeSlot_->next_;
257 
258  return slot;
259  }
260 
264  template<class type>
266  {
267  struct CircularBufferSlot* slot = getNextSlot();
268 
269  if(slot != 0)
270  return slot->frame_;
271 
272  return 0;
273  }
274 
283  template<class type>
284  type* CircularBuffer<type>::getFrame(unsigned int id, bool create)
285  {
286  type* frame=0;
287  bool wasErr = false;
288 
289  // Lock the frame buffer. We do this so that no other call to
290  // getFrame() can modify the slot map while we are searching it.
291  //
292  // Even with interlocking, we can still run into trouble if multiple
293  // threads are simultaneously creating slots with create == true,
294  // since the returned frame may refer to a slot whose id has since
295  // been modified by another call to this method, but this is not my
296  // usage model. What I'm concerned about is a single thread
297  // creating frames with create == true, and multiple threads looking
298  // them up with create == false. In this case, as long as we use
299  // interlocking, we are guaranteed that one thread will not search
300  // the map while another thread is in the process of modifying it,
301  // and vice versa.
302 
303  guard_.lock();
304 
305  try {
306 
307  // Search the map for a match
308 
309  struct CircularBufferSlot* slot = findSlot(id);
310 
311  // If a slot with that id was found, return it. If no slot was
312  // found, and we are not creating slots with this call, return
313  // NULL.
314 
315  if(slot != 0 || !create) {
316  if(slot == 0)
317  frame = 0;
318  else
319  frame = (slot == 0) ? 0 : slot->frame_;
320  } else {
321 
322  // Else create the slot
323 
324  slot = getNextSlot();
325 
326  // If no free slots were found, slot will be NULL.
327 
328  if(slot!=0) {
329 
330  // If a free slot was found, insert it into the map of
331  // tagged frames
332 
333  frameMap_[id] = slot;
334 
335  // And set the id of this slot
336 
337  slot->id_ = id;
338 
339  // Set the return value
340 
341  frame = slot->frame_;
342  }
343  }
344  } catch (...) {
345  wasErr = true;
346  }
347 
348  // Release the frame buffer guard mutex
349 
350  guard_.unlock();
351 
352  // If an error occurred, throw it here.
353 
354  if(wasErr)
355  ThrowError("Caught an exception");
356 
357  // Else return the frame
358 
359  return frame;
360  }
361 
365  template<class type>
366  struct CircularBuffer<type>::CircularBufferSlot*
367  CircularBuffer<type>::findSlot(unsigned int id)
368  {
369  std::map<unsigned int, struct CircularBufferSlot*>::iterator slot;
370 
371  slot = frameMap_.find(id);
372 
373  if(slot != frameMap_.end())
374  return slot->second;
375 
376  return 0;
377  }
378 
383  template<class type>
385  {
386  return nUsed_;
387  }
388 
392  template<class type>
393  void CircularBuffer<type>::clearSlot(CircularBufferSlot* slot)
394  {
395  // Delete this slot's entry in the map
396 
397  frameMap_.erase(slot->id_);
398 
399  // Make sure this frame is unlocked
400 
401  slot->frame_->unlock();
402 
403  // And reinitialize its contents.
404 
405  slot->frame_->reinitialize();
406  }
407 
408  } // End namespace util
409 } // End namespace gcp
410 
411 
412 #endif // End #ifndef GCP_UTIL_CIRCULARBUFFER_H
type * getNextFrame()
Definition: CircularBuffer.h:265
type * dispatchNextFrame()
Definition: CircularBuffer.h:190
type * getFrame(unsigned int id, bool create)
Definition: CircularBuffer.h:284
unsigned int getNframesInQueue()
Definition: CircularBuffer.h:384
Definition: Mutex.h:16
CircularBuffer(unsigned nFrame)
Definition: CircularBuffer.h:143
std::vector< struct CircularBufferSlot > slots_
Definition: CircularBuffer.h:78
Definition: CircularBuffer.h:69
virtual ~CircularBuffer()
Definition: CircularBuffer.h:183
unsigned long nSlot_
Definition: CircularBuffer.h:83
Definition: CircularBuffer.h:23