diff --git a/modules/database/src/ioc/db/callback.c b/modules/database/src/ioc/db/callback.c index 0e40138e8..cf6a7b467 100644 --- a/modules/database/src/ioc/db/callback.c +++ b/modules/database/src/ioc/db/callback.c @@ -100,6 +100,10 @@ static int priorityValue[NUM_CALLBACK_PRIORITIES] = {0, 1, 2}; int callbackSetQueueSize(int size) { + if (size<=0) { + fprintf(stderr, "Queue size must be positive\n"); + return -1; + } if (epicsAtomicGetIntT(&cbState)!=cbInit) { fprintf(stderr, "Callback system already initialized\n"); return -1; diff --git a/modules/database/src/ioc/db/callback.h b/modules/database/src/ioc/db/callback.h index ac600fd01..3e76eb7b7 100644 --- a/modules/database/src/ioc/db/callback.h +++ b/modules/database/src/ioc/db/callback.h @@ -38,53 +38,169 @@ extern "C" { #define priorityMedium 1 #define priorityHigh 2 +/** Handle for delayed work. + * + * \pre Must be zero initialized prior to first use. + * + * \since 3.15.6 epicsCallback typedef added. CALLBACK typedef deprecated. + */ typedef struct callbackPvt { + /** Callback function */ void (*callback)(struct callbackPvt*); + /** One of priorityLow, priorityMedium, or priorityHigh */ int priority; - void *user; /*for use by callback user*/ - void *timer; /*for use by callback itself*/ + /** for use by callback API user*/ + void *user; + /** Must be zero initialized. Used by callback internals. */ + void *timer; }epicsCallback; #if !defined(EPICS_NO_CALLBACK) +/** Deprecated alias for epicsCallback + * + * Name conflicts with definition from windows.h. + * Portable applications should prefer epicsCallback + * and define the EPICS_NO_CALLBACK pre-processor macro to hide this typedef. + * + * \since 3.15.6 Deprecated in favor of epicsCallback typedef + */ typedef epicsCallback CALLBACK; #endif typedef void (*CALLBACKFUNC)(struct callbackPvt*); - +/** See callbackQueueStatus() */ typedef struct callbackQueueStats { + /** Maxiumum depth of queues */ int size; + /** Current number of elements on each queue */ int numUsed[NUM_CALLBACK_PRIORITIES]; + /** Maximum numUsed seen so far (from init or reset) */ int maxUsed[NUM_CALLBACK_PRIORITIES]; + /** Number of overflow events */ int numOverflow[NUM_CALLBACK_PRIORITIES]; } callbackQueueStats; +/** Assigns callbackPvt::callback */ #define callbackSetCallback(PFUN, PCALLBACK) \ ( (PCALLBACK)->callback = (PFUN) ) +/** Assigns callbackPvt::priority */ #define callbackSetPriority(PRIORITY, PCALLBACK) \ ( (PCALLBACK)->priority = (PRIORITY) ) +/** Assigns callbackPvt::priority */ #define callbackGetPriority(PRIORITY, PCALLBACK) \ ( (PRIORITY) = (PCALLBACK)->priority ) +/** Assigns callbackPvt::user */ #define callbackSetUser(USER, PCALLBACK) \ ( (PCALLBACK)->user = (void *) (USER) ) +/** Read and return callbackPvt::user */ #define callbackGetUser(USER, PCALLBACK) \ ( (USER) = (PCALLBACK)->user ) DBCORE_API void callbackInit(void); DBCORE_API void callbackStop(void); DBCORE_API void callbackCleanup(void); +/** Queue immediate callback request. + * + * Each epicsCallback may be queued multiple times. + * epicsCallback object must not be modified while queued, + * and must remain valid while queued or executing. + * + * \param pCallback Caller expected to initialize or zero all members before first call. + * \return Zero on success. + * Errors if callback members not initialized correctly, or if queue is full. + */ DBCORE_API int callbackRequest(epicsCallback *pCallback); +/** Setup callback to process a record + * \pre Callback must be zero initialized. + * + * \param pcallback Callback to initialize. + * \param Priority priorityLow, priorityMedium, or priorityHigh + * \param pRec A record pointer (dbCommon or specific recordType) + */ DBCORE_API void callbackSetProcess( epicsCallback *pcallback, int Priority, void *pRec); +/** (Re)Initialize callback object and queue + * + * Shorthand for callbackSetProcess() followed by callbackRequest() + * + * \pre Callback object must be zero initialized before first call. + */ DBCORE_API int callbackRequestProcessCallback( epicsCallback *pCallback,int Priority, void *pRec); +/** Queue delayed callback request + * + * Each epicsCallback has a single timer. + * Repeated calls before expiration will cancel and reschedule timer. + * epicsCallback object must not be modified while queued, + * and must remain valid while queued or executing. + * + * \param pCallback Callback object. Caller expected to initialize or zero all members. + * \param seconds Relative to call time. Expected to be >= 0. + * \return Zero on success. + * Errors if callback members not initialized correctly, or if queue is full. + */ DBCORE_API void callbackRequestDelayed( epicsCallback *pCallback,double seconds); +/** Cancel delayed callback. + * + * Usage not recommended. Caller can not distinguish between successful + * cancellation, or expiration. In the later case the callback may still be + * queued or executing. + * + * \param pcallback Callback object previously passed to callbackRequestDelayed() + * + * \post Timer is cancelled. However, callback may be queued or executing. + */ DBCORE_API void callbackCancelDelayed(epicsCallback *pcallback); +/** (Re)Initialize callback object and queue + * + * Shorthand for callbackSetProcess() followed by callbackRequestDelayed() + * + * \pre Callback object must be zero initialized before first call. + */ DBCORE_API void callbackRequestProcessCallbackDelayed( epicsCallback *pCallback, int Priority, void *pRec, double seconds); +/** Set callback queue depth + * + * \param size A positive integer + * \return -1 if too late to change depth + * + * \pre Must be called before iocInit() + */ DBCORE_API int callbackSetQueueSize(int size); +/** Query configuration and statistics from callback system + * \param reset If non-zero, reset maxUsed after reading. + * \param result NULL, or location for results + * \return -2 if result is NULL. reset happens anyway. + * + * \since 7.0.2. Also present in 3.16.2 + */ DBCORE_API int callbackQueueStatus(const int reset, callbackQueueStats *result); DBCORE_API void callbackQueueShow(const int reset); +/** Setup multiple worker threads for specified priority + * + * By default, only one thread is run for each priority (3 in total). + * + * Calling with count==0 will take the count from the callbackParallelThreadsDefault + * global variable (default default is 2). + * Calling with count>0 sets the number of worker threads directly. + * Calling with count<0 computes the count based on the number of CPU cores on the host. + * eg. Passing -2 on an 8 core system will start 6 worker threads. + * In all cases, at least one worker thread will always run. + * + * A special prio name of "*" will modify all priorities. + * Otherwise, only the named priority is modified. + * + * \param count If zero, reset to default (callbackParallelThreadsDefault global/iocsh variable). + * If positive, exact number of worker threads to create. + * If negative, number of worker threads less than core count. + * \param prio Priority name. eg. "*", "LOW", "MEDIUM" or "HIGH". + * \return zero on success, non-zero if called after iocInit() or with invalid arguments. + * + * \pre Must be called before iocInit() + * + * \since 3.15.0.2 + */ DBCORE_API int callbackParallelThreads(int count, const char *prio); #ifdef __cplusplus diff --git a/modules/libcom/src/env/envDefs.h b/modules/libcom/src/env/envDefs.h index ae03ec1d7..a768d13c8 100644 --- a/modules/libcom/src/env/envDefs.h +++ b/modules/libcom/src/env/envDefs.h @@ -98,10 +98,13 @@ struct in_addr; * is set to '\0' and NULL is returned. * * \param pParam Pointer to config param structure. - * \param bufDim Dimension of parameter buffer + * \param bufDim Dimension of parameter buffer. + * Must be greater than zero. * \param pBuf Pointer to parameter buffer * \return Pointer to the environment variable value string, or * NULL if no parameter value and default value was empty. + * + * \post A terminating nil will be written to pBuf. */ LIBCOM_API char * epicsStdCall envGetConfigParam(const ENV_PARAM *pParam, int bufDim, char *pBuf);