Quick Links: |
Provide a protocol for managing log record handles. More...
Namespaces | |
namespace | ball |
ball::RecordBuffer | protocol class for managing log record handles |
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). ball::RecordBuffer
. 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; };
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()); }
// my_recordbuffer.cpp
my_RecordBuffer::~my_RecordBuffer() { }
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 } }