Expose callback queue status
This allows tools like iocStats to monitor the queue status of the callback queues. This fixes lp:1786540.
This commit is contained in:
@@ -54,6 +54,7 @@ typedef struct cbQueueSet {
|
||||
epicsEventId semWakeUp;
|
||||
epicsRingPointerId queue;
|
||||
int queueOverflow;
|
||||
int queueOverflows;
|
||||
int shutdown;
|
||||
int threadsConfigured;
|
||||
int threadsRunning;
|
||||
@@ -99,10 +100,55 @@ int callbackSetQueueSize(int size)
|
||||
fprintf(stderr, "Callback system already initialized\n");
|
||||
return -1;
|
||||
}
|
||||
callbackQueueSize = size;
|
||||
epicsAtomicSetIntT(&callbackQueueSize, size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int callbackQueueStatus(const int reset, callbackQueueStats *result)
|
||||
{
|
||||
if (!callbackIsInit) return -1;
|
||||
int ret;
|
||||
if (result) {
|
||||
result->size = epicsAtomicGetIntT(&callbackQueueSize);
|
||||
int prio;
|
||||
for(prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) {
|
||||
epicsRingPointerId qId = callbackQueue[prio].queue;
|
||||
result->numUsed[prio] = epicsRingPointerGetUsed(qId);
|
||||
result->maxUsed[prio] = epicsRingPointerGetHighWaterMark(qId);
|
||||
result->numOverflow[prio] = epicsAtomicGetIntT(&callbackQueue[prio].queueOverflows);
|
||||
}
|
||||
ret = 0;
|
||||
} else {
|
||||
ret = -2;
|
||||
}
|
||||
if (reset) {
|
||||
int prio;
|
||||
for(prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) {
|
||||
epicsRingPointerResetHighWaterMark(callbackQueue[prio].queue);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void callbackQueuePrintStatus(const int reset)
|
||||
{
|
||||
callbackQueueStats stats;
|
||||
if (callbackQueueStatus(reset, &stats) == -1) {
|
||||
fprintf(stderr, "Callback system not initialized, yet. Please run "
|
||||
"iocInit before using this command.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
printf("PRIORITY HIGH-WATER MARK ITEMS IN Q Q SIZE %% USED Q OVERFLOWS\n");
|
||||
int prio;
|
||||
for (prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) {
|
||||
double qusage = 100.0 * stats.numUsed[prio] / stats.size;
|
||||
printf("%8s %15d %10d %6d %6.1f %11d\n", threadNamePrefix[prio],
|
||||
stats.maxUsed[prio], stats.numUsed[prio], stats.size,
|
||||
qusage, stats.numOverflow[prio]);
|
||||
}
|
||||
}
|
||||
|
||||
int callbackParallelThreads(int count, const char *prio)
|
||||
{
|
||||
if (callbackIsInit) {
|
||||
@@ -240,7 +286,7 @@ void callbackInit(void)
|
||||
epicsThreadId tid;
|
||||
|
||||
callbackQueue[i].semWakeUp = epicsEventMustCreate(epicsEventEmpty);
|
||||
callbackQueue[i].queue = epicsRingPointerLockedCreate(callbackQueueSize);
|
||||
callbackQueue[i].queue = epicsRingPointerLockedCreate(epicsAtomicGetIntT(&callbackQueueSize));
|
||||
if (callbackQueue[i].queue == 0)
|
||||
cantProceed("epicsRingPointerLockedCreate failed for %s\n",
|
||||
threadNamePrefix[i]);
|
||||
@@ -290,6 +336,7 @@ int callbackRequest(CALLBACK *pcallback)
|
||||
if (!pushOK) {
|
||||
epicsInterruptContextMessage(fullMessage[priority]);
|
||||
mySet->queueOverflow = TRUE;
|
||||
epicsAtomicIncrIntT(&mySet->queueOverflows);
|
||||
return S_db_bufFull;
|
||||
}
|
||||
epicsEventSignal(mySet->semWakeUp);
|
||||
|
||||
@@ -48,6 +48,13 @@ typedef epicsCallback CALLBACK;
|
||||
|
||||
typedef void (*CALLBACKFUNC)(struct callbackPvt*);
|
||||
|
||||
typedef struct callbackQueueStats {
|
||||
int size;
|
||||
int numUsed[NUM_CALLBACK_PRIORITIES];
|
||||
int maxUsed[NUM_CALLBACK_PRIORITIES];
|
||||
int numOverflow[NUM_CALLBACK_PRIORITIES];
|
||||
} callbackQueueStats;
|
||||
|
||||
#define callbackSetCallback(PFUN, PCALLBACK) \
|
||||
( (PCALLBACK)->callback = (PFUN) )
|
||||
#define callbackSetPriority(PRIORITY, PCALLBACK) \
|
||||
@@ -73,6 +80,8 @@ epicsShareFunc void callbackCancelDelayed(CALLBACK *pcallback);
|
||||
epicsShareFunc void callbackRequestProcessCallbackDelayed(
|
||||
CALLBACK *pCallback, int Priority, void *pRec, double seconds);
|
||||
epicsShareFunc int callbackSetQueueSize(int size);
|
||||
epicsShareFunc int callbackQueueStatus(const int reset, callbackQueueStats *result);
|
||||
void callbackQueuePrintStatus(const int reset);
|
||||
epicsShareFunc int callbackParallelThreads(int count, const char *prio);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -296,6 +296,17 @@ static void scanOnceSetQueueSizeCallFunc(const iocshArgBuf *args)
|
||||
scanOnceSetQueueSize(args[0].ival);
|
||||
}
|
||||
|
||||
/* scanOnceQueueStatus */
|
||||
static const iocshArg scanOnceQueueStatusArg0 = { "reset",iocshArgInt};
|
||||
static const iocshArg * const scanOnceQueueStatusArgs[1] =
|
||||
{&scanOnceQueueStatusArg0};
|
||||
static const iocshFuncDef scanOnceQueueStatusFuncDef =
|
||||
{"scanOnceQueueStatus",1,scanOnceQueueStatusArgs};
|
||||
static void scanOnceQueueStatusCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
scanOnceQueuePrintStatus(args[0].ival);
|
||||
}
|
||||
|
||||
/* scanppl */
|
||||
static const iocshArg scanpplArg0 = { "rate",iocshArgDouble};
|
||||
static const iocshArg * const scanpplArgs[1] = {&scanpplArg0};
|
||||
@@ -335,6 +346,17 @@ static void callbackSetQueueSizeCallFunc(const iocshArgBuf *args)
|
||||
callbackSetQueueSize(args[0].ival);
|
||||
}
|
||||
|
||||
/* callbackQueueStatus */
|
||||
static const iocshArg callbackQueueStatusArg0 = { "reset", iocshArgInt};
|
||||
static const iocshArg * const callbackQueueStatusArgs[1] =
|
||||
{&callbackQueueStatusArg0};
|
||||
static const iocshFuncDef callbackQueueStatusFuncDef =
|
||||
{"callbackQueueStatus",1,callbackQueueStatusArgs};
|
||||
static void callbackQueueStatusCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
callbackQueuePrintStatus(args[0].ival);
|
||||
}
|
||||
|
||||
/* callbackParallelThreads */
|
||||
static const iocshArg callbackParallelThreadsArg0 = { "no of threads", iocshArgInt};
|
||||
static const iocshArg callbackParallelThreadsArg1 = { "priority", iocshArgString};
|
||||
@@ -441,12 +463,14 @@ void dbIocRegister(void)
|
||||
iocshRegister(&dbLockShowLockedFuncDef,dbLockShowLockedCallFunc);
|
||||
|
||||
iocshRegister(&scanOnceSetQueueSizeFuncDef,scanOnceSetQueueSizeCallFunc);
|
||||
iocshRegister(&scanOnceQueueStatusFuncDef,scanOnceQueueStatusCallFunc);
|
||||
iocshRegister(&scanpplFuncDef,scanpplCallFunc);
|
||||
iocshRegister(&scanpelFuncDef,scanpelCallFunc);
|
||||
iocshRegister(&postEventFuncDef,postEventCallFunc);
|
||||
iocshRegister(&scanpiolFuncDef,scanpiolCallFunc);
|
||||
|
||||
iocshRegister(&callbackSetQueueSizeFuncDef,callbackSetQueueSizeCallFunc);
|
||||
iocshRegister(&callbackQueueStatusFuncDef,callbackQueueStatusCallFunc);
|
||||
iocshRegister(&callbackParallelThreadsFuncDef,callbackParallelThreadsCallFunc);
|
||||
|
||||
/* Needed before callback system is initialized */
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "cantProceed.h"
|
||||
#include "dbDefs.h"
|
||||
#include "ellLib.h"
|
||||
#include "epicsAtomic.h"
|
||||
#include "epicsEvent.h"
|
||||
#include "epicsMutex.h"
|
||||
#include "epicsPrint.h"
|
||||
@@ -63,6 +64,7 @@ static volatile enum ctl scanCtl;
|
||||
static int onceQueueSize = 1000;
|
||||
static epicsEventId onceSem;
|
||||
static epicsRingBytesId onceQ;
|
||||
static int onceQOverruns = 0;
|
||||
static epicsThreadId onceTaskId;
|
||||
static void *exitOnce;
|
||||
|
||||
@@ -676,6 +678,7 @@ int scanOnceCallback(struct dbCommon *precord, once_complete cb, void *usr)
|
||||
if (!pushOK) {
|
||||
if (newOverflow) errlogPrintf("scanOnce: Ring buffer overflow\n");
|
||||
newOverflow = FALSE;
|
||||
epicsAtomicIncrIntT(&onceQOverruns);
|
||||
} else {
|
||||
newOverflow = TRUE;
|
||||
}
|
||||
@@ -722,6 +725,40 @@ int scanOnceSetQueueSize(int size)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int scanOnceQueueStatus(const int reset, scanOnceQueueStats *result)
|
||||
{
|
||||
if (!onceQ) return -1;
|
||||
int ret;
|
||||
if (result) {
|
||||
result->size = epicsRingBytesSize(onceQ) / sizeof(onceEntry);
|
||||
result->numUsed = epicsRingBytesUsedBytes(onceQ) / sizeof(onceEntry);
|
||||
result->maxUsed = epicsRingBytesHighWaterMark(onceQ) / sizeof(onceEntry);
|
||||
result->numOverflow = epicsAtomicGetIntT(&onceQOverruns);
|
||||
ret = 0;
|
||||
} else {
|
||||
ret = -2;
|
||||
}
|
||||
if (reset) {
|
||||
epicsRingBytesResetHighWaterMark(onceQ);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void scanOnceQueuePrintStatus(const int reset)
|
||||
{
|
||||
scanOnceQueueStats stats;
|
||||
if (scanOnceQueueStatus(reset, &stats) == -1) {
|
||||
fprintf(stderr, "scanOnce system not initialized, yet. Please run "
|
||||
"iocInit before using this command.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
printf("PRIORITY HIGH-WATER MARK ITEMS IN Q Q SIZE %% USED Q OVERFLOWS\n");
|
||||
double qusage = 100.0 * stats.numUsed / stats.size;
|
||||
printf("%8s %15d %10d %6d %6.1f %11d\n", "scanOnce", stats.maxUsed, stats.numUsed, stats.size, qusage,
|
||||
epicsAtomicGetIntT(&onceQOverruns));
|
||||
}
|
||||
|
||||
static void initOnce(void)
|
||||
{
|
||||
if ((onceQ = epicsRingBytesLockedCreate(sizeof(onceEntry)*onceQueueSize)) == NULL) {
|
||||
|
||||
@@ -42,6 +42,13 @@ struct dbCommon;
|
||||
typedef void (*io_scan_complete)(void *usr, IOSCANPVT, int prio);
|
||||
typedef void (*once_complete)(void *usr, struct dbCommon*);
|
||||
|
||||
typedef struct scanOnceQueueStats {
|
||||
int size;
|
||||
int numUsed;
|
||||
int maxUsed;
|
||||
int numOverflow;
|
||||
} scanOnceQueueStats;
|
||||
|
||||
epicsShareFunc long scanInit(void);
|
||||
epicsShareFunc void scanRun(void);
|
||||
epicsShareFunc void scanPause(void);
|
||||
@@ -57,6 +64,8 @@ epicsShareFunc double scanPeriod(int scan);
|
||||
epicsShareFunc int scanOnce(struct dbCommon *);
|
||||
epicsShareFunc int scanOnceCallback(struct dbCommon *, once_complete cb, void *usr);
|
||||
epicsShareFunc int scanOnceSetQueueSize(int size);
|
||||
epicsShareFunc int scanOnceQueueStatus(const int reset, scanOnceQueueStats *result);
|
||||
void scanOnceQueuePrintStatus(const int reset);
|
||||
|
||||
/*print periodic lists*/
|
||||
epicsShareFunc int scanppl(double rate);
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "epicsAtomic.h"
|
||||
#include "epicsSpin.h"
|
||||
#include "dbDefs.h"
|
||||
#include "epicsRingBytes.h"
|
||||
@@ -38,6 +39,7 @@ typedef struct ringPvt {
|
||||
volatile int nextPut;
|
||||
volatile int nextGet;
|
||||
int size;
|
||||
int highWaterMark;
|
||||
volatile char buffer[1]; /* actually larger */
|
||||
}ringPvt;
|
||||
|
||||
@@ -47,6 +49,7 @@ epicsShareFunc epicsRingBytesId epicsShareAPI epicsRingBytesCreate(int size)
|
||||
if(!pring)
|
||||
return NULL;
|
||||
pring->size = size + SLOP;
|
||||
pring->highWaterMark = 0;
|
||||
pring->nextGet = 0;
|
||||
pring->nextPut = 0;
|
||||
pring->lock = 0;
|
||||
@@ -131,8 +134,13 @@ epicsShareFunc int epicsShareAPI epicsRingBytesPut(
|
||||
if (pring->lock) epicsSpinUnlock(pring->lock);
|
||||
return 0;
|
||||
}
|
||||
if (nbytes)
|
||||
if (nbytes) {
|
||||
memcpy ((void *)&pring->buffer[nextPut], value, nbytes);
|
||||
int curUsed = pring->size - SLOP - freeCount;
|
||||
if (curUsed > epicsAtomicGetIntT(&pring->highWaterMark)) {
|
||||
epicsAtomicSetIntT(&pring->highWaterMark, curUsed);
|
||||
}
|
||||
}
|
||||
nextPut += nbytes;
|
||||
}
|
||||
else {
|
||||
@@ -143,8 +151,13 @@ epicsShareFunc int epicsShareAPI epicsRingBytesPut(
|
||||
}
|
||||
topCount = size - nextPut;
|
||||
copyCount = (nbytes > topCount) ? topCount : nbytes;
|
||||
if (copyCount)
|
||||
if (copyCount) {
|
||||
memcpy ((void *)&pring->buffer[nextPut], value, copyCount);
|
||||
int curUsed = pring->size - SLOP - freeCount;
|
||||
if (curUsed > epicsAtomicGetIntT(&pring->highWaterMark)) {
|
||||
epicsAtomicSetIntT(&pring->highWaterMark, curUsed);
|
||||
}
|
||||
}
|
||||
nextPut += copyCount;
|
||||
if (nextPut == size) {
|
||||
int nLeft = nbytes - copyCount;
|
||||
@@ -224,3 +237,15 @@ epicsShareFunc int epicsShareAPI epicsRingBytesIsFull(epicsRingBytesId id)
|
||||
{
|
||||
return (epicsRingBytesFreeBytes(id) <= 0);
|
||||
}
|
||||
|
||||
epicsShareFunc int epicsShareAPI epicsRingBytesHighWaterMark(epicsRingBytesIdConst id)
|
||||
{
|
||||
ringPvt *pring = (ringPvt *)id;
|
||||
return epicsAtomicGetIntT(&pring->highWaterMark);
|
||||
}
|
||||
|
||||
epicsShareFunc void epicsShareAPI epicsRingBytesResetHighWaterMark(epicsRingBytesId id)
|
||||
{
|
||||
ringPvt *pring = (ringPvt *)id;
|
||||
epicsAtomicSetIntT(&pring->highWaterMark, epicsRingBytesUsedBytes(id));
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ extern "C" {
|
||||
#include "shareLib.h"
|
||||
|
||||
typedef void *epicsRingBytesId;
|
||||
typedef void const *epicsRingBytesIdConst;
|
||||
|
||||
epicsShareFunc epicsRingBytesId epicsShareAPI epicsRingBytesCreate(int nbytes);
|
||||
/* Same, but secured by a spinlock */
|
||||
@@ -39,6 +40,8 @@ epicsShareFunc int epicsShareAPI epicsRingBytesUsedBytes(epicsRingBytesId id);
|
||||
epicsShareFunc int epicsShareAPI epicsRingBytesSize(epicsRingBytesId id);
|
||||
epicsShareFunc int epicsShareAPI epicsRingBytesIsEmpty(epicsRingBytesId id);
|
||||
epicsShareFunc int epicsShareAPI epicsRingBytesIsFull(epicsRingBytesId id);
|
||||
epicsShareFunc int epicsShareAPI epicsRingBytesHighWaterMark(epicsRingBytesIdConst id);
|
||||
epicsShareFunc void epicsShareAPI epicsRingBytesResetHighWaterMark(epicsRingBytesId id);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -90,3 +90,15 @@ epicsShareFunc int epicsShareAPI epicsRingPointerIsFull(epicsRingPointerId id)
|
||||
voidPointer *pvoidPointer = reinterpret_cast<voidPointer*>(id);
|
||||
return((pvoidPointer->isFull()) ? 1 : 0);
|
||||
}
|
||||
|
||||
epicsShareFunc int epicsShareAPI epicsRingPointerGetHighWaterMark(epicsRingPointerIdConst id)
|
||||
{
|
||||
voidPointer const *pvoidPointer = reinterpret_cast<voidPointer const*>(id);
|
||||
return(pvoidPointer->getHighWaterMark());
|
||||
}
|
||||
|
||||
epicsShareFunc void epicsShareAPI epicsRingPointerResetHighWaterMark(epicsRingPointerId id)
|
||||
{
|
||||
voidPointer *pvoidPointer = reinterpret_cast<voidPointer*>(id);
|
||||
pvoidPointer->resetHighWaterMark();
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
* epicsRingPointerLocked uses a spinlock.
|
||||
*/
|
||||
|
||||
#include "epicsAtomic.h"
|
||||
#include "epicsSpin.h"
|
||||
#include "shareLib.h"
|
||||
|
||||
@@ -40,18 +41,22 @@ public: /* Functions */
|
||||
int getSize() const;
|
||||
bool isEmpty() const;
|
||||
bool isFull() const;
|
||||
int getHighWaterMark() const;
|
||||
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;
|
||||
};
|
||||
|
||||
@@ -59,6 +64,7 @@ extern "C" {
|
||||
#endif /*__cplusplus */
|
||||
|
||||
typedef void *epicsRingPointerId;
|
||||
typedef void const *epicsRingPointerIdConst;
|
||||
|
||||
epicsShareFunc epicsRingPointerId epicsShareAPI epicsRingPointerCreate(int size);
|
||||
/* Same, but secured by a spinlock */
|
||||
@@ -74,6 +80,8 @@ epicsShareFunc int epicsShareAPI epicsRingPointerGetUsed(epicsRingPointerId id)
|
||||
epicsShareFunc int epicsShareAPI epicsRingPointerGetSize(epicsRingPointerId id);
|
||||
epicsShareFunc int epicsShareAPI epicsRingPointerIsEmpty(epicsRingPointerId id);
|
||||
epicsShareFunc int epicsShareAPI epicsRingPointerIsFull(epicsRingPointerId id);
|
||||
epicsShareFunc int epicsShareAPI epicsRingPointerGetHighWaterMark(epicsRingPointerIdConst id);
|
||||
epicsShareFunc void epicsShareAPI epicsRingPointerResetHighWaterMark(epicsRingPointerId id);
|
||||
|
||||
/* This routine was incorrectly named in previous releases */
|
||||
#define epicsRingPointerSize epicsRingPointerGetSize
|
||||
@@ -95,7 +103,8 @@ epicsShareFunc int epicsShareAPI epicsRingPointerIsFull(epicsRingPointerId id);
|
||||
|
||||
template <class T>
|
||||
inline epicsRingPointer<T>::epicsRingPointer(int sz, bool locked) :
|
||||
lock(0), nextPush(0), nextPop(0), size(sz+1), buffer(new T* [sz+1])
|
||||
lock(0), nextPush(0), nextPop(0), size(sz+1), highWaterMark(0),
|
||||
buffer(new T* [sz+1])
|
||||
{
|
||||
if (locked)
|
||||
lock = epicsSpinCreate();
|
||||
@@ -121,6 +130,10 @@ inline bool epicsRingPointer<T>::push(T *p)
|
||||
}
|
||||
buffer[next] = p;
|
||||
nextPush = newNext;
|
||||
int used = getUsedNoLock();
|
||||
if (used > epicsAtomicGetIntT(&highWaterMark)) {
|
||||
epicsAtomicSetIntT(&highWaterMark, used);
|
||||
}
|
||||
if (lock) epicsSpinUnlock(lock);
|
||||
return(true);
|
||||
}
|
||||
@@ -161,12 +174,19 @@ inline int epicsRingPointer<T>::getFree() const
|
||||
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 = nextPush - nextPop;
|
||||
if (n < 0) n += size;
|
||||
int n = getUsedNoLock();
|
||||
if (lock) epicsSpinUnlock(lock);
|
||||
return n;
|
||||
}
|
||||
@@ -196,6 +216,18 @@ inline bool epicsRingPointer<T>::isFull() const
|
||||
return((count == 0) || (count == size));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline int epicsRingPointer<T>::getHighWaterMark() const
|
||||
{
|
||||
return epicsAtomicGetIntT(&highWaterMark);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline void epicsRingPointer<T>::resetHighWaterMark()
|
||||
{
|
||||
epicsAtomicSetIntT(&highWaterMark, getUsed());
|
||||
}
|
||||
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* INCepicsRingPointerh */
|
||||
|
||||
Reference in New Issue
Block a user