Files
epics-base/modules/libcom/src/ring/epicsRingPointer.h
2020-05-19 18:04:26 -05:00

348 lines
10 KiB
C++
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*************************************************************************\
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
* National Laboratory.
* Copyright (c) 2002 The Regents of the University of California, as
* Operator of Los Alamos National Laboratory.
* Copyright (c) 2012 ITER Organization.
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/**
* \file epicsRingPointer.h
* \brief A circular buffer to store pointers
* \author Marty Kraimer, Ralph Lange
*
* \details
* epicsRingPointer.h provides both C and C++ APIs for creating and using ring
* buffers (first in first out circular buffers) that store pointers. The
* unlocked kind is designed so that one writer thread and one reader thread
* can access the ring simultaneously without requiring mutual exclusion. The
* locked variant uses an epicsSpinLock, and works with any numbers of writer
* and reader threads.
* \note If there is only one writer it is not necessary to lock pushes.
* If there is a single reader it is not necessary to lock pops.
* epicsRingPointerLocked uses a spinlock.
*/
#ifndef INCepicsRingPointerh
#define INCepicsRingPointerh
#include "epicsSpin.h"
#include "shareLib.h"
#ifdef __cplusplus
/**
* \brief A C++ template class providing methods for creating and using a ring
* buffer (a first in, first out circular buffer) that stores pointers to
* objects of the template type.
*/
template <class T>
class epicsRingPointer {
public: /* Functions */
/**\brief Constructor
* \param size Maximum number of elements (pointers) that can be stored
* \param locked If true, the spin lock secured variant is created
*/
epicsRingPointer(int size, bool locked);
/**\brief Destructor
*/
~epicsRingPointer();
/**\brief Push a new entry on the ring
* \return True on success, False if the buffer was full
*/
bool push(T *p);
/**\brief Take an element off the ring
* \return The element, or NULL if the ring was empty
*/
T* pop();
/**\brief Remove all elements from the ring.
* \note If this operation is performed on a ring buffer of the
* unsecured kind, all access to the ring should be locked.
*/
void flush();
/**\brief Get how much free space remains in the ring
* \return The number of additional elements the ring could hold.
*/
int getFree() const;
/**\brief Get how many elements are stored on the ring
* \return The number of elements currently stored.
*/
int getUsed() const;
/**\brief Get the size of the ring
* \return The \c size specified when the ring was created.
*/
int getSize() const;
/**\brief Test if the ring is currently empty
* \return True if the ring is empty, otherwise false.
*/
bool isEmpty() const;
/**\brief Test if the ring is currently full
* \return True if the ring is full, otherwise false.
*/
bool isFull() const;
/**\brief See how full the ring has got since it was last checked.
*
* Returns the maximum number of elements the ring
* buffer has held since the water mark was last reset.
* A new ring buffer starts with a water mark of 0.
* \return Actual highwater mark
*/
int getHighWaterMark() const;
/**\brief Reset high water mark
*
* High water mark will be set to the current usage
*/
void resetHighWaterMark();
private: /* Prevent compiler-generated member functions */
/* default constructor, copy constructor, assignment operator */
epicsRingPointer();
epicsRingPointer(const epicsRingPointer &);
epicsRingPointer& operator=(const epicsRingPointer &);
int getUsedNoLock() const;
private: /* Data */
epicsSpinId lock;
volatile int nextPush;
volatile int nextPop;
int size;
int highWaterMark;
T * volatile * buffer;
};
extern "C" {
#endif /*__cplusplus */
/** \brief An identifier for the C API to a ring buffer storing pointers */
typedef void *epicsRingPointerId;
typedef void const *epicsRingPointerIdConst;
/**
* \brief Create a new ring buffer
* \param size Size of ring buffer to create
* \return Ring buffer identifier or NULL on failure
*/
epicsShareFunc epicsRingPointerId epicsShareAPI epicsRingPointerCreate(int size);
/**
* \brief Create a new ring buffer, secured by a spinlock
* \param size Size of ring buffer to create
* \return Ring buffer identifier or NULL on failure
*/
epicsShareFunc epicsRingPointerId epicsShareAPI epicsRingPointerLockedCreate(int size);
/**
* \brief Delete the ring buffer and free any associated memory
* \param id Ring buffer identifier
*/
epicsShareFunc void epicsShareAPI epicsRingPointerDelete(epicsRingPointerId id);
/**
* \brief Push pointer into the ring buffer
* \param id Ring buffer identifier
* \param p Pointer to be pushed to the ring
* \return 1 if the pointer was successfully pushed, 0 if the buffer was full
*/
epicsShareFunc int epicsShareAPI epicsRingPointerPush(epicsRingPointerId id,void *p);
/**
* \brief Take an element off the ring
* \param id Ring buffer identifier
* \return The pointer from the buffer, or NULL if the ring was empty
*/
epicsShareFunc void* epicsShareAPI epicsRingPointerPop(epicsRingPointerId id) ;
/**
* \brief Remove all elements from the ring
* \param id Ring buffer identifier
* \note If this operation is performed on a ring buffer of the unsecured
* kind, all access to the ring should be locked.
*/
epicsShareFunc void epicsShareAPI epicsRingPointerFlush(epicsRingPointerId id);
/**
* \brief Return the amount of empty space in the ring buffer
* \param id Ring buffer identifier
* \return The number of additional elements it could hold.
*/
epicsShareFunc int epicsShareAPI epicsRingPointerGetFree(epicsRingPointerId id);
/**
* \brief Return the number of elements stored in the ring buffer
* \param id Ring buffer identifier
* \return The number of elements stored in the ring buffer
*/
epicsShareFunc int epicsShareAPI epicsRingPointerGetUsed(epicsRingPointerId id);
/**
* \brief Return the size of the ring
* \param id Ring buffer identifier
* \return The size of the ring buffer, i.e. the value of \c size
* given when the ring was created.
*/
epicsShareFunc int epicsShareAPI epicsRingPointerGetSize(epicsRingPointerId id);
/**
* \brief Check if the ring buffer is currently empty
* \param id Ring buffer identifier
* \return 1 if the ring is empty, otherwise 0
*/
epicsShareFunc int epicsShareAPI epicsRingPointerIsEmpty(epicsRingPointerId id);
/**
* \brief Check if the ring buffer is currently full
* \param id Ring buffer identifier
* \return 1 if the ring buffer is full, otherwise 0
*/
epicsShareFunc int epicsShareAPI epicsRingPointerIsFull(epicsRingPointerId id);
/**
* \brief Get the Highwater mark of the ring buffer
*
* Returns the largest number of elements the ring buffer has held since
* the water mark was last reset. A new ring buffer starts with a
* water mark of 0.
* \param id Ring buffer identifier
* \return Actual Highwater mark
*/
epicsShareFunc int epicsShareAPI epicsRingPointerGetHighWaterMark(epicsRingPointerIdConst id);
/**
* \brief Reset the Highwater mark of the ring buffer
*
* The Highwater mark will be set to the current usage
* \param id Ring buffer identifier
*/
epicsShareFunc void epicsShareAPI epicsRingPointerResetHighWaterMark(epicsRingPointerId id);
/* This routine was incorrectly named in previous releases */
#define epicsRingPointerSize epicsRingPointerGetSize
#ifdef __cplusplus
}
#endif
/* END OF DECLARATIONS */
/* INLINE FUNCTIONS */
#ifdef __cplusplus
template <class T>
inline epicsRingPointer<T>::epicsRingPointer(int sz, bool locked) :
lock(0), nextPush(0), nextPop(0), size(sz+1), highWaterMark(0),
buffer(new T* [sz+1])
{
if (locked)
lock = epicsSpinCreate();
}
template <class T>
inline epicsRingPointer<T>::~epicsRingPointer()
{
if (lock) epicsSpinDestroy(lock);
delete [] buffer;
}
template <class T>
inline bool epicsRingPointer<T>::push(T *p)
{
if (lock) epicsSpinLock(lock);
int next = nextPush;
int newNext = next + 1;
if(newNext>=size) newNext=0;
if (newNext == nextPop) {
if (lock) epicsSpinUnlock(lock);
return(false);
}
buffer[next] = p;
nextPush = newNext;
int used = getUsedNoLock();
if (used > highWaterMark) highWaterMark = used;
if (lock) epicsSpinUnlock(lock);
return(true);
}
template <class T>
inline T* epicsRingPointer<T>::pop()
{
if (lock) epicsSpinLock(lock);
int next = nextPop;
if (next == nextPush) {
if (lock) epicsSpinUnlock(lock);
return(0);
}
T*p = buffer[next];
++next;
if(next >=size) next = 0;
nextPop = next;
if (lock) epicsSpinUnlock(lock);
return(p);
}
template <class T>
inline void epicsRingPointer<T>::flush()
{
if (lock) epicsSpinLock(lock);
nextPop = 0;
nextPush = 0;
if (lock) epicsSpinUnlock(lock);
}
template <class T>
inline int epicsRingPointer<T>::getFree() const
{
if (lock) epicsSpinLock(lock);
int n = nextPop - nextPush - 1;
if (n < 0) n += size;
if (lock) epicsSpinUnlock(lock);
return n;
}
template <class T>
inline int epicsRingPointer<T>::getUsedNoLock() const
{
int n = nextPush - nextPop;
if (n < 0) n += size;
return n;
}
template <class T>
inline int epicsRingPointer<T>::getUsed() const
{
if (lock) epicsSpinLock(lock);
int n = getUsedNoLock();
if (lock) epicsSpinUnlock(lock);
return n;
}
template <class T>
inline int epicsRingPointer<T>::getSize() const
{
return(size-1);
}
template <class T>
inline bool epicsRingPointer<T>::isEmpty() const
{
bool isEmpty;
if (lock) epicsSpinLock(lock);
isEmpty = (nextPush == nextPop);
if (lock) epicsSpinUnlock(lock);
return isEmpty;
}
template <class T>
inline bool epicsRingPointer<T>::isFull() const
{
if (lock) epicsSpinLock(lock);
int count = nextPush - nextPop +1;
if (lock) epicsSpinUnlock(lock);
return((count == 0) || (count == size));
}
template <class T>
inline int epicsRingPointer<T>::getHighWaterMark() const
{
return highWaterMark;
}
template <class T>
inline void epicsRingPointer<T>::resetHighWaterMark()
{
if (lock) epicsSpinLock(lock);
highWaterMark = getUsedNoLock();
if (lock) epicsSpinUnlock(lock);
}
#endif /* __cplusplus */
#endif /* INCepicsRingPointerh */