diff --git a/src/ioc/db/dbCa.c b/src/ioc/db/dbCa.c index 965115f3d..3c5948401 100644 --- a/src/ioc/db/dbCa.c +++ b/src/ioc/db/dbCa.c @@ -13,7 +13,7 @@ * Date: 26MAR96 * */ - +#define EPICS_DBCA_PRIVATE_API #include #include #include @@ -191,6 +191,42 @@ static void caLinkDec(caLink *pca) if (callback) callback(userPvt); } +/* Block until worker thread has processed all previously queued actions. + * Does not prevent additional actions from being queued. + */ +void dbCaSync(void) +{ + epicsEventId wake; + caLink templink; + + /* we only partially initialize templink. + * It has no link field and no subscription + * so the worker must handle it early + */ + memset(&templink, 0, sizeof(templink)); + templink.refcount = 1; + + wake = epicsEventMustCreate(epicsEventEmpty); + templink.lock = epicsMutexMustCreate(); + + templink.userPvt = wake; + + addAction(&templink, CA_SYNC); + + epicsEventMustWait(wake); + /* Worker holds workListLock when calling epicsEventMustTrigger() + * we cycle through workListLock to ensure worker call to + * epicsEventMustTrigger() returns before we destroy the event. + */ + epicsMutexMustLock(workListLock); + epicsMutexUnlock(workListLock); + + assert(templink.refcount==1); + + epicsMutexDestroy(templink.lock); + epicsEventDestroy(wake); +} + void dbCaCallbackProcess(void *userPvt) { struct link *plink = (struct link *)userPvt; @@ -947,9 +983,13 @@ static void dbCaTask(void *arg) break; /* workList is empty */ } link_action = pca->link_action; + if (link_action&CA_SYNC) + epicsEventMustTrigger((epicsEventId)pca->userPvt); /* dbCaSync() requires workListLock to be held here */ pca->link_action = 0; if (link_action & CA_CLEAR_CHANNEL) --removesOutstanding; epicsMutexUnlock(workListLock); /* Give back immediately */ + if (link_action&CA_SYNC) + continue; if (link_action & CA_CLEAR_CHANNEL) { /* This must be first */ caLinkDec(pca); /* No alarm is raised. Since link is changing so what? */ diff --git a/src/ioc/db/dbCa.h b/src/ioc/db/dbCa.h index cfdf7321f..85100c48f 100644 --- a/src/ioc/db/dbCa.h +++ b/src/ioc/db/dbCa.h @@ -71,6 +71,10 @@ epicsShareFunc long dbCaGetUnits(const struct link *plink, extern struct ca_client_context * dbCaClientContext; +#ifdef EPICS_DBCA_PRIVATE_API +epicsShareFunc void dbCaSync(void); +#endif + #ifdef __cplusplus } #endif diff --git a/src/ioc/db/dbCaPvt.h b/src/ioc/db/dbCaPvt.h index 6149d08ae..b5fc33635 100644 --- a/src/ioc/db/dbCaPvt.h +++ b/src/ioc/db/dbCaPvt.h @@ -32,6 +32,7 @@ #define CA_MONITOR_NATIVE 0x10 #define CA_MONITOR_STRING 0x20 #define CA_GET_ATTRIBUTES 0x40 +#define CA_SYNC 0x1000 /* write type */ #define CA_PUT 0x1 #define CA_PUT_CALLBACK 0x2 diff --git a/src/std/rec/test/linkRetargetLinkTest.c b/src/std/rec/test/linkRetargetLinkTest.c index e829fa6e0..894c94587 100644 --- a/src/std/rec/test/linkRetargetLinkTest.c +++ b/src/std/rec/test/linkRetargetLinkTest.c @@ -9,6 +9,7 @@ * * Test using several stringout records to retarget the link of another record */ +#define EPICS_DBCA_PRIVATE_API #include @@ -37,6 +38,13 @@ static void testRetarget(void) eltc(0); testIocInitOk(); eltc(1); + /* wait for local CA links to be connected or dbPutField() will fail */ + /* wait for initial CA_CONNECT actions to be processed. + * Assume that local CA links deliver callbacks synchronously + * eg. that ca_create_channel() will invoke the connection callback + * before returning. + */ + dbCaSync(); lnkmon = testMonitorCreate("rec:ai.INP", DBE_VALUE, 0); valmon = testMonitorCreate("rec:ai", DBE_VALUE, 0);