Avoid race in linkRetarget

Add dbCaSync() to avoid a race between a call to dbCaGetField()
and the link becoming connected.
This commit is contained in:
Michael Davidsaver
2016-02-16 15:07:40 -05:00
parent b4fd19e7db
commit 2be59e985d
4 changed files with 54 additions and 1 deletions
+41 -1
View File
@@ -13,7 +13,7 @@
* Date: 26MAR96
*
*/
#define EPICS_DBCA_PRIVATE_API
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
@@ -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? */
+4
View File
@@ -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
+1
View File
@@ -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
+8
View File
@@ -9,6 +9,7 @@
*
* Test using several stringout records to retarget the link of another record
*/
#define EPICS_DBCA_PRIVATE_API
#include <string.h>
@@ -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);