Quick Links:

bal | bbl | bdl | bsl

Namespaces

Component ball_recordbuffer
[Package ball]

Provide a protocol for managing log record handles. More...

Namespaces

namespace  ball

Detailed Description

Outline
Purpose:
Provide a protocol for managing log record handles.
Classes:
ball::RecordBuffer protocol class for managing log record handles
See also:
Component ball_record, Component ball_fixedsizerecordbuffer
Description:
This component defines the base-level protocol, ball::RecordBuffer, for managing record handles (specifically instances of bsl::shared_ptr<ball::Record>) in a double-ended buffer. In particular, the ball::RecordBuffer protocol class provides abstract methods to push a record handle into either end (back or front) of the buffer (pushBack and pushFront), to obtain read-only access to the log record positioned at either end (back and front) and to remove the record positioned at either end (popBack and popFront). Note that the classes implementing this protocol are supposed to manage record handles and not the records themselves, specifically, they should not allocate the memory for records and should not explicitly destroy records (a record is destroyed automatically when the reference count associated with its handle becomes zero). The push methods (pushBack and pushFront) are not guaranteed to succeed. Concrete implementations implementations are permitted to remove records from the buffer in order to attempt to accommodate a push request (which implies that, after a successful call to a push method, length is not guaranteed to be more than one, and an unsuccessful call to a push method is permitted to leave the buffer empty).
Usage:
This section illustrates intended use of this component.
Example 1: Defining a Concrete RecordBuffer Type:
This example shows the definition of a simple concrete record buffer. The requisite steps are:
  1. Define a concrete class derived from ball::RecordBuffer.
  2. Implement all pure virtual functions.
The concrete thread-safe my_RecordBuffer class in this example implements the ball::RecordBuffer protocol by delegating to an instance of 'bslvector<bsl::shared_ptr<ball::Record> > ':
  // my_recordbuffer.h

  class my_RecordBuffer : public ball::RecordBuffer {
      // This class provides a thread-safe implementation of the
      // 'ball::RecordBuffer' protocol.  This implementation employs a vector
      // to hold an unlimited number of record handles.

      mutable bslmt::RecursiveMutex d_mutex;
                                   // thread safety provider (see
                                   // the implementation notes for the
                                   // justification for using recursive mutex
                                   // rather a plain mutex)

      bsl::vector<bsl::shared_ptr<ball::Record> >
                         d_buffer; // buffer of record handles

    private:
      // NOT IMPLEMENTED
      my_RecordBuffer(const my_RecordBuffer&);
      my_RecordBuffer& operator=(const my_RecordBuffer&);

    public:
      // CREATORS
      my_RecordBuffer();
      virtual ~my_RecordBuffer();

      // MANIPULATORS
      virtual void beginSequence();
      virtual void endSequence();
      virtual void popBack();
      virtual void popFront();
      virtual int pushBack(const bsl::shared_ptr<ball::Record>& handle);
      virtual int pushFront(
                   const bsl::shared_ptr<ball::Record>& handle);
      virtual void removeAll();

      // ACCESSORS
      virtual const bsl::shared_ptr<ball::Record>& back() const;
      virtual const bsl::shared_ptr<ball::Record>& front() const;
      virtual int length() const;
  };
Implementation Notes:
Recursive mutex (rather than plain mutex) is chosen to provide thread safety. This allows the manipulation of the record buffer between the call to beginSequence and endSequence. If we had used plain mutex, calling any method on the record buffer between the calls to beginSequence and endSequence would result in a deadlock.
  // CREATORS
  inline
  my_RecordBuffer::my_RecordBuffer() { }

  // MANIPULATORS
  inline
  void my_RecordBuffer::beginSequence()
  {
      d_mutex.lock();
  }

  inline
  void my_RecordBuffer::endSequence()
  {
      d_mutex.unlock();
  }

  inline
  void my_RecordBuffer::popBack()
  {
      bslmt::LockGuard<bslmt::RecursiveMutex> guard(&d_mutex);
      d_buffer.pop_back();
  }

  inline
  void my_RecordBuffer::popFront()
  {
      bslmt::LockGuard<bslmt::RecursiveMutex> guard(&d_mutex);
      d_buffer.erase(d_buffer.begin());
  }

  inline
  int my_RecordBuffer::pushBack(
                       const bsl::shared_ptr<ball::Record>& handle)
  {
      bslmt::LockGuard<bslmt::RecursiveMutex> guard(&d_mutex);
      d_buffer.push_back(handle);
      return 0;
  }

  inline
  int my_RecordBuffer::pushFront(
                       const bsl::shared_ptr<ball::Record>& handle)
  {
      bslmt::LockGuard<bslmt::RecursiveMutex> guard(&d_mutex);
      d_buffer.insert(d_buffer.begin(), handle);
      return 0;
  }

  inline
  void my_RecordBuffer::removeAll()
  {
      bslmt::LockGuard<bslmt::RecursiveMutex> guard(&d_mutex);
      d_buffer.clear();
  }

  // ACCESSORS
  inline
  const bsl::shared_ptr<ball::Record>& my_RecordBuffer::back() const
  {
      bslmt::LockGuard<bslmt::RecursiveMutex> guard(&d_mutex);
      return d_buffer.back();
  }

  inline
  const bsl::shared_ptr<ball::Record>& my_RecordBuffer::front() const
  {
      bslmt::LockGuard<bslmt::RecursiveMutex> guard(&d_mutex);
      return d_buffer.front();
  }

  inline
  int my_RecordBuffer::length() const
  {
      bslmt::LockGuard<bslmt::RecursiveMutex> guard(&d_mutex);
      return static_cast<int>(d_buffer.size());
  }
Note that we always implement a virtual destructor (non-inline) in the .cpp file (to indicate the unique location of the class's virtual table):
  // my_recordbuffer.cpp

  my_RecordBuffer::~my_RecordBuffer() { }
Example 2: Using a ball::RecordBuffer:
This example demonstrates working with the ball::RecordBuffer protocol. We implement a function processRecord that processes a specified record handle based on its severity.
  void processRecord(const bsl::shared_ptr<ball::Record>& handle,
                           ball::RecordBuffer&            buffer)
      // Process the specified 'handle', based on it's severity.  Records
      // (encapsulated in 'handle') with severity equal to or *greater*
      // severe than (i.e., with *numerical* value *less* than or equal to)
      // 'ball::Severity::e_WARN' are pushed back into the specified
      // 'buffer'.  Records with a severity equal to or more severe than
      // 'ball::Severity::e_ERROR' are (in addition to get pushed back
      // into the 'buffer') printed to 'stdout', and then each record
      // contained in 'buffer' is in turn printed to 'stdout' and then
      // removed from 'buffer'.  That is, any severity level equal to or
      // more severe than 'ball::Severity::e_ERROR' triggers a trace-back
      // of all records accumulated in the buffer and flushes the buffer.
      // The function is thread safe and thus allows multiple concurrent
      // invocations of this function with the same record buffer.

  {
      int severity = handle->fixedFields().severity();

      if (ball::Severity::e_WARN >= severity) {
          buffer.pushBack(handle);
      }
      if (ball::Severity::e_ERROR >= severity) {
          bsl::cout << *handle;
          buffer.beginSequence(); // lock the buffer before traversing
          int length = buffer.length();
          while (length--) {
              bsl::cout << buffer.back();
              buffer.popBack();
          }
          buffer.endSequence();   // unlock the buffer after traversing
      }
  }