From a46bd5ae88087bbadab0d547141146ebf4d46dc8 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Sat, 20 Sep 2025 14:18:12 -0500 Subject: [PATCH] dbCa: iocInit wait for local CA links to connect --- documentation/new-notes/PR-713.md | 8 +++++ modules/database/src/ioc/db/dbCa.c | 35 +++++++++++++++++---- modules/database/src/ioc/db/dbCaPvt.h | 21 +++++++++++++ modules/database/src/ioc/db/dbLink.c | 6 ++-- modules/database/test/ioc/db/dbCaLinkTest.c | 7 ----- 5 files changed, 62 insertions(+), 15 deletions(-) create mode 100644 documentation/new-notes/PR-713.md diff --git a/documentation/new-notes/PR-713.md b/documentation/new-notes/PR-713.md new file mode 100644 index 000000000..c647c39bc --- /dev/null +++ b/documentation/new-notes/PR-713.md @@ -0,0 +1,8 @@ +### iocInit wait for local CA links to connect + +PR [713](https://github.com/epics-base/epics-base/pull/713) + +During iocInit(), wait for local CA links to connect. +With this change, database authors can be certain that +any local CA link is connected prior to `initHookAfterIocRunning` +and `field(PINI, RUNNING)`. diff --git a/modules/database/src/ioc/db/dbCa.c b/modules/database/src/ioc/db/dbCa.c index 660fc5223..0a0da33df 100644 --- a/modules/database/src/ioc/db/dbCa.c +++ b/modules/database/src/ioc/db/dbCa.c @@ -40,6 +40,7 @@ /* We can't include dbStaticLib.h here */ #define dbCalloc(nobj,size) callocMustSucceed(nobj,size,"dbCalloc") +#include #include "db_access_routines.h" #include "dbCa.h" #include "dbCaPvt.h" @@ -64,6 +65,7 @@ extern int dbServiceIsolate; static ELLLIST workList = ELLLIST_INIT; /* Work list for dbCaTask */ static epicsMutexId workListLock; /*Mutual exclusions semaphores for workList*/ static epicsEventId workListEvent; /*wakeup event for dbCaTask*/ +static size_t initOutstanding; static int removesOutstanding = 0; #define removesOutstandingWarning 10000 @@ -340,7 +342,7 @@ static void dbCaLinkInitImpl(int isolate) dbCaCtl = ctlPause; dbCaWorker = epicsThreadCreateOpt("dbCaLink", dbCaTask, NULL, &opts); - /* wait for worker to startup and initialize dbCaClientContext */ + /* wait for worker to startup, initialize dbCaClientContext, and connect local CA */ epicsEventMustWait(startStopEvent); } @@ -369,11 +371,12 @@ void dbCaPause(void) epicsEventSignal(workListEvent); } } - -void dbCaAddLinkCallback(struct link *plink, - dbCaCallback connect, dbCaCallback monitor, void *userPvt) +void dbCaAddLinkCallbackOpt(struct dbLocker *locker, struct link *plink, + dbCaCallback connect, dbCaCallback monitor, void *userPvt, + unsigned flags) { caLink *pca; + (void)locker; /* Passed for symmetry with dbDbAddLink(). So far unused. */ assert(!plink->value.pv_link.pvt); @@ -385,6 +388,10 @@ void dbCaAddLinkCallback(struct link *plink, pca->connect = connect; pca->monitor = monitor; pca->userPvt = userPvt; + pca->flags = flags; + + if(flags & DBCA_CALLBACK_INIT_WAIT) + epicsAtomicIncrSizeT(&initOutstanding); epicsMutexMustLock(pca->lock); plink->lset = &dbCa_lset; @@ -394,15 +401,22 @@ void dbCaAddLinkCallback(struct link *plink, epicsMutexUnlock(pca->lock); } +void dbCaAddLinkCallback(struct link *plink, + dbCaCallback connect, dbCaCallback monitor, void *userPvt) +{ + dbCaAddLinkCallbackOpt(NULL, plink, connect, monitor, userPvt, 0); +} + long dbCaAddLink(struct dbLocker *locker, struct link *plink, short dbfType) { - dbCaAddLinkCallback(plink, 0, 0, NULL); + dbCaAddLinkCallbackOpt(locker, plink, 0, 0, NULL, 0); return 0; } void dbCaRemoveLink(struct dbLocker *locker, struct link *plink) { caLink *pca = (caLink *)plink->value.pv_link.pvt; + (void)locker; /* Passed for symmetry with dbDbRemoveLink(). So far unused. */ if (!pca) return; epicsMutexMustLock(pca->lock); @@ -962,6 +976,10 @@ static void eventCallback(struct event_handler_args arg) } } done: + if(pca->flags & DBCA_CALLBACK_INIT_WAIT) { + pca->flags &= ~DBCA_CALLBACK_INIT_WAIT; + addAction(pca, CA_INIT_WAIT); + } epicsMutexUnlock(pca->lock); if (monitor) monitor(userPvt); } @@ -1099,7 +1117,9 @@ static void dbCaTask(void *arg) dbCaClientContext = ca_current_context (); SEVCHK(ca_add_exception_event(exceptionCallback,NULL), "ca_add_exception_event"); - epicsEventSignal(startStopEvent); + if(epicsAtomicGetSizeT(&initOutstanding)==0) + epicsEventSignal(startStopEvent); + // else: defer to CA_INIT_WAIT /* channel access event loop */ while (TRUE){ @@ -1255,6 +1275,9 @@ static void dbCaTask(void *arg) db_process(prec); dbScanUnlock(prec); } + if ((link_action & CA_INIT_WAIT) && epicsAtomicDecrSizeT(&initOutstanding)==0) { + epicsEventSignal(startStopEvent); + } } SEVCHK(ca_flush_io(), "dbCaTask"); } diff --git a/modules/database/src/ioc/db/dbCaPvt.h b/modules/database/src/ioc/db/dbCaPvt.h index 1ee0745e0..185b6104f 100644 --- a/modules/database/src/ioc/db/dbCaPvt.h +++ b/modules/database/src/ioc/db/dbCaPvt.h @@ -21,6 +21,19 @@ #include "epicsMutex.h" #include "epicsTypes.h" #include "link.h" +#include "shareLib.h" +#include "libCaAPI.h" + +#ifndef INC_cadef_H +/* Copy some definitions so this header to be included from + * places where cadef.h and db_access.h can not. + */ +typedef void * chid; +typedef void * evid; +LIBCA_API extern const unsigned short dbr_value_size[]; +LIBCA_API short epicsShareAPI ca_field_type (chid chan); +#define MAX_UNITS_SIZE 8 +#endif /* link_action mask */ #define CA_CLEAR_CHANNEL 0x1 @@ -32,6 +45,7 @@ #define CA_GET_ATTRIBUTES 0x40 #define CA_SYNC 0x1000 #define CA_DBPROCESS 0x2000 +#define CA_INIT_WAIT 0x4000 /* write type */ #define CA_PUT 0x1 #define CA_PUT_CALLBACK 0x2 @@ -63,6 +77,7 @@ typedef struct caLink dbCaCallback connect; dbCaCallback monitor; void *userPvt; + unsigned flags; /* The following are for write request */ short putType; dbCaCallback putCallback; @@ -97,4 +112,10 @@ typedef struct caLink unsigned long nUpdate; }caLink; +#define DBCA_CALLBACK_INIT_WAIT (1) + +void dbCaAddLinkCallbackOpt(struct dbLocker *locker, struct link *plink, + dbCaCallback connect, dbCaCallback monitor, + void *userPvt, unsigned flags); + #endif /* INC_dbCaPvt_H */ diff --git a/modules/database/src/ioc/db/dbLink.c b/modules/database/src/ioc/db/dbLink.c index a2819d553..e03650eab 100644 --- a/modules/database/src/ioc/db/dbLink.c +++ b/modules/database/src/ioc/db/dbLink.c @@ -31,7 +31,7 @@ #include "dbAccessDefs.h" #include "dbAddr.h" #include "dbBase.h" -#include "dbCa.h" +#include "dbCaPvt.h" #include "dbCommon.h" #include "dbConstLink.h" #include "dbDbLink.h" @@ -125,7 +125,9 @@ void dbInitLink(struct link *plink, short dbfType) if (dbfType == DBF_INLINK) plink->value.pv_link.pvlMask |= pvlOptInpNative; - dbCaAddLink(NULL, plink, dbfType); + int isLocal = dbChannelTest(plink->value.pv_link.pvname)==0; + + dbCaAddLinkCallbackOpt(NULL, plink, NULL, NULL, NULL, isLocal ? DBCA_CALLBACK_INIT_WAIT : 0); if (dbfType == DBF_FWDLINK) { char *pperiod = strrchr(plink->value.pv_link.pvname, '.'); diff --git a/modules/database/test/ioc/db/dbCaLinkTest.c b/modules/database/test/ioc/db/dbCaLinkTest.c index 8243a6c82..c50649910 100644 --- a/modules/database/test/ioc/db/dbCaLinkTest.c +++ b/modules/database/test/ioc/db/dbCaLinkTest.c @@ -29,13 +29,6 @@ #include "dbEvent.h" #include "shareLib.h" -/* Declarations from cadef.h and db_access.h which we can't include here */ -typedef void * chid; -typedef void * evid; -epicsShareExtern const unsigned short dbr_value_size[]; -epicsShareExtern short epicsShareAPI ca_field_type (chid chan); -#define MAX_UNITS_SIZE 8 - #include "dbCaPvt.h" #include "errlog.h" #include "testMain.h"