diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html index ad503c2a7..626129fe1 100644 --- a/documentation/RELEASE_NOTES.html +++ b/documentation/RELEASE_NOTES.html @@ -14,6 +14,31 @@

Changes between 3.15.1 and 3.15.2

+

Enhanced API for asTrapWrite listeners

+ +

External software such as the CA Put Logging module that registers a listener +with the asTrapWrite subsystem was not previously given access to the actual +data being sent by the CA client. In most cases this was not a problem as the +listener can look at the field being modified both before and after the +operation, but if the put processes the record which immediately overwrites the +new value, the client's value cannot be observed.

+ +

This release adds three fields to the asTrapWriteMessage structure that is +passed to the listener routines. These new fields provide the CA data type, the +number of array elements, and a pointer to the source data buffer. This change +is completely backwards compatible with listener code written against the +original API. The new API can be detected at compile-time as follows:

+ +
+#include "asLib.h"
+
+/* ... */
+
+#ifdef asTrapWriteWithData
+    /* Enhanced API */
+#endif
+
+

Use of PATH_FILTER in Makefiles deprecated

The PATH_FILTER variable was being called to convert forward shashes diff --git a/src/ca/client/tools/tool_lib.c b/src/ca/client/tools/tool_lib.c index 718e4f940..8b9c5ca28 100644 --- a/src/ca/client/tools/tool_lib.c +++ b/src/ca/client/tools/tool_lib.c @@ -118,6 +118,7 @@ char *val2str (const void *v, unsigned type, int index) strcpy (str, "*** invalid type"); return str; } + strcpy (str, "!!!"); base_type = type % (LAST_TYPE+1); diff --git a/src/ioc/dbStatic/dbStaticLib.c b/src/ioc/dbStatic/dbStaticLib.c index 4cf6d4603..e1af4c720 100644 --- a/src/ioc/dbStatic/dbStaticLib.c +++ b/src/ioc/dbStatic/dbStaticLib.c @@ -588,6 +588,7 @@ void dbFreeBase(dbBase *pdbbase) dbPvdFreeMem(pdbbase); dbFreePath(pdbbase); free((void *)pdbbase); + pdbbase = NULL; return; } diff --git a/src/ioc/rsrv/camessage.c b/src/ioc/rsrv/camessage.c index f9f0bbc6f..6ade766ed 100644 --- a/src/ioc/rsrv/camessage.c +++ b/src/ioc/rsrv/camessage.c @@ -284,21 +284,18 @@ static void log_header ( pciu = MPTOPCIU(mp); if (pContext) { - epicsPrintf ("CAS: request from %s => \"%s\"\n", + epicsPrintf ("CAS: request from %s => %s\n", hostName, pContext); } - epicsPrintf ( -"CAS: Request from %s => cmmd=%d cid=0x%x type=%d count=%d postsize=%u\n", + epicsPrintf ( "CAS: Request from %s => cmmd=%d cid=0x%x type=%d count=%d postsize=%u\n", hostName, mp->m_cmmd, mp->m_cid, mp->m_dataType, mp->m_count, mp->m_postsize); - epicsPrintf ( -"CAS: Request from %s => available=0x%x \tN=%u dbch=%p\n", - hostName, mp->m_available, mnum, (pciu?(void *)&pciu->dbch:NULL)); + epicsPrintf ( "CAS: Request from %s => available=0x%x \tN=%u paddr=%p\n", + hostName, mp->m_available, mnum, (pciu ? (void *)&pciu->dbch : NULL)); if (mp->m_cmmd==CA_PROTO_WRITE && mp->m_dataType==DBF_STRING && pPayLoad ) { - epicsPrintf ( -"CAS: Request from %s => \tThe string written: %s \n", + epicsPrintf ( "CAS: Request from %s => Wrote string \"%s\"\n", hostName, (char *)pPayLoad ); } } @@ -854,10 +851,10 @@ static int write_action ( caHdrLargeArray *mp, return RSRV_ERROR; } - asWritePvt = asTrapWriteBefore ( pciu->asClientPVT, + asWritePvt = asTrapWriteWithData ( pciu->asClientPVT, pciu->client->pUserName ? pciu->client->pUserName : "", pciu->client->pHostName ? pciu->client->pHostName : "", - pciu->dbch ); + pciu->dbch, mp->m_dataType, mp->m_count, pPayload ); dbStatus = dbChannel_put( pciu->dbch, @@ -1881,11 +1878,12 @@ static int write_notify_action ( caHdrLargeArray *mp, void *pPayload, pciu->pPutNotify->dbrType = mp->m_dataType; - pciu->pPutNotify->asWritePvt = asTrapWriteBefore ( + pciu->pPutNotify->asWritePvt = asTrapWriteWithData ( pciu->asClientPVT, pciu->client->pUserName ? pciu->client->pUserName : "", pciu->client->pHostName ? pciu->client->pHostName : "", - pciu->dbch ); + pciu->dbch, mp->m_dataType, mp->m_count, + pciu->pPutNotify->pbuffer ); dbProcessNotify(&pciu->pPutNotify->dbPutNotify); diff --git a/src/ioc/rsrv/camsgtask.c b/src/ioc/rsrv/camsgtask.c index 4eedfc428..65d37c9a9 100644 --- a/src/ioc/rsrv/camsgtask.c +++ b/src/ioc/rsrv/camsgtask.c @@ -68,9 +68,10 @@ void camsgtask ( void *pParm ) status = socket_ioctl (client->sock, FIONREAD, &nchars); if (status < 0) { char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); - errlogPrintf("CAS: io ctl err - %s\n", + errlogPrintf("CAS: ioctl error - %s\n", sockErrBuf); cas_send_bs_msg(client, TRUE); } @@ -99,9 +100,8 @@ void camsgtask ( void *pParm ) } if ( anerrno == SOCK_ENOBUFS ) { - errlogPrintf ( - "rsrv: system low on network buffers " - "- receive retry in 15 seconds\n" ); + errlogPrintf ( + "CAS: Out of network buffers, retring receive in 15 seconds\n" ); epicsThreadSleep ( 15.0 ); continue; } @@ -110,10 +110,15 @@ void camsgtask ( void *pParm ) * normal conn lost conditions */ if ( ( anerrno != SOCK_ECONNABORTED && - anerrno != SOCK_ECONNRESET && - anerrno != SOCK_ETIMEDOUT ) || - CASDEBUG > 2 ) { - errlogPrintf ( "CAS: client disconnect(errno=%d)\n", anerrno ); + anerrno != SOCK_ECONNRESET && + anerrno != SOCK_ETIMEDOUT ) || + CASDEBUG > 2 ) { + char sockErrBuf[64]; + + epicsSocketConvertErrorToString( + sockErrBuf, sizeof ( sockErrBuf ), anerrno); + errlogPrintf ( "CAS: Client disconnected - %s\n", + sockErrBuf ); } break; } diff --git a/src/ioc/rsrv/caserverio.c b/src/ioc/rsrv/caserverio.c index 91cb5e2c6..c5865d91d 100644 --- a/src/ioc/rsrv/caserverio.c +++ b/src/ioc/rsrv/caserverio.c @@ -90,7 +90,8 @@ void cas_send_bs_msg ( struct client *pclient, int lock_needed ) } if ( anerrno == SOCK_ENOBUFS ) { - errlogPrintf ( "rsrv: system low on network buffers - send retry in 15 seconds\n" ); + errlogPrintf ( + "CAS: Out of network buffers, retrying send in 15 seconds\n" ); epicsThreadSleep ( 15.0 ); continue; } @@ -108,8 +109,7 @@ void cas_send_bs_msg ( struct client *pclient, int lock_needed ) char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); - errlogPrintf ( - "CAS: TCP send to \"%s\" failed because \"%s\"\n", + errlogPrintf ( "CAS: TCP send to %s failed - %s\n", buf, sockErrBuf); } pclient->disconnect = TRUE; @@ -135,7 +135,7 @@ void cas_send_bs_msg ( struct client *pclient, int lock_needed ) char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); - errlogPrintf ("rsrv: socket shutdown error was %s\n", + errlogPrintf ("CAS: Socket shutdown error - %s\n", sockErrBuf ); } } @@ -204,7 +204,7 @@ void cas_send_dg_msg ( struct client * pclient ) } else { errlogPrintf ( - "cas: system failed to send entire udp frame?\n" ); + "CAS: System failed to send entire udp frame?\n" ); } } else { @@ -213,11 +213,8 @@ void cas_send_dg_msg ( struct client * pclient ) epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); ipAddrToDottedIP ( &pclient->addr, buf, sizeof(buf) ); - errlogPrintf( - "CAS: UDP send to \"%s\" " - "failed because \"%s\"\n", - buf, - sockErrBuf); + errlogPrintf( "CAS: UDP send to %s failed - %s\n", + buf, sockErrBuf); } pclient->send.stk = 0u; diff --git a/src/ioc/rsrv/caservertask.c b/src/ioc/rsrv/caservertask.c index 00095eaf1..132fe462f 100644 --- a/src/ioc/rsrv/caservertask.c +++ b/src/ioc/rsrv/caservertask.c @@ -276,7 +276,7 @@ int rsrv_init (void) status = envGetLongConfigParam ( &EPICS_CA_MAX_ARRAY_BYTES, &maxBytesAsALong ); if ( status || maxBytesAsALong < 0 ) { - errlogPrintf ( "cas: EPICS_CA_MAX_ARRAY_BYTES was not a positive integer\n" ); + errlogPrintf ( "CAS: EPICS_CA_MAX_ARRAY_BYTES was not a positive integer\n" ); rsrvSizeofLargeBufTCP = MAX_TCP; } else { @@ -290,7 +290,7 @@ int rsrv_init (void) maxBytes = 0xffffffff; } if ( maxBytes < MAX_TCP ) { - errlogPrintf ( "cas: EPICS_CA_MAX_ARRAY_BYTES was rounded up to %u\n", MAX_TCP ); + errlogPrintf ( "CAS: EPICS_CA_MAX_ARRAY_BYTES was rounded up to %u\n", MAX_TCP ); rsrvSizeofLargeBufTCP = MAX_TCP; } else { @@ -571,7 +571,7 @@ void destroy_client ( struct client *client ) freeListFree ( rsrvLargeBufFreeListTCP, client->send.buf ); } else { - errlogPrintf ( "cas: Corrupt send buffer free list type code=%u during client cleanup?\n", + errlogPrintf ( "CAS: Corrupt send buffer free list type code=%u during client cleanup?\n", client->send.type ); } } @@ -583,7 +583,7 @@ void destroy_client ( struct client *client ) freeListFree ( rsrvLargeBufFreeListTCP, client->recv.buf ); } else { - errlogPrintf ( "cas: Corrupt recv buffer free list type code=%u during client cleanup?\n", + errlogPrintf ( "CAS: Corrupt recv buffer free list type code=%u during client cleanup?\n", client->send.type ); } } diff --git a/src/libCom/as/asLib.h b/src/libCom/as/asLib.h index af8e0dc30..95fed7a9a 100644 --- a/src/libCom/as/asLib.h +++ b/src/libCom/as/asLib.h @@ -30,25 +30,38 @@ typedef enum{ } asClientStatus; typedef void (*ASCLIENTCALLBACK) (ASCLIENTPVT,asClientStatus); + /* The following routines are macros with the following syntax long asCheckGet(ASCLIENTPVT asClientPvt); long asCheckPut(ASCLIENTPVT asClientPvt); */ -#define asCheckGet(asClientPvt)\ - (asActive \ - ? ((asClientPvt)->access>=asREAD ? TRUE : FALSE)\ - : TRUE) -#define asCheckPut(asClientPvt)\ - (asActive \ - ? ((asClientPvt)->access>=asWRITE ? TRUE : FALSE)\ - : TRUE) -#define asTrapWriteBefore(asClientPvt,user,host,addr) \ - (((asActive) && (asClientPvt)->trapMask) \ - ? asTrapWriteBeforeWrite((user),(host),(addr)) \ - : 0) +#define asCheckGet(asClientPvt) \ + (!asActive || ((asClientPvt)->access >= asREAD)) +#define asCheckPut(asClientPvt) \ + (!asActive || ((asClientPvt)->access >= asWRITE)) + +/* More convenience macros +void *asTrapWriteWithData(ASCLIENTPVT asClientPvt, + const char *userid, const char *hostid, void *addr, + int dbrType, int no_elements, void *data); +void asTrapWriteAfter(ASCLIENTPVT asClientPvt); +*/ +#define asTrapWriteWithData(asClientPvt, user, host, addr, type, count, data) \ + ((asActive && (asClientPvt)->trapMask) \ + ? asTrapWriteBeforeWithData((user), (host), (addr), (type), (count), (data)) \ + : 0) +#define asTrapWriteAfter(pvt) \ + if (pvt) asTrapWriteAfterWrite(pvt) + +/* This macro is for backwards compatibility, upgrade any code + calling it to use asTrapWriteWithData() instead ASAP: +void *asTrapWriteBefore(ASCLIENTPVT asClientPvt, + const char *userid, const char *hostid, void *addr); +*/ +#define asTrapWriteBefore(asClientPvt, user, host, addr) \ + asTrapWriteWithData(asClientPvt, user, host, addr, 0, 0, NULL) + -#define asTrapWriteAfter(pvt) if((pvt)) asTrapWriteAfterWrite((pvt)) - epicsShareFunc long epicsShareAPI asInitialize(ASINPUTFUNCPTR inputfunction); epicsShareFunc long epicsShareAPI asInitFile( const char *filename,const char *substitutions); @@ -100,8 +113,9 @@ epicsShareFunc int epicsShareAPI asDumpMemFP(FILE *fp,const char *asgname, epicsShareFunc int epicsShareAPI asDumpHash(void); epicsShareFunc int epicsShareAPI asDumpHashFP(FILE *fp); -epicsShareFunc void * epicsShareAPI asTrapWriteBeforeWrite( - const char *userid,const char *hostid,void *addr); +epicsShareFunc void * epicsShareAPI asTrapWriteBeforeWithData( + const char *userid, const char *hostid, void *addr, + int dbrType, int no_elements, void *data); epicsShareFunc void epicsShareAPI asTrapWriteAfterWrite(void *pvt); diff --git a/src/libCom/as/asTrapWrite.c b/src/libCom/as/asTrapWrite.c index 9aa795262..544e9a211 100644 --- a/src/libCom/as/asTrapWrite.c +++ b/src/libCom/as/asTrapWrite.c @@ -3,8 +3,7 @@ * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. -* EPICS BASE Versions 3.13.7 -* and higher are distributed subject to a Software License Agreement found +* EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /*asTrapWrite.c */ @@ -101,47 +100,53 @@ void epicsShareAPI asTrapWriteUnregisterListener(asTrapWriteId id) = (listenerPvt *)ellNext(&plistenerPvt->node); if(plistenerPvt->plistener == plistener) { ellDelete(&pwriteMessage->listenerPvtList,&plistenerPvt->node); - freeListFree(pasTrapWritePvt->freeListListenerPvt,(void *)plistenerPvt); + freeListFree(pasTrapWritePvt->freeListListenerPvt, plistenerPvt); } plistenerPvt = pnext; } pwriteMessage = (writeMessage *)ellNext(&pwriteMessage->node); } ellDelete(&pasTrapWritePvt->listenerList,&plistener->node); - free((void *)plistener); + free(plistener); epicsMutexUnlock(pasTrapWritePvt->lock); } -void * epicsShareAPI asTrapWriteBeforeWrite( - const char *userid,const char *hostid,void *addr) +void * epicsShareAPI asTrapWriteBeforeWithData( + const char *userid, const char *hostid, void *addr, + int dbrType, int no_elements, void *data) { writeMessage *pwriteMessage; listener *plistener; - listenerPvt *plistenerPvt; - if(pasTrapWritePvt==0) return(0); - if(ellCount(&pasTrapWritePvt->listenerList)<=0) return 0; + if (pasTrapWritePvt == 0 || + ellCount(&pasTrapWritePvt->listenerList) <= 0) return 0; + pwriteMessage = (writeMessage *)freeListCalloc( pasTrapWritePvt->freeListWriteMessage); pwriteMessage->message.userid = userid; pwriteMessage->message.hostid = hostid; pwriteMessage->message.serverSpecific = addr; + pwriteMessage->message.dbrType = dbrType; + pwriteMessage->message.no_elements = no_elements; + pwriteMessage->message.data = data; ellInit(&pwriteMessage->listenerPvtList); + epicsMutexMustLock(pasTrapWritePvt->lock); - ellAdd(&pasTrapWritePvt->writeMessageList,&pwriteMessage->node); + ellAdd(&pasTrapWritePvt->writeMessageList, &pwriteMessage->node); plistener = (listener *)ellFirst(&pasTrapWritePvt->listenerList); - while(plistener) { - plistenerPvt = (listenerPvt *)freeListCalloc( + while (plistener) { + listenerPvt *plistenerPvt = (listenerPvt *)freeListCalloc( pasTrapWritePvt->freeListListenerPvt); + plistenerPvt->plistener = plistener; pwriteMessage->message.userPvt = 0; - (*plistener->func)(&pwriteMessage->message,0); + plistener->func(&pwriteMessage->message, 0); plistenerPvt->userPvt = pwriteMessage->message.userPvt; - ellAdd(&pwriteMessage->listenerPvtList,&plistenerPvt->node); + ellAdd(&pwriteMessage->listenerPvtList, &plistenerPvt->node); plistener = (listener *)ellNext(&plistener->node); } epicsMutexUnlock(pasTrapWritePvt->lock); - return((void *)pwriteMessage); + return pwriteMessage; } void epicsShareAPI asTrapWriteAfterWrite(void *pvt) @@ -149,20 +154,22 @@ void epicsShareAPI asTrapWriteAfterWrite(void *pvt) writeMessage *pwriteMessage = (writeMessage *)pvt; listenerPvt *plistenerPvt; - if(pwriteMessage==0 || pasTrapWritePvt==0) return; + if (pwriteMessage == 0 || + pasTrapWritePvt == 0) return; + epicsMutexMustLock(pasTrapWritePvt->lock); plistenerPvt = (listenerPvt *)ellFirst(&pwriteMessage->listenerPvtList); - while(plistenerPvt) { + while (plistenerPvt) { listenerPvt *pnext = (listenerPvt *)ellNext(&plistenerPvt->node); - listener *plistener; - plistener = plistenerPvt->plistener; + listener *plistener = plistenerPvt->plistener; + pwriteMessage->message.userPvt = plistenerPvt->userPvt; - (*plistener->func)(&pwriteMessage->message,1); - ellDelete(&pwriteMessage->listenerPvtList,&plistenerPvt->node); - freeListFree(pasTrapWritePvt->freeListListenerPvt,(void *)plistenerPvt); + plistener->func(&pwriteMessage->message, 1); + ellDelete(&pwriteMessage->listenerPvtList, &plistenerPvt->node); + freeListFree(pasTrapWritePvt->freeListListenerPvt, plistenerPvt); plistenerPvt = pnext; } - ellDelete(&pasTrapWritePvt->writeMessageList,&pwriteMessage->node); - freeListFree(pasTrapWritePvt->freeListWriteMessage,(void *)pwriteMessage); + ellDelete(&pasTrapWritePvt->writeMessageList, &pwriteMessage->node); + freeListFree(pasTrapWritePvt->freeListWriteMessage, pwriteMessage); epicsMutexUnlock(pasTrapWritePvt->lock); } diff --git a/src/libCom/as/asTrapWrite.h b/src/libCom/as/asTrapWrite.h index ec34c5312..b8033cb94 100644 --- a/src/libCom/as/asTrapWrite.h +++ b/src/libCom/as/asTrapWrite.h @@ -3,9 +3,8 @@ * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. -* EPICS BASE Versions 3.13.7 -* and higher are distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. \*************************************************************************/ /*asTrapWrite.h*/ /* Author: Marty Kraimer Date: 07NOV2000 */ @@ -24,6 +23,9 @@ typedef struct asTrapWriteMessage { const char *hostid; void *serverSpecific; void *userPvt; + int dbrType; /* Data type from ca/db_access.h, NOT dbFldTypes.h */ + int no_elements; + void *data; /* Might be NULL if no data is available */ } asTrapWriteMessage; diff --git a/src/libCom/test/epicsExceptionTest.cpp b/src/libCom/test/epicsExceptionTest.cpp index b9ecdc940..b40ceb0dc 100644 --- a/src/libCom/test/epicsExceptionTest.cpp +++ b/src/libCom/test/epicsExceptionTest.cpp @@ -70,7 +70,7 @@ static void epicsExceptionTestPrivate () try { char * p = new ( nothrow ) char [unsuccessfulNewSize]; - testOk(p == 0, "new (nothrow)"); + testOk(p == 0, "new (nothrow) returned %p", p); } catch( ... ) { testFail("new (nothrow): threw"); diff --git a/src/libCom/test/epicsMathTest.c b/src/libCom/test/epicsMathTest.c index 527dd2922..8ea763cf0 100644 --- a/src/libCom/test/epicsMathTest.c +++ b/src/libCom/test/epicsMathTest.c @@ -31,11 +31,26 @@ MAIN(epicsMathTest) testOk1(epicsINF == epicsINF); testOk1(epicsINF > 0.0); testOk1(epicsINF - epicsINF != 0.0); + +#if defined(_WIN64) && defined(_MSC_VER) + testTodoBegin("Known failure on windows-x64"); +#endif testOk1(epicsINF + -epicsINF != 0.0); testOk1(-epicsINF + epicsINF != 0.0); +#if defined(_WIN64) && defined(_MSC_VER) + testTodoEnd(); +#endif + testOk1(isnan(epicsINF - epicsINF)); + +#if defined(_WIN64) && defined(_MSC_VER) + testTodoBegin("Known failure on windows-x64"); +#endif testOk1(isnan(epicsINF + -epicsINF)); testOk1(isnan(-epicsINF + epicsINF)); +#if defined(_WIN64) && defined(_MSC_VER) + testTodoEnd(); +#endif testOk1(isnan(epicsNAN)); testOk1(!isinf(epicsNAN)); @@ -46,8 +61,15 @@ MAIN(epicsMathTest) testOk1(!(epicsNAN >= epicsNAN)); testOk1(!(epicsNAN > epicsNAN)); testOk1(isnan(epicsNAN - epicsNAN)); + +#if defined(_WIN64) && defined(_MSC_VER) + testTodoBegin("Known failure on windows-x64"); +#endif testOk1(isnan(epicsNAN + -epicsNAN)); testOk1(isnan(-epicsNAN + epicsNAN)); +#if defined(_WIN64) && defined(_MSC_VER) + testTodoEnd(); +#endif c = huge / tiny; testOk(!isnan(c), "!isnan(1e300 / 1e-300)"); diff --git a/src/libCom/test/epicsMessageQueueTest.cpp b/src/libCom/test/epicsMessageQueueTest.cpp index 5e33361c5..3bd47ae27 100644 --- a/src/libCom/test/epicsMessageQueueTest.cpp +++ b/src/libCom/test/epicsMessageQueueTest.cpp @@ -10,8 +10,6 @@ * $Revision-Id$ * * Author W. Eric Norum - * norume@aps.anl.gov - * 630 252 4793 */ #include #include @@ -109,7 +107,7 @@ receiver(void *arg) testDiag("Sender %d -- %d messages", sender, expectmsg[sender-1]-1); } if (!testOk1(errors == 0)) - testDiag("error count was %d", errors); + testDiag("Receiver finished, error count was %d", errors); epicsEventSignal(finished); } @@ -127,6 +125,7 @@ sender(void *arg) epicsThreadSleep(0.005 * (randBelow(5))); epicsThreadSleep(0.005 * (randBelow(20))); } + testDiag("%s exiting, sent %d messages", epicsThreadGetNameSelf(), i-1); } extern "C" void messageQueueTest(void *parm) @@ -293,6 +292,7 @@ MAIN(epicsMessageQueueTest) messageQueueTest, NULL); (void) epicsEventWait(finished); + epicsThreadSleep(1.0); return testDone(); }