// ball_recordbuffer.h                                                -*-C++-*-

// ----------------------------------------------------------------------------
//                                   NOTICE
//
// This component is not up to date with current BDE coding standards, and
// should not be used as an example for new development.
// ----------------------------------------------------------------------------

#ifndef INCLUDED_BALL_RECORDBUFFER
#define INCLUDED_BALL_RECORDBUFFER

#include <bsls_ident.h>
BSLS_IDENT("$Id: $")

//@PURPOSE: Provide a protocol for managing log record handles.
//
//@CLASSES:
//  ball::RecordBuffer: protocol class for managing log record handles
//
//@SEE_ALSO: ball_record, 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
// 'bsl::vector<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
//      }
//  }
//..

#include <balscm_version.h>

#include <bsl_memory.h>

namespace BloombergLP {
namespace ball {

class Record;

                           // ==================
                           // class RecordBuffer
                           // ==================

class RecordBuffer {
    // Provide a protocol (or pure interface) for managing record handles
    // (specifically instances of 'bsl::shared_ptr<Record>').

  public:
    // CREATORS
    virtual ~RecordBuffer();
        // Remove all record handles stored in this record buffer and destroy
        // this record buffer.

    // MANIPULATORS
    virtual void popBack() = 0;
        // Remove from this record buffer the record handle positioned at the
        // back of the buffer.  The behavior is undefined unless
        // '0 < length()'.

    virtual void popFront() = 0;
        // Remove from this record buffer the record handle positioned at the
        // front of the buffer.  The behavior is undefined unless
        // '0 < length()'.

    virtual int pushBack(const bsl::shared_ptr<Record>& handle) = 0;
        // Append the specified 'handle' to the back of this record buffer.
        // Return 0 on success, and a non-zero value otherwise.  Note that
        // concrete implementations are permitted to remove records from the
        // buffer in order to attempt to accommodate a 'pushBack' request
        // (which implies that, after a successful call to 'pushBack', 'length'
        // is not guaranteed to be more than one, and an unsuccessful call to
        // 'pushBack' is permitted to leave the buffer empty).

    virtual int pushFront(const bsl::shared_ptr<Record>& handle) = 0;
        // Insert the specified 'handle' at the front of this record buffer.
        // Return 0 on success, and a non-zero value otherwise.  Note that
        // concrete implementations are permitted to remove records from the
        // buffer in order to attempt to accommodate a 'pushFront' request
        // (which implies that, after a successful call to 'pushFront',
        // 'length' is not guaranteed to be more than one, and an unsuccessful
        // call to 'pushFront' is permitted to leave the buffer empty).

    virtual void removeAll() = 0;
        // Remove all record handles stored in this record buffer.  Note that
        // 'length()' is now 0.

    virtual void beginSequence() = 0;
        // *Lock* this record buffer so that a sequence of method invocations
        // on this record buffer can occur uninterrupted by other threads.  The
        // buffer will remain *locked* until 'endSequence' is called.

    virtual void endSequence() = 0;
        // *Unlock* this record buffer, thus allowing other threads to access
        // it.  The behavior is undefined unless the buffer is already *locked*
        // by 'beginSequence' method.

    // ACCESSORS
    virtual const bsl::shared_ptr<Record>& back() const = 0;
        // Return a reference of the shared pointer referring to the record
        // positioned at the back of this record buffer.  The behavior is
        // undefined unless this record buffer has been locked by the
        // 'beginSequence' method and '0 < length()'.

    virtual const bsl::shared_ptr<Record>& front() const = 0;
        // Return a reference of the shared pointer referring to the record
        // positioned at the front of this record buffer.  The behavior is
        // undefined unless this record buffer has been locked by the
        // 'beginSequence' method and '0 < length()'.

    virtual int length() const = 0;
        // Return the number of record handles in this record buffer.
};

}  // close package namespace
}  // close enterprise namespace

#endif

// ----------------------------------------------------------------------------
// Copyright 2015 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------- END-OF-FILE ----------------------------------