next up previous contents index
Next: 20. libCom OSI libraries Up: AppDevGuide Previous: 18. IOC Shell   Contents   Index


19. libCom

This chapter and the next describe the facilities provided in <base>/src/libCom. This chapter describes facilities which are platform independent. The next chapter describes facilities which have different implementations on different platforms.

19.1 bucketLib

bucketLib.h describes a hash facility for integers, pointers, and strings. It is used by the Channel Access Server. It is currently undocumented.

19.2 calc

postfix.h defines several macros and the routines used by the calculation record type calcRecord, access security, and other code, to compile and evaluate mathematical expressions. The syntax of the infix expressions accepted is described below.

long postfix(const char *psrc, char *ppostfix, short *perror);
long calcArgUsage(const char *ppostfix, unsigned long *pinputs,
    unsigned long *pstores);
const char * calcErrorStr(short error);
long calcPerform(double *parg, double *presult, const char *ppostfix);

The postfix() routine converts an expression from infix to postfix notation. It is the callers's responsibility to make sure that ppostfix points to sufficient storage to hold the postfix expression; the macro INFIX_TO_POSTFIX_SIZE(n) can be used to calculate an appropriate buffer from the length of the infix string. There is no longer a maximum length to the input expression that can be accepted, although there are internal limits to the complexity of the expressions that can be converted and evaluated. If postfix() returns a non-zero value it will have placed an error code at the location pointed to by perror. The error codes used are defined in postfix.h as a series of macros with names starting CALC_ERR_, but a string representation of the error code is more useful and can be obtained by passing the value to the calcErrorStr() routine, which returns a static error message string explaining the error.

Software using the calc subsystem may need to know what expression arguments are used and/or modified by a particular expression. It can discover this from the postfix string by calling calcArgUsage(), which takes two pointers pinputs and pstores to a pair of unsigned long bitmaps which return that information to the caller. Passing a NULL value for either of these pointers is legal if only the other is needed. The least signficant bit (bit 0) of the bitmap at *pinputs will be set if the expression depends on the argument A, and so on through bit 11 for the argument L. Similarly, bit 0 of the bitmap at *pstores will be set if the expression assigns a value to the argument A. An argument that is not used until after a value has been assigned to it will not be set in the pinputs bitmap, thus the bits can be used to determine whether a value needs to be supplied for their associated argument or not for the purposes of evaluating the expression. The return value from calcArgUsage() will be non-zero if the ppostfix expression was illegal, otherwise 0.

The postfix expression is evaluated by calling the calcPerform() routine, which returns the status values 0 for OK, or non-zero if an error is discovered during the evaluation process.

The arguments to calcPerform() are:

parg - Pointer to an array of double values for the arguments A-L that can appear in the expression. Note that the argument values may be modified if the expression uses the assignment operator.

presult - Where to put the calculated result, which may be a NaN or Infinity.

ppostfix - The postfix expression created by postfix().

19.2.1 Infix Expression Syntax

The infix expressions that can be used are very similar to the C expression syntax, but with some additions and subtle differences in operator meaning and precedence. The string may contain a series of expressions separated by a semi-colon character `;' any one of which may actually provide the calculation result; however all of the other expressions included must assign their result to a variable. All alphabetic elements described below are case independent, so upper and lower case letters may be used and mixed in the variable and function names as desired. Spaces may be used anywhere within an expression except between the characters that make up a single expression element. Numeric Literals

The simplest expression element is a numeric literal, any (positive) number expressed using the standard floating point syntax that can be stored as a double precision value. This now includes the values Infinity and NaN (not a number). Note that negative numbers will be encoded as a positive literal to which the unary negate operator is applied.


Inf Constants

There are three trigonometric constants available to any expression which return a value: Variables

Variables are used to provide inputs to an expression, and are named using the single letters A through L inclusive or the keyword VAL which refers to the previous result of this calculation. The software that makes use of the expression evaluation code should document how the individual variables are given values; for the calc record type the input links INPA through INPL can be used to obtain these from other record fields, and VAL refers to the the VAL field (which can be overwritten from outside the record via Channel Access or a database link). Variable Assignment Operator

Recently added is the ability to assign the result of a sub-expression to any of the single letter variables, which can then be used in another sub-expression. The variable assignment operator is the character pair := and must immediately follow the name of the variable to receive the expression value. Since the infix string must return exactly one value, every expression string must have exactly one sub-expression that is not an assignment, which can appear anywhere in the string. Sub-expressions within the string are separated by a semi-colon character.


B; B:=A
i:=i+1; a*sin(i*D2R) Arithmetic Operators

The usual binary arithmetic operators are provided: + - * and / with their usual relative precedence and left-to-right associativity, and - may also be used as a unary negate operator where it has a higher precedence and associates from right to left. There is no unary plus operator, so numeric literals cannot begin with a + sign.


a*b + c
a/-4 - b

Three other binary operators are also provided: % is the integer modulo operator, while the synonymous operators ** and ^ raise their left operand to the power of the right operand. % has the same precedence and associativity as * and /, while the power operators associate left-to-right and have a precedence in between * and unary minus.


e:=a%10; d:=a/10%10; c:=a/100%10; b:=a/1000%10; b*4096+c*256+d*16+e
sqrt(a**2 + b**2) Algebraic Functions

Various algebraic functions are available which take parameters inside parentheses. The parameter seperator is a comma. Trigonometric Functions

Standard circular trigonometric functions, with angles expressed in radians: Hyperbolic Trigonometry

The basic hyperbolic functions are provided, but no inverse functions (which are not provided by the ANSI C math library either). Numeric Functions

The numeric functions perform operations related to the floating point numeric representation and truncation or rounding. Boolean Operators

These operators regard their arguments as true or false, where 0.0 is false and any other value is true. Bitwise Operators

The bitwise operators convert their arguments to an integer (by truncation), perform the appropriate bitwise operation and convert back to a floating point value. Unlike in C though, ^ is not a bitwise exclusive-or operator. Relational Operators

Standard numeric comparisons between two values: Conditional Operator

Expressions can use the C conditional operator, which has a lower precedence than all of the other operators except for the assignment operator.


a < 360 ? a+1 : 0 Parentheses

Sub-expressions can be placed within parentheses to override operator precence rules. Parentheses can be nested to any depth, but the intermediate value stack used by the expression evaluation engine is limited to 80 results (which requirea an expression at least 321 characters long to reach).

19.3 cppStd

This subdirectory of libCom is intended for facilities such as class and function templates that implement parts of the ISO standard C++ library where such facilities are not available or not efficient on all the target platforms on which EPICS is supported. EPICS does not make use of the C++ container templates because the large number of memory allocation and deletion operations that these use causes memory pool fragmentation on some platforms, threatening the lifetime of an individual IOC.

19.3.1 epicsAlgorithm

epicsAlgorithm.h contains a few templates that are also available in the C++ standard header algorithm, but are provided here in a much smaller file. algorithm contains many templates for sorting and searching through C++ template containers which are not used in EPICS. If all you need from there is std::min(), std::max() and/or std::swap() your code may compile faster if you include epicsAlgorithm.h and use epicsMin(), epicsMax() and epicsSwap() instead.

Template Function Meaning
epicsMin(a, b) Returns the smaller of a or b compared using a<b. Handles NaNs correctly.
epicsMax(a, b) Returns the larger of a or b compared using a<b. Handles NaNa correctly.
epicsSwap(a, b) Swaps the values of a and b; the data type must support both copy-construction and assignment.

19.4 epicsExit

void epicsExit(int status);
void epicsExitCallAtExits(void);
void epicsAtExit(void (*epicsExitFunc)(void *arg), void *arg);
void epicsExitCallAtThreadExits(void);
int  epicsAtThreadExit(void (*epicsExitFunc)(void *arg), void *arg);

This is an extended replacement for the Posix exit and atexit routines, which also provides a pointer argument to pass to the exit handlers. This facility was created because of problems on vxWorks and windows with the implementation of atexit, i.e. neither of these systems implement exit and atexit according to the POSIX standard.

Method Meaning
epicsExit This calls epicsExitCallAtExits and then passes status on to exit.
epicsExitCallAtExits This calls each of the functions registered by prior calls to epicsAtExit, in reverse order of their registration. Most applications will not call this routine directly.
epicsAtExit Register a function and an associated context parameter, to be called with the given parameter when epicsExitCallAtExits is invoked.
epicsExitCallAtThreadExits This calls each of the functions that were registered by the current thread calling epicsAtThreadExit, in reverse order of the function registration. This routine is called automatically when an epicsThread's main entry method returns, but will not be run if the thread is stopped by other means.
epicsAtThreadExit Register a function and an associated context parameter. The function will be called with the given parameter when epicsExitCallAtThreadExits is invoked by the current thread ending normally, i.e. when the thread function returns.

19.5 cvtFast

cvtFast.h provides routines for converting various numeric types into an ascii string. They offer a combination of speed and convenience not available with sprintf().

/* These functions return the number of ASCII characters generated */
int cvtFloatToString(float value, char *pstr, unsigned short precision);
int cvtDoubleToString(double value, char *pstr, unsigned short prec);
int cvtFloatToExpString(float value, char *pstr, unsigned short prec);
int cvtDoubleToExpString(double value, char *pstr, unsigned short prec);
int cvtFloatToCompactString(float value, char *pstr, unsigned short prec);
int cvtDoubleToCompactString(double value, char *pstr, unsigned short prec);
int cvtCharToString(char value, char *pstring);
int cvtUcharToString(unsigned char value, char *pstr);
int cvtShortToString(short value, char *pstr);
int cvtUshortToString(unsigned short value, char *pstr);
int cvtLongToString(epicsInt32 value, char *pstr);
int cvtUlongToString(epicsUInt32 value, char *pstr);
int cvtLongToHexString(epicsInt32 value, char *pstr);
int cvtLongToOctalString(epicsInt32 value, char *pstr);
unsigned long cvtBitsToUlong(
        epicsUInt32 src,
        unsigned bitFieldOffset,
        unsigned bitFieldLength);
unsigned long cvtUlongToBits(
        epicsUInt32 src,
        epicsUInt32 dest,
        unsigned bitFieldOffset,
        unsigned bitFieldLength);

19.6 cxxTemplates

This directory contains the following C++ template headers:

Currently these are only being used by Channel Access Clients and the portable Channel Access Server. It has not been decided if any of these will remain in libCom.

19.7 dbmf

dbmf.h (Database Macro/Free) describes a facility that prevents memory fragmentation when memory is allocated and then freed a short time later.

Routines within iocCore like dbLoadDatabase() have the following attributes:

In some environments, e.g. vxWorks 5.x, this behavior causes severe memory fragmentation.

The dbmf facility stops the memory fragmentation. It should NOT be used by code that allocates storage and then keeps it for a considerable period of time before releasing. Such code can use the freeList library described below.

int dbmfInit(size_t size, int chunkItems);
void *dbmfMalloc(size_t bytes);
void dbmfFree(void* bytes);
void dbmfFreeChunks(void);
int dbmfShow(int level);

Routine Meaning
dbmfInit() Initialize the facility. Each time malloc() must be called size*chunkItems bytes are allocated. size is the maximum size request from dbmfMalloc() that will be allocated from the dbmf pool. If dbmfInit() was not called before one of the other routines then it is automatically called with size=64 and chuckItems=10.
dbmfMalloc() Allocate memory. If bytes is > size then malloc() is used to allocate the memory.
dbmfFree() Free the memory allocated by dbmfMalloc().
dbmfFreeChunks() Free all chunks that have contain only free items.
dbmfShow() Show the status of the dbmf memory pool.

19.8 ellLib

ellLib.h describes a double linked list library. It provides functionality similar to the vxWorks lstLib library. See the vxWorks documentation for details. There is an ellXXX() routine to replace most vxWorks lstXXX() routines.

typedef struct ELLNODE {
  struct ELLNODE  *next;
  struct ELLNODE  *previous;

typedef void (*FREEFUNC)(void *);

typedef struct ELLLIST {
  ELLNODE  node;
  int   count;
void ellInit (ELLLIST *pList);
int ellCount (ELLLIST *pList);
ELLNODE *ellFirst (ELLLIST *pList);
ELLNODE *ellLast (ELLLIST *pList);
ELLNODE *ellNext (ELLNODE *pNode);
ELLNODE *ellPrevious (ELLNODE *pNode);
void ellAdd (ELLLIST *pList, ELLNODE *pNode);
void ellConcat (ELLLIST *pDstList, ELLLIST *pAddList);
void ellDelete (ELLLIST *pList, ELLNODE *pNode);
void ellExtract (ELLLIST *pSrcList, ELLNODE *pStartNode,
    ELLNODE *pEndNode, ELLLIST *pDstList);
ELLNODE *ellGet (ELLLIST *pList);
void ellInsert (ELLLIST *plist, ELLNODE *pPrev, ELLNODE *pNode);
ELLNODE *ellNth (ELLLIST *pList, int nodeNum);
ELLNODE *ellNStep (ELLNODE *pNode, int nStep);
int ellFind (ELLLIST *pList, ELLNODE *pNode);
void ellFree2 (ELLLIST *pList, FREEFUNC freeFunc);
void ellFree (ELLLIST *pList);    // Use only if freeFunc is free()
void ellVerify (ELLLIST *pList);

19.9 epicsRingBytes

epicsRingBytes.h describes a C facility for a commonly used type of ring buffer.

19.9.1 C interface

EpicsRingBytes provides methods for creating and using ring buffers (first in first out circular buffers) that store bytes. The unlocked variant 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.

epicsRingBytesId epicsRingBytesCreate(int nbytes);
epicsRingBytesId epicsRingBytesLockedCreate(int nbytes);
void epicsRingBytesDelete(epicsRingBytesId id);
int epicsRingBytesGet(epicsRingBytesId id, char *value,int nbytes);
int epicsRingBytesPut(epicsRingBytesId id, char *value,int nbytes);
void epicsRingBytesFlush(epicsRingBytesId id);
int epicsRingBytesFreeBytes(epicsRingBytesId id);
int epicsRingBytesUsedBytes(epicsRingBytesId id);
int epicsRingBytesSize(epicsRingBytesId id);
int epicsRingBytesIsEmpty(epicsRingBytesId id);
int epicsRingBytesIsFull(epicsRingBytesId id);

Method Meaning
epicsRingBytesCreate() Create a new ring buffer of size nbytes. The returned epicsRingBytesId is passed to the other ring methods.
epicsRingBytesLockedCreate() Same as epicsRingBytesCreate, but create the spin lock secured variant of the ring buffer.
epicsRingBytesDelete() Delete the ring buffer and free any associated memory.
epicsRingBytesGet() Move up to nbytes from the ring buffer to value. The number of bytes actually moved is returned.
epicsRingBytesPut() Move nbytes from value to the ring buffer if there is enough free space available to hold them. The number of bytes actually moved is returned, which will be zero if insufficient space exists.
epicsRingBytesFlush() Make the ring buffer empty.
epicsRingBytesFreeBytes() Return the number of free bytes in the ring buffer.
epicsRingBytesUsedBytes() Return the number of bytes currently stored in the ring buffer.
epicsRingBytesSize() Return the size of the ring buffer, i.e., nbytes specified in the call to epicsRingBytesCreate().
epicsRingBytesIsEmpty() Return (true, false) if the ring buffer is currently empty.
epicsRingBytesIsFull() Return (true, false) if the ring buffer is currently empty.

epicsRingBytes has the following properties:

19.10 epicsRingPointer

epicsRingPointer.h describes a C++ and a C facility for a commonly used type of ring buffer.

19.10.1 C++ Interface

EpicsRingPointer provides methods for creating and using ring buffers (first in first out circular buffers) that store pointers. The unlocked variant 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.

template <class T>
class epicsRingPointer {
    epicsRingPointer(int size, bool locked);
    bool push(T *p);
    T* pop();
    void flush();
    int getFree() const;
    int getUsed() const;
    int getSize() const;
    bool isEmpty() const;
    bool isFull() const;

private: // Prevent compiler-generated member functions
    // default constructor, copy constructor, assignment operator
    epicsRingPointer(const epicsRingPointer &);
    epicsRingPointer& operator=(const epicsRingPointer &);

private: // Data

An epicsRingPointer cannot be assigned to, copy-constructed, or constructed without giving the size argument. The C++ compiler will object to some of the statements below:

epicsRingPointer rp0();   // Error: default constructor is private
epicsRingPointer rp1(10); // OK
epicsRingPointer rp2(t1); // Error: copy constructor is private
epicsRingPointer *prp;    // OK, pointer
*prp = rp1;               // Error: assignment operator is private
prp = &rp1;               // OK, pointer assignment and address-of

Method Meaning
epicsRingPointer() Constructor. The size is the maximum number of elements (pointers) that can be stored in the ring. If locked is true, the spin lock secured variant is created.
~epicsRingPointer() Destructor.
push() Push a new entry on the ring. It returns (false,true) is (failure, success). Failure means the ring was full.
pop() Take a element off the ring. It returns 0 (null) if the ring was empty.
flush() Remove all elements from the ring. If this operation is performed on a ring buffer of the unsecured variant, all access to the ring should be locked.
getFree() Return the amount of empty space in the ring, i.e. how many additional elements it can hold.
getUsed() Return the number of elements stored on the ring
getSize() Return the size of the ring, i.e. the value of size specified when the ring was created.
isEmpty() Returns true if the ring is empty, else false.
isFull() Returns true if the ring is full, else false.

19.10.2 C interface

typedef void *epicsRingPointerId;
    epicsRingPointerId epicsRingPointerCreate(int size);
    epicsRingPointerId epicsRingPointerLockedCreate(int size);
    void epicsRingPointerDelete(epicsRingPointerId id);
    /*epicsRingPointerPop returns 0 if the ring was empty */
    void * epicsRingPointerPop(epicsRingPointerId id) ;
    /*epicsRingPointerPush returns (0,1) if p (was not, was) put on ring*/
    int epicsRingPointerPush(epicsRingPointerId id,void *p);
    void epicsRingPointerFlush(epicsRingPointerId id);
    int epicsRingPointerGetFree(epicsRingPointerId id);
    int epicsRingPointerGetUsed(epicsRingPointerId id);
    int epicsRingPointerGetSize(epicsRingPointerId id);
    int epicsRingPointerIsEmpty(epicsRingPointerId id);
    int epicsRingPointerIsFull(epicsRingPointerId id);

Each C function corresponds to one of the C++ methods.

epicsRingPointerCreate() creates the unsecured variant, epicsRingPointerLockedCreate() creates the spin lock secured variant of the ring buffer.

19.11 epicsTimer

epicsTimer.h describes a C++ and a C timer facility.

19.11.1 C++ Interface epicsTimerNotify and epicsTimer

class epicsTimerNotify {
    enum restart_t { noRestart, restart };
    class expireStatus {
        expireStatus ( restart_t );
        expireStatus ( restart_t, const double &expireDelaySec );
        bool restart () const;
        double expirationDelay () const;
        double delay;
    virtual ~epicsTimerNotify ();
    // return noRestart OR return expireStatus ( restart, 30.0 /* sec */ );
    virtual expireStatus expire ( const epicsTime & currentTime ) = 0;
    virtual void show ( unsigned int level ) const;

class epicsTimer {
    virtual void destroy () = 0; // requires existence of timer queue
    virtual void start ( epicsTimerNotify &, const epicsTime & ) = 0;
    virtual void start ( epicsTimerNotify &, double delaySeconds ) = 0;
    virtual void cancel () = 0;
    struct expireInfo {
        expireInfo ( bool active, const epicsTime & expireTime );
        bool active;
        epicsTime expireTime;
    virtual expireInfo getExpireInfo () const = 0;
    double getExpireDelay ();
    virtual void show ( unsigned int level ) const = 0;
    virtual ~epicsTimer () = 0; // use destroy
Method Meaning
epicsTimerNotify:: expire() Code using an epicsTimer must include a class that inherits from epicsTimerNotify. The derived class must implement the method expire(), which is called by the epicsTimer when the associated timer expires. epicsTimerNotify defines a class expireStatus which makes it easy to implement both one shot and periodic timers. A one-shot expire() returns with the statement return(noRestart); A periodic timer returns with a statement like return(restart,10.0); where is second argument is the delay until the next callback.
epicsTimer epicsTimer is an abstract base class. An epics timer can only be created by calling createTimer, which is a method of epicsTimerQueue.
destroy This is provided instead of a destructor. This will automatically call cancel before freeing all resources used by the timer.
start() Starts the timer to expire either at the specified time or the specified number of seconds in the future. If the timer is already active when start is called, it is first canceled.
cancel() If the timer is scheduled, cancel it. If it is not scheduled do nothing. Note that if the expire() method is already running, this call delays until the expire() completes.
getExpireInfo Get expireInfo, which says if timer is active and if so when it expires.
getExpireDelay() Return the number of seconds until the timer will expire. If the timer is not active it returns DBL_MAX
show() Display info about object. epicsTimerQueue

class epicsTimerQueue {
    virtual epicsTimer & createTimer () = 0;
    virtual void show ( unsigned int level ) const = 0;
    virtual ~epicsTimerQueue () = 0;
Method Meaning
createTimer() This is a ``factory" method to create timers which use this queue.
show() Display info about object epicsTimerQueueActive

class epicsTimerQueueActive : public epicsTimerQueue {
    static epicsTimerQueueActive & allocate (
        bool okToShare, unsigned threadPriority = epicsThreadPriorityMin + 10 );
    virtual void release () = 0;
    virtual ~epicsTimerQueueActive () = 0;

Method Meaning
allocate() This is a ``factory" method to create a timer queue. If okToShare is (true,false) then a (shared, separate) thread will manage the timer requests. If the okToShare constructor parameter is true and a timer queue is already running at the specified priority then it will be referenced for shared use by the application, and an independent timer queue will not be created. This method should not be called from within a C++ static constructor, since the queue thread requires that a current time provider be available and the last-resort time provider is not guaranteed to have been registered until all constructors have run. Editorial note: It is useful for two independent timer queues to run at the same priority if there are multiple processors, or if there is an application with well behaved timer expire functions that needs to be independent of applications with computationally intensive, mutex locking, or IO blocking timer expire functions.
release() Release the queue, i.e. the calling facility will no longer use the queue. The caller MUST ensure that it does not own any active timers. When the last facility using the queue calls release, all resources used by the queue are freed. epicsTimerQueueNotify and epicsTimerQueuePassive

These two classes manage a timer queue for single threaded applications. Since it is single threaded, the application is responsible for requesting that the queue be processed.

class epicsTimerQueueNotify {
    // called when a new timer is inserted into the queue and the
    // delay to the next expire has changed
    virtual void reschedule () = 0;
    // if there is a quantum in the scheduling of timer intervals
    // return this quantum in seconds. If unknown then return zero.
    virtual double quantum () = 0;
    virtual ~epicsTimerQueueNotify () = 0;

class epicsTimerQueuePassive {
    static epicsTimerQueuePassive & create ( epicsTimerQueueNotify & );
    virtual ~epicsTimerQueuePassive () = 0;
    // process returns the delay to the next expire
    virtual double process (const epicsTime & currentTime) = 0;

Method Meaning
epicsTimerQueueNotify:: reschedule() The virtual function epicsTimerQueueNotify::reschedule() is called when the delay to the next timer to expire on the timer queue changes.
epicsTimerQueueNotify:: quantum The virtual function epicsTimerQueueNotify::quantum() returns the timer expire interval scheduling quantum in seconds. This allows different types of timer queues to use application specific timer expire delay scheduling policies. The implementation of epicsTimerQueueActive employs epicsThreadSleep() for this purpose, and therefore epicsTimerQueueActive::quantum() returns the returned value from epicsThreadSleepQuantum(). Other types of timer queues might choose to schedule timer expiration using specialized hardware interrupts. In this case epicsTimerQueueNotify::quantum() might return a value reflecting the precision of a hardware timer. If unknown, then epicsTimerQueueNotify::quantum() should return zero.
epicsTimerQueuePassive epicsTimerQueuePassive is an abstract base class so cannot be instantiated directly, but contains a static member function to create a concrete passive timer queue object of a (hidden) derived class.
create() A ``factory" method to create a non-threaded timer queue. The calling software also passes an object derived from epicsTimerQueueNotify to receive reschedule() callbacks.
~epicsTimerQueuePassive() Destructor. The caller MUST ensure that it does not own any active timers, i.e. it must cancel any active timers before deleting the epicsTimerQueuePassive object.
process() This calls expire() for all timers that have expired. The facility that creates the queue MUST call this. It returns the delay until the next timer will expire.

19.11.2 C Interface

typedef struct epicsTimerForC * epicsTimerId;
typedef void ( *epicsTimerCallback ) ( void *pPrivate );

/* thread managed timer queue */
typedef struct epicsTimerQueueActiveForC * epicsTimerQueueId;
epicsTimerQueueId epicsTimerQueueAllocate(
    int okToShare, unsigned int threadPriority );
void epicsTimerQueueRelease ( epicsTimerQueueId );
epicsTimerId epicsTimerQueueCreateTimer ( epicsTimerQueueId queueid,
        epicsTimerCallback callback, void *arg );
void epicsTimerQueueDestroyTimer ( epicsTimerQueueId queueid, epicsTimerId id );
void epicsTimerQueueShow ( epicsTimerQueueId id, unsigned int level );

/* passive timer queue */
typedef struct epicsTimerQueuePassiveForC * epicsTimerQueuePassiveId;
typedef void ( *epicsTimerQueueNotifyReschedule ) ( void *pPrivate );
typedef double ( * epicsTimerQueueNotifyQuantum ) ( void * pPrivate );
epicsTimerQueuePassiveId epicsTimerQueuePassiveCreate(
    void *pPrivate );
void epicsTimerQueuePassiveDestroy ( epicsTimerQueuePassiveId );
epicsTimerId epicsTimerQueuePassiveCreateTimer (epicsTimerQueuePassiveId queueid,
     epicsTimerCallback pCallback, void *pArg );
void epicsTimerQueuePassiveDestroyTimer (
    epicsTimerQueuePassiveId queueid,epicsTimerId id );
double epicsTimerQueuePassiveProcess ( epicsTimerQueuePassiveId );
void epicsTimerQueuePassiveShow(epicsTimerQueuePassiveId id,unsigned int level);
/* timer */
void epicsTimerStartTime(epicsTimerId id, const epicsTimeStamp *pTime);
void epicsTimerStartDelay(epicsTimerId id, double delaySeconds);
void epicsTimerCancel ( epicsTimerId id );
double epicsTimerGetExpireDelay ( epicsTimerId id );
void epicsTimerShow ( epicsTimerId id, unsigned int level );

The C interface provides most of the facilities as the C++ interface. It does not support the periodic timer features. The typedefs epicsTimerQueueNotifyReschedule and epicsTimerQueueNotifyQuantum are the ``C" interface equivalents to epicsTimerQueueNotify:: reschedule() and epicsTimerQueueNotify::quantum().

19.11.3 Example

This example allocates a timer queue and two objects which have a timer that uses the queue. Each object is requested to schedule itself. The expire() callback just prints the name of the object. After scheduling each object the main thread just sleeps long enough for each expire to occur and then just returns after releasing the queue.

#include <stdio.h>
#include "epicsTimer.h"

class something : public epicsTimerNotify {
    something(const char* nm,epicsTimerQueueActive &queue)
    : name(nm), timer(queue.createTimer()) {}
    virtual ~something() { timer.destroy();}
    void start(double delay) {timer.start(*this,delay);}
    virtual expireStatus expire(const epicsTime & currentTime) {
    const char* name;
    epicsTimer &timer;

void epicsTimerExample()
    epicsTimerQueueActive &queue = epicsTimerQueueActive::allocate(true);
        something first("first",queue);
        something second("second",queue);


19.11.4 C Example

This example shows how C programs can use EPICS timers.

#include <stdio.h>
#include <epicsTimer.h>
#include <epicsThread.h>

static void
handler (void *arg)
    printf ("%s timer tripped.\n", (char *)arg);

main(int argc, char **argv)
    epicsTimerQueueId timerQueue;
    epicsTimerId first, second;

     * Create the queue of timer requests
    timerQueue = epicsTimerQueueAllocate(1,epicsThreadPriorityScanHigh);

     * Create the timers
    first = epicsTimerQueueCreateTimer(timerQueue, handler, "First");
    second = epicsTimerQueueCreateTimer(timerQueue, handler, "Second");

     * Start a timer
    printf("First timer should trip in 3 seconds.\n");
    epicsTimerStartDelay(first, 3.0);
    printf("First timer should have tripped by now.\n");

     * Try starting and then cancelling a request
    printf("Second timer should trip in 3 seconds.\n");
    epicsTimerStartDelay(first, 3.0);
    epicsTimerStartDelay(second, 3.0);
    printf("Second timer should have tripped, first timer should not have 

     * Clean up a single timer
    epicsTimerQueueDestroyTimer(timerQueue, first);

     *  Clean up an entire queue of timers
    return 0;

19.12 fdmgr

File Descriptor Manager. fdManager.h describes a C++ implementation. fdmgr.h describes a C implementation. Neither is currently documented.

19.13 freeList

freeList.h describes routines to allocate and free fixed size memory elements. Free elements are maintained on a free list rather then being returned to the heap via calls to free. When it is necessary to call malloc(), memory is allocated in multiples of the element size.

void freeListInitPvt(void **ppvt, int size, int nmalloc);
void *freeListCalloc(void *pvt);
void *freeListMalloc(void *pvt);
void freeListFree(void *pvt, void*pmem);
void freeListCleanup(void *pvt);
size_t freeListItemsAvail(void *pvt);


pvt - For internal use by the freelist library. Caller must provide storage for a ``void *pvt"

size - Size in bytes of each element. Note that all elements must be same size

nmalloc - Number of elements to allocate when regular malloc() must be called.

19.14 gpHash

gpHash.h describes a general purpose hash table for character strings. The hash table contains tableSize entries. Each entry is a list of members that hash to the same value. The user can maintain separate directories which share the same table by having a different pvt value for each directory.

typedef struct{
    ELLNODE     node;
    const char  *name;          /*address of name placed in directory*/
    void        *pvtid;         /*private name for subsystem user*/
    void        *userPvt;       /*private for user*/

struct gphPvt;

/*tableSize must be power of 2 in range 256 to 65536*/
void gphInitPvt(struct gphPvt **ppvt, int tableSize);
GPHENTRY *gphFind(struct gphPvt *pvt, const char *name, void *pvtid);
GPHENTRY *gphAdd(struct gphPvt *pvt, const char *name, void *pvtid);
void gphDelete(struct gphPvt *pvt, const char *name, void *pvtid);
void gphFreeMem(struct gphPvt *pvt);
void gphDump(struct gphPvt *pvt);
void gphDumpFP(FILE *fp, struct gphPvt *pvt);


pvt - For internal use by the gpHash library. Caller must provide storage for a struct gphPvt *pvt

name - The character string that will be hashed and added to table.

pvtid - The name plus the value of this pointer constitute a unique entry.

19.15 logClient

Together with the program iocLogServer this provides generic support for logging text messages from an IOC or other program to a file on the log server host machine.

A log client runs on the IOC. It accepts string messages and forwards them over a TCP connection to its designated log server (normally running on a host machine).

A log server accepts connections from multiple clients and writes the messages it receives into a rotating file. A log server program ('iocLogServer') is also part of EPICS base.

Configuration of the iocLogServer, as well as the standard iocLogClient that internally uses this library, are described in Section 10.7.

The header file logClient.h exports the following types and routines:

typedef void *logClientId;

An abstract data type, representing a log client.

logClientId logClientCreate (
    struct in_addr server_addr, unsigned short server_port);

Create a new log client. Will block the calling task for a maximum of 2 seconds trying to connect to a server with the given ip address and port. If a connection cannot be established, an error message is printed on the console, but the log client will keep trying to connect in the background. This is done by a background task, that will also periodically (every 5 seconds) flush pending messages out to the server.

void logClientSend (logClientId id, const char *message);

Send the given message to the given log client. Messages are not immediately sent to the log server. Instead they are sent whenever the cache overflows, or logClientFlush() is called.

void logClientFlush (logClientId id);

Immediately send all outstanding messages to the server.

void logClientShow (logClientId id, unsigned level);

Print information about the log clients internal state to stdout.

For backward compatibility with older versions of the logClient library, the header file also includes iocLog.h, which exports the definitions for the standard iocLogClient for error logging. See Chapter 10.7.2.

Also for backward compatibility, the following deprecated routines are exported.

logClientId logClientInit (void);

Create a log client using the environment variables EPICS_IOC_LOG_INET and EPICS_IOC_LOG_PORT as inputs to logClientCreate and also registers the client with the errlog task using errlogAddListener.

void logClientSendMessage (logClientId id, const char *message);

Check the global variable iocLogDisable before calling logClientSend.

19.16 macLib

macLib.h describes a general purpose macro substitution library. It is used for all macro substitution in base.

long macCreateHandle(
    MAC_HANDLE  **handle,       /* address of variable to receive pointer */
                                /* to new macro substitution context */
    char        *pairs[]        /* pointer to NULL-terminated array of */
                                /* {name,value} pair strings; a NULL */
                                /* value implies undefined; a NULL */
                                /* argument implies no macros */

void macSuppressWarning(
    MAC_HANDLE  *handle,        /* opaque handle */
    int         falseTrue       /*0 means ussue, 1 means suppress*/

/*following returns #chars copied, <0 if any macros are undefined*/
long macExpandString(
    MAC_HANDLE  *handle,        /* opaque handle */
    char        *src,           /* source string */
    char        *dest,          /* destination string */
    long        maxlen          /* maximum number of characters to copy */
                                /* to destination string */

/*following returns length of value */
long macPutValue(
    MAC_HANDLE  *handle,        /* opaque handle */
    char        *name,          /* macro name */
    char        *value          /* macro value */

/*following returns #chars copied (<0 if undefined) */
long macGetValue(
    MAC_HANDLE  *handle,        /* opaque handle */
    char        *name,          /* macro name or reference */
    char        *value,         /* string to receive macro value or name */
                                /* argument if macro is undefined */
    long        maxlen          /* maximum number of characters to copy */
                                /* to value */

long macDeleteHandle(MAC_HANDLE *handle);
long macPushScope(MAC_HANDLE *handle);
long macPopScope(MAC_HANDLE *handle);
long macReportMacros(MAC_HANDLE *handle);

/* Function prototypes (utility library) */

/*following returns #defns encountered; <0 = ERROR */
long macParseDefns(
     MAC_HANDLE *handle,        /* opaque handle; can be NULL if default */
                                /* special characters are to be used */
    char        *defns,         /* macro definitions in "a=xxx,b=yyy" */
                                /* format */
    char        **pairs[]       /* address of variable to receive pointer */
                                /* to NULL-terminated array of {name, */
                                /* value} pair strings; all storage is */
                                /* allocated contiguously */

/*following returns #macros defined; <0 = ERROR */
long macInstallMacros(MAC_HANDLE *handle,
    char        *pairs[]        /* pointer to NULL-terminated array of */
                                /* {name,value} pair strings; a NULL */
                                /* value implies undefined; a NULL */
                                /* argument implies no macros */

/*Expand string using environment variables as macro definitions */
epicsShareFunc char *         /* expanded string; NULL if any undefined macros */
epicsShareAPI macEnvExpand(
    char *str                   /* string to be expanded */

/*Expand string using environment variables alongside macro definitions */
epicsShareFunc char *         /* expanded string; NULL if any undefined macros */
epicsShareAPI macDefExpand(
    const char *str,            /* string to be expanded */
    MAC_HANDLE *macros          /* opaque handle; can be NULL if default */
                                /* special characters are to be used */

NOTE: The directory <base>/src/libCom/macLib contains two files macLibNOTES and macLibREADME that explain this library.

19.17 epicsThreadPool

epicsThreadPool.h implements general purpose threaded work queue. Pieces of work (jobs) are submitted to a queue which is shared by a group of worker threads. After jobs are placed on the queue they may be executed in any order.

The thread pool library will never implicitly destroy pools or jobs. Such cleanup is always the responsibility of user code.

19.17.1 Configure a pool

An epicsThreadPool instance can be obtained in two ways. The preferred method is the use an existing shared thread pool. Alternately, a new pool may be explicitly created. In both cases NULL may be passed to use the configuration, or a non-default configuration may be prepared in the following way.

typedef struct {
    unsigned int initialThreads;
    unsigned int maxThreads;
    unsigned int workerStack;
    unsigned int workerPriority;
} epicsThreadPoolConfig;

void epicsThreadPoolConfigDefaults(epicsThreadPoolConfig *);

Pool configuration should always be initialized with epicsThreadPoolConfigDefault() before modification by user code. This will initialize configuration parameters to sensible defaults, which can then be overwritten as needed by user code.

Note that no epicsThreadPool functions will ever retain a pointer to the user provided epicsThreadPoolConfig instance.

A suggestion for the number of worker threads to start immediately when the pool is created. When this number is less than maxThreads additional workers may be started later as needed. Defaults to zero.
Upper limit on the number of worker threads in this pool. Defaults to number of CPU cores.
Worker thread stack size. Defaults to size of epicsThreadStackSmall.
Worker thread priority. Defaults to just above epicsThreadPriorityCAServerHigh

These defaults are choosen to be suitable for CPU intensive background work by drivers.

19.17.2 Create a shared pool

epicsThreadPool* epicsThreadPoolGetShared(epicsThreadPoolConfig *opts);
void epicsThreadPoolReleaseShared(epicsThreadPool *pool);

A shared thread pool is obtained by calling epicsThreadPoolGetShared(). A global list of shared pools is examined. If an existing pool matches the requested configuration, then it is returned. Otherwise a new pool is created, added to the global list, then returned. epicsThreadPoolGetShared() may return NULL in situations of memory exhaustion.

Note that NULL may be passed to use the default configuration.

As for example:

void usercode() {
  epicsThreadPoolConfig myconf;
  /* overwrite defaults if needed */
  myconf.workerPriority = epicsThreadPriorityLow;
  ... = epicsThreadPoolGetShared(&myconf);

  /* or to use the defaults */
  ... = epicsThreadPoolGetShared(NULL);

The user provided configuration may be altered to ensure that the maxThreads is greater than or equal to the number of threads the host system can run in parallel. In addition, when a existing shared pool is returned, the user supplied config is overwritten with the pool's actual config.

If a thread pool will not be used further it must be released, which may cause it to be free'd when no other references exist. It is advisable to ensure that all queued jobs have completed as queued jobs may still run if the other references to the queue remain.

When matching a requested configuration against the configuration of a existing shared pool, the following conditions must be meet for an existing shared queue to be used.

Note that the initialThreads option is ignored when requesting a shared pool.

19.17.3 Creating an exclusive pool

epicsThreadPool* epicsThreadPoolCreate(epicsThreadPoolConfig *opts);
void epicsThreadPoolDestroy(epicsThreadPool *);

Unshared thread pools are created/destroyed in a similar fashion.

Note that NULL may be passed to use the default configuration.

When a pool is destroyed it will freeze the queue to prevent new jobs from being added, then block until any previously queued jobs complete.

19.17.4 Basic job operations

/* job modes */
typedef enum {
} epicsJobMode;
typedef void (*epicsJobFunction)(void* arg, epicsJobMode mode);

#define EPICSJOB_SELF ...
epicsJob* epicsJobCreate(epicsThreadPool* pool,
                         epicsJobFunction cb,
                         void* user);
void epicsJobDestroy(epicsJob*);
int epicsJobQueue(epicsJob*);
int epicsJobUnqueue(epicsJob*);

The normal lifecycle of a job is for it to be created, queued some number of times, then destroyed. Like with an epicsThreadPool*, the lifecycle of an epicsJob* is completely controlled by user code. Jobs will never be implicitly destroyed. When created, a pool, work function, and user argument are specified. The special user argument EPICSJOB_SELF may be passed to set the user argument to the epicsJob* returned by epicsJobCreate().

A job may be queued at any time. The queuing process can fail (return non-zero) if:

A job may be unqueued with epicsJobUnqueue(). This function will return 0 if the job was successfully removed from the queue or non-zero if this is not possible. A job can not be unqueued if it was not queued to begin with, is running, or has completed.

A job may also be destroyed at any time, including while its job function is running. In this case destruction is deferred until the job function returns.

If a thread pool is destroyed before all of its jobs are destroyed, then each job function is called one final time with the mode epicsJobModeCleanup to provide an opportunity to call epicsJobDestroy. If this is not done, then the job is disassociated from the pool. It is always the responsibility of user code to explicitly call epicsJobDestroy.

19.17.5 Writing job functions

typedef struct {
  epicsJob* job;
} myWork;

void myfunction(void* arg,
                epicsJobMode mode)
  myWork *priv=arg;
  if(mode==epicsJobModeCleanup) {
  /* do normal work */

void somewhere(...)
   epicsThreadPool *pool;
   myWork *priv = ...; /* allocate somehow */
   pool = epicsThreadPoolCreate(NULL);
   assert(pool!=NULL && priv!=NULL);
   priv->job = epicsJobCreate(pool, &myfunction, priv);

Some restrictions apply to job functions. Only the following epicsThreadPool functions may be called from a job function. When using a shared pool, no modification should be made to the worker threads (eg. don't change priority). If such modifications are needed, then an exclusively owned pool should be created.

No internal locks are held while a job function runs. So a job function may lock arbitrary mutexes without causing a deadlock. When in a job function, care must be taken to only call those function explicitly marked as safe to call from a running job function as these functions are written to avoid corrupting the internal state of the pool.

19.17.6 Moving jobs between pools

It may be desirable to move epicsJob instances between pools, or to have jobs not associated with any pool. This is supported with the caveat that the epicsJobMove() function must not run concurrently with any other epicsThreadPool functions operating on the same job. In addition to functions operating explicitly on this job, this also includes epicsThreadPoolDestroy()

A job may be created with no pool association by passing NULL to the epicsJobCreate() function instead of an explicit epicsThreadPool* pointer. The association can be changed at runtime with the epicsJobMove() function.

19.17.7 Pool control

typedef enum {
} epicsThreadPoolOption;
void epicsThreadPoolControl(epicsThreadPool* pool,
                            epicsThreadPoolQueueOption opt,
                            unsigned int val);
int epicsThreadPoolWait(epicsThreadPool* pool, double timeout);

It may be useful to manipulate the queue of a thread pool at runtime (eg. unittests). Currently defined options are:

Set to 0 to prevent additional jobs from being queued. Set to 1 to resume normal operation.
Set to 0 to prevents workers from taking jobs from the queue. Set to 1 for normal operation.
These options may be combined with epicsThreadPoolWait() to block until the queue is empty.

epicsThreadPoolWait() accepts a timeout in seconds. A timeout value less than 0.0 never times out, a value of exactly 0.0 will not block, and values greater than 0.0 will block for the requested time at most.

This function returns 0 if the queue was emptied and no jobs are running at any moment during the timeout period, or non-zero if the timeout period ellapses and jobs remain in the queue or are running.

19.18 misc

19.18.1 aToIPAddr

The function prototype for this routine appears in osiSock.h

int aToIPAddr(const char *pAddrString, unsigned short defaultPort,
              struct sockaddr_in *pIP);

aToIPAddr() fills in the structure pointed to by the pIP argument with the Internet address and port number specified by the pAddrString argument.

Three forms of pAddrString are accepted:

  1. n.n.n.n:p

    The Internet address of the host, specified as four (usually decimal) numbers separated by periods.

  2. xxxxxxxx:p

    The Internet address number of the host, specified as a single (usually hexadecimal) number.

  3. hostname:p

    The Internet host name of the host.

In all cases the `:p' may be omitted in which case the port number is set to the value of the defaultPort argument. The numbers are normally interpreted in base 16 if they begin with `0x' or `0X', in base 8 if they begin with `0', and in base 10 otherwise. However the numeric forms are interpreted by the operating system's gethostbyname() function, thus the acceptable bases may be OS-specific.

19.18.2 adjustment

adjustment.h describes a single function:

size_t adjustToWorstCaseAlignment(size_t size);

adjustToWorstCaseAlignment() returns a value >= size that an exact multiple of the worst case alignment for the architecture on which the routine is executed.

19.18.3 cantProceed

cantProceed.h describes routines that are provided for code that can't proceed when an error occurs.

void cantProceed(const char *errorMessage);
void *callocMustSucceed(size_t count, size_t size,const char *errorMessage);
void *mallocMustSucceed(size_t size, const char *errorMessage);

cantProceed() issues the error message and suspends the current task - it will never return. callocMustSucceed() and mallocMustSucceed() can be used in place of calloc() and malloc(). If size or count are zero, or the memory allocation fails, they output a message and call cantProceed().

19.18.4 dbDefs

dbDefs.h contains definitions that are still used in base but should not be. Hopefully these all go away some day. This has been the hope for about ten years.

19.18.5 epicsConvert

epicsConvert.h currently describes:

float epicsConvertDoubleToFloat(double value);

epicsConvertDoubleToFloat converts a double to a float. If the double value is outside the range that can be represented as a float the value assigned will be FLT_MIN or FLT_MAX with the appropriate matching sign. A floating exception is never raised.

19.18.6 epicsString

epicsString.h currently describes:

int epicsStrnRawFromEscaped(char *dst, size_t dstlen, const char *src,
    size_t srclen);
int epicsStrnEscapedFromRaw(char *outbuf, size_t outsize, const char *inbuf,
    size_t inlen);
size_t epicsStrnEscapedFromRawSize(const char *src, size_t srclen);
int epicsStrCaseCmp(const char *s1, const char *s2);
int epicsStrnCaseCmp(const char *s1, const char *s2, int n);
char *epicsStrDup(const char *s);
int epicsStrPrintEscaped(FILE *fp, const char *s, int n);
int epicsStrGlobMatch(const char *str, const char *pattern);
char *epicsStrtok_r(char *s, const char *delim, char **lasts);
unsigned int epicsStrHash(const char *str, unsigned int seed);
unsigned int epicsMemHash(const char *str, size_t length,
    unsigned int seed);

epicsStrnRawFromEscaped copies up to strlen characters from the string src into a buffer dst of size dstlen, converting C-style escape sequences into their binary form. A zero byte terminates the input string. The resulting string will be zero-terminated as long as dstlen is non-zero. The return value is the number of characters that were actually written into dst, not counting characters that would not fit or the zero terminator. Since the output string can never be longer than the source, it is legal for src and dst to point to the same buffer and strlen and dstlen to have the same value, thus performing the character translation in-place.

epicsStrnEscapedFromRaw does the opposite of epicsStrnRawFromEscaped: It tries to copy strlen characters from the string src into a buffer dst of size dstlen, converting non-printable characters into C-style escape sequences. A zero byte will not terminate the input string. The output string will be zero-terminated as long as dstlen is non-zero. No more than dstlen characters will actually be written into the output buffer, although all the characters in the input string will be read. The return value is the number of characters that would have been stored in the output buffer if it were large enough, or a negative value if dst == src. In-place translations are not allowed since the escaped results will usually be larger than the input string.

The following escaped character constants will be used in the output:

\a  \b  \f  \n  \r  \t  \v  \\  \'  \"

All other non-printable characters appear as octal escapes in form \ooo where ooo are three octal digits (0-7). Non-printable characters are determined by the C runtime library's isprint() function.

epicsStrnEscapedFromRawSize scans up to strlen characters of the string src that may contain non-printable characters, and returns the size of the output buffer that would be needed to escape that string. The terminating zero-byte needed in the output buffer is not included in the count, so must be allowed for by the caller. This routine is faster than calling epicsStrnEscapedFromRaw with a zero length output buffer; both should return the same result.

epicsStrPrintEscaped prints the contents of its input buffer, substituting escape sequences for non-printable characters.

epicsStrCaseCmp and epicsStrnCaseCmp implement the strcasecmp and strncasecmp functions respectively, which are not available on all supported operating systems. They operate like strcmp and strncmp, but are case insensitive, using the C locale.

epicsStrDup implements strdup, which is not available on all supported operating systems. It allocates sufficient memory for the string, copies it and returns a pointer to the new copy. The pointer should eventually be passed to the function free(). If insufficient memory is available cantProceed() is called.

epicsStrGlobMatch returns non-zero if the str matches the shell wild-card pattern.

epicsStrtok_r implements strtok_r, which is not available on all operating systems.

epicsStrHash calculates a hash of a zero-terminated string str, while epicsMemHash uses the same algorithm on a fixed-length memory buffer that may contain zero bytes. In both cases an initial seed value may be provided which permits multiple strings or buffers to be combined into a single hash result. The final result should be masked to achieve the desired number of bits in the hash value.

19.18.7 epicsTypes

epicsTypes.h provides typedefs for architecture independent data types.

typedef char            epicsInt8;
typedef unsigned char   epicsUInt8;
typedef short           epicsInt16;
typedef unsigned short  epicsUInt16;
typedef epicsUInt16     epicsEnum16;
typedef int             epicsInt32;
typedef unsigned        epicsUInt32;
typedef float           epicsFloat32;
typedef double          epicsFloat64;
typedef epicsInt32      epicsStatus;

So far the definitions provided in this header file have worked on all architectures. In addition to the above definitions epicsTypes.h has a number of definitions for displaying the types and other useful definitions. See the header file for details.

19.18.8 locationException

A C++ template for use as an exception object, used inside Channel Access. Not documented here.

19.18.9 shareLib.h

This is the header file for the ``decorated names" that appear in header files, e.g.

#define epicsExportSharedSymbols
epicsShareFunc int epicsShareAPI a_func(int arg);

Thse are needed to properly create DLLs on windows. Read the comments in the shareLib.h file for a detailed description of where they should be used. Note that the epicsShareAPI decorator is deprecated for all new EPICS APIs and is being removed from APIs that are only used within the IOC.

19.18.10 truncateFile.h

TF_RETURN truncateFile (const char *pFileName, unsigned size);


pFileName - name (and optionally path) of file

truncateFile() truncates the file to the specified size. truncate() is not used because it is not portable. It returns TF_OK if the file is less than size bytes or if it was successfully truncated. It returns TF_ERROR if the file could not be truncated.

19.18.11 unixFileName.h


19.18.12 epicsUnitTest.h

The unit test routines make it easy for a test program to generate output that is compatible with the Test Anything Protocol and can thus be used with Perl's automated Test::Harness as well as generating human-readable output. The routines detect whether they are being run automatically and print a summary of the results at the end if not.

void testPlan(int tests);
int  testOk(int pass, const char *fmt, ...);
#define testOk1(cond) testOk(cond, "%s", #cond)
void testPass(const char *fmt, ...);
void testFail(const char *fmt, ...);
int  testOkV(int pass, const char *fmt, va_list pvar);
void testSkip(int skip, const char *why)
void testTodoBegin(const char *why);
void testTodoEnd();
int  testDiag(const char *fmt, ...);
void testAbort(const char *fmt, ...);
int  testDone(void);

typedef int (*TESTFUNC)(void);
epicsShareFunc void testHarness(void);
epicsShareFunc void runTestFunc(const char *name, TESTFUNC func);

#define runTest(func) runTestFunc(#func, func)

A test program starts with a call to testPlan(), announcing how many tests are to be conducted. If this number is not known a value of zero can be used during development, but it is recommended that the correct value be substituted after the test program has been completed.

Individual test results are reported using any of testOk(), testOk1(), testOkV(), testPass() or testFail(). The testOk() call takes and also returns a logical pass/fail result (zero means failure, any other value is success) and a printf-like format string and arguments which describe the test. The convenience macro testOk1() is provided which stringifies its single condition argument, reducing the effort needed when writing test programs. The individual testPass() and testFail() routines can be used when the test program takes a different path on success than on failure, but one or other must always be called for any particular test. The testOkV() routine is a varargs form of testOk() included for internal purposes which may prove useful in some cases.

If some program condition or failure makes it impossible to run some tests, the testSkip() routine can be used to indicate how many tests are being omitted from the run, thus keeping the test counts correct; the constant string why is displayed as an explanation to the user (this string is not printf-like).

If some tests are expected to fail because functionality in the module under test has not yet been fully implemented, these tests may still be executed, wrapped between calls to testTodoBegin() and testTodoEnd(). testTodoBegin() takes a constant string indicating why these tests are not expected to succeed. This modifies the counting of the results so the wrapped tests will not be recorded as failures.

Additional information can be supplied using the testDiag() routine, which displays the relevent information as a comment in the result output. None of the printable strings passed to any testXxx() routine should contain a newline `\n' character, newlines will be added by the test routines as part of the Test Anything Protocol. For multiple lines of diagnostic output, call testDiag() as many times as necessary.

If at any time the test program is unable to continue for some catastrophic reason, calling testAbort() with an appropriate message will ensure that the test harness understands this. testAbort() does not return, but calls the ANSI C routine abort() to cause the program to stop immediately.

After all of the tests have been completed, the return value from testDone() can be used as the return status code from the program's main() routine.

On vxWorks and RTEMS, an alternative test harness can be used to run a series of tests in order and summarize the results from them all at the end just like the Perl harness does. The routine testHarness() is called once at the beginning of the test harness program. Each test program is run by passing its main routine name to the runTest() macro which expands into a call to the runTestFunc() routine. The last test program or the harness program itself must finish by calling epicsExit() which triggers the test summary mechanism to generate its result outputs (from an epicsAtExit callback routine).

Some tests require the context of an IOC to be run. This conflicts with the idea of running multiple tests within a test harness, as iocInit() is only allowed to be called once, and some parts of the full IOC (e.g. the rsrv CA server) can not be shut down cleanly. The function iocBuildIsolated() allows to start an IOC without its Channel Access parts, so that it can be shutdown quite cleany using iocShutdown(). This feature is only intended to be used from test programs. Do not use it on productional IOCs. After building the IOC using iocBuildIsolated() or iocBuild(), it has to be started by calling iocRun(). The suggested call sequence in a test program that needs to run the IOC without Channel Access is:

#include "iocInit.h"

    iocBuildIsolated() || iocRun();

[... test code ...]

    pdbbase = NULL;
    return testDone();

The part from iocBuildIsolated() to iocShutdown() can be repeated to execute multiple tests within one executable or harness.

To make it easier to create a single test program that can be built for both the embedded and workstation operating system harnesses, the header file testMain.h provides a convenience macro MAIN() that adjusts the name of the test program according to the platform it is running on: main() on workstations and a regular function name on embedded systems.

The following is a simple example of a test program using the epicsUnitTest routines:

#include <math.h>
#include "epicsUnitTest.h"
#include "testMain.h"

    testOk(sin(0.0) == 0.0, "Sine starts");
    testOk(cos(0.0) == 1.0, "Cosine continues");
    if (!testOk1(M_PI == 4.0*atan(1.0)))
        testDiag("4*atan(1) = %g", 4.0*atan(1.0));
    return testDone();

The output from running the above program looks like this:

ok  1 - Sine starts
ok  2 - Cosine continues
ok  3 - M_PI == 4.0*atan(1.0)

       Tests: 3
      Passed:  3 = 100%

next up previous contents index
Next: 20. libCom OSI libraries Up: AppDevGuide Previous: 18. IOC Shell   Contents   Index
Andrew Johnson 2015-04-14