From 2d1d14e4b49f5c5eafef5afd570b51bda8ddd3c4 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Tue, 6 Jun 2017 23:18:55 -0400 Subject: [PATCH 01/18] Make rsrv register itself using a DBD file This allows the rsrv to be excluded by removing 'include rsrv.dbd' from a local copy of the base.dbd file. Server initialization, running and pausing are moved to new methods of the dbServer object, and the existing registration path is converted to just use the new rsrv.dbd file. --- src/ioc/db/dbServer.c | 16 ++++++++++++ src/ioc/db/dbServer.h | 11 ++++++++ src/ioc/misc/iocInit.c | 14 +++++----- src/ioc/misc/iocshRegisterCommon.c | 4 +-- src/ioc/rsrv/Makefile | 6 ++--- src/ioc/rsrv/caservertask.c | 42 ++++++++++++++++-------------- src/ioc/rsrv/rsrv.dbd | 3 +++ src/ioc/rsrv/rsrv.h | 6 ++--- src/ioc/rsrv/rsrvIocRegister.c | 11 +++++--- src/ioc/rsrv/rsrvIocRegister.h | 25 ------------------ src/std/softIoc/base.dbd | 2 ++ 11 files changed, 75 insertions(+), 65 deletions(-) create mode 100644 src/ioc/rsrv/rsrv.dbd delete mode 100644 src/ioc/rsrv/rsrvIocRegister.h diff --git a/src/ioc/db/dbServer.c b/src/ioc/db/dbServer.c index bc1094ce7..cb3830230 100644 --- a/src/ioc/db/dbServer.c +++ b/src/ioc/db/dbServer.c @@ -56,3 +56,19 @@ int dbServerClient(char *pBuf, size_t bufSize) return -1; } +#define STARTSTOP(routine, method) \ +void routine(void) \ +{ \ + dbServer *psrv = (dbServer *)ellFirst(&serverList); \ +\ + while (psrv) { \ + if (psrv->method) \ + psrv->method(); \ + psrv = (dbServer *)ellNext(&psrv->node); \ + } \ +} + +STARTSTOP(dbInitServers, init) +STARTSTOP(dbRunServers, run) +STARTSTOP(dbPauseServers, pause) +STARTSTOP(dbStopServers, stop) diff --git a/src/ioc/db/dbServer.h b/src/ioc/db/dbServer.h index 345468676..0ed7d3799 100644 --- a/src/ioc/db/dbServer.h +++ b/src/ioc/db/dbServer.h @@ -36,6 +36,12 @@ typedef struct dbServer { /* Get identity of client initiating the calling thread */ /* Must return 0 (OK), or -1 (ERROR) from unknown threads */ int (* client) (char *pBuf, size_t bufSize); + + /* Start/stop methods */ + void (* init) (void); + void (* run) (void); + void (* pause) (void); + void (* stop) (void); } dbServer; @@ -51,6 +57,11 @@ epicsShareFunc void dbsr(unsigned level); epicsShareFunc int dbServerClient(char *pBuf, size_t bufSize); +epicsShareFunc void dbInitServers(void); +epicsShareFunc void dbRunServers(void); +epicsShareFunc void dbPauseServers(void); +epicsShareFunc void dbStopServers(void); + #ifdef __cplusplus } #endif diff --git a/src/ioc/misc/iocInit.c b/src/ioc/misc/iocInit.c index 98f7dae11..738d9b472 100644 --- a/src/ioc/misc/iocInit.c +++ b/src/ioc/misc/iocInit.c @@ -6,7 +6,7 @@ * Copyright (c) 2013 Helmholtz-Zentrum Berlin * für Materialien und Energie GmbH. * EPICS BASE is distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Original Author: Marty Kraimer @@ -53,6 +53,7 @@ #include "dbLock.h" #include "dbNotify.h" #include "dbScan.h" +#include "dbServer.h" #include "dbStaticLib.h" #include "dbStaticPvt.h" #include "devSup.h" @@ -69,7 +70,6 @@ #include "registryDriverSupport.h" #include "registryJLinks.h" #include "registryRecordType.h" -#include "rsrv.h" static enum { iocVirgin, iocBuilding, iocBuilt, iocRunning, iocPaused, iocStopped @@ -203,8 +203,7 @@ int iocBuild(void) status = iocBuild_2(); if (status) return status; - /* Start CA server threads */ - rsrv_init(); + dbInitServers(); status = iocBuild_3(); @@ -247,7 +246,8 @@ int iocRun(void) if (iocState == iocBuilt) initHookAnnounce(initHookAfterInterruptAccept); - rsrv_run(); + dbRunServers(); + initHookAnnounce(initHookAfterCaServerRunning); if (iocState == iocBuilt) initHookAnnounce(initHookAtEnd); @@ -268,7 +268,7 @@ int iocPause(void) } initHookAnnounce(initHookAtIocPause); - rsrv_pause(); + dbPauseServers(); initHookAnnounce(initHookAfterCaServerPaused); dbCaPause(); @@ -421,7 +421,7 @@ static void initRecSup(void) static void initDevSup(void) { dbRecordType *pdbRecordType; - + for (pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList); pdbRecordType; pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node)) { diff --git a/src/ioc/misc/iocshRegisterCommon.c b/src/ioc/misc/iocshRegisterCommon.c index fefa716b9..4f8ce6646 100644 --- a/src/ioc/misc/iocshRegisterCommon.c +++ b/src/ioc/misc/iocshRegisterCommon.c @@ -4,7 +4,7 @@ * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* in file LICENSE that is included with this distribution. \*************************************************************************/ #include "envDefs.h" @@ -21,7 +21,6 @@ #include "iocshRegisterCommon.h" #include "miscIocRegister.h" #include "registryIocRegister.h" -#include "rsrvIocRegister.h" #define quote(v) #v #define str(v) quote(v) @@ -51,7 +50,6 @@ void iocshRegisterCommon(void) dbIocRegister(); dbtoolsIocRegister(); asIocRegister(); - rsrvIocRegister(); miscIocRegister(); libComRegister(); } diff --git a/src/ioc/rsrv/Makefile b/src/ioc/rsrv/Makefile index ba6ed6bd6..8ad7d01c4 100644 --- a/src/ioc/rsrv/Makefile +++ b/src/ioc/rsrv/Makefile @@ -4,7 +4,7 @@ # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found -# in file LICENSE that is included with this distribution. +# in file LICENSE that is included with this distribution. #************************************************************************* # This is a Makefile fragment, see src/ioc/Makefile. @@ -16,7 +16,8 @@ caserverio_INCLUDES = -I$(SRC)/ca/client camessage_INCLUDES = -I$(SRC)/ca/client INC += rsrv.h -INC += rsrvIocRegister.h + +DBD += rsrv.dbd dbCore_SRCS += caserverio.c dbCore_SRCS += caservertask.c @@ -25,4 +26,3 @@ dbCore_SRCS += camessage.c dbCore_SRCS += cast_server.c dbCore_SRCS += online_notify.c dbCore_SRCS += rsrvIocRegister.c - diff --git a/src/ioc/rsrv/caservertask.c b/src/ioc/rsrv/caservertask.c index 37c721f8a..eb18b0d40 100644 --- a/src/ioc/rsrv/caservertask.c +++ b/src/ioc/rsrv/caservertask.c @@ -469,18 +469,11 @@ void rsrv_build_addr_lists(void) } } -static dbServer rsrv_server = { - ELLNODE_INIT, - "rsrv", - casr, - casStatsFetch, - casClientInitiatingCurrentThread -}; - /* * rsrv_init () */ -int rsrv_init (void) +static +void rsrv_init (void) { long maxBytesAsALong; long status; @@ -499,8 +492,6 @@ int rsrv_init (void) rsrvCurrentClient = epicsThreadPrivateCreate (); - dbRegisterServer(&rsrv_server); - if ( envGetConfigParamPtr ( &EPICS_CAS_SERVER_PORT ) ) { ca_server_port = envGetInetPortConfigParam ( &EPICS_CAS_SERVER_PORT, (unsigned short) CA_SERVER_PORT ); @@ -768,26 +759,22 @@ int rsrv_init (void) &rsrv_online_notify_task, NULL); epicsEventMustWait(beacon_startStopEvent); - - return RSRV_OK; } -int rsrv_run (void) +static +void rsrv_run (void) { castcp_ctl = ctlRun; casudp_ctl = ctlRun; beacon_ctl = ctlRun; - - return RSRV_OK; } -int rsrv_pause (void) +static +void rsrv_pause (void) { beacon_ctl = ctlPause; casudp_ctl = ctlPause; castcp_ctl = ctlPause; - - return RSRV_OK; } static unsigned countChanListBytes ( @@ -1551,3 +1538,20 @@ void casStatsFetch ( unsigned *pChanCount, unsigned *pCircuitCount ) } UNLOCK_CLIENTQ; } + + +static dbServer rsrv_server = { + ELLNODE_INIT, + "rsrv", + casr, + casStatsFetch, + casClientInitiatingCurrentThread, + rsrv_init, + rsrv_run, + rsrv_pause +}; + +void rsrv_register_server(void) +{ + dbRegisterServer(&rsrv_server); +} diff --git a/src/ioc/rsrv/rsrv.dbd b/src/ioc/rsrv/rsrv.dbd new file mode 100644 index 000000000..0c3118f82 --- /dev/null +++ b/src/ioc/rsrv/rsrv.dbd @@ -0,0 +1,3 @@ +# This DBD file links the RSRV CA server into the IOC + +registrar(rsrvRegistrar) diff --git a/src/ioc/rsrv/rsrv.h b/src/ioc/rsrv/rsrv.h index 10947cdf0..cb966df2f 100644 --- a/src/ioc/rsrv/rsrv.h +++ b/src/ioc/rsrv/rsrv.h @@ -4,7 +4,7 @@ * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* in file LICENSE that is included with this distribution. \*************************************************************************/ /* @@ -27,9 +27,7 @@ extern "C" { #endif -epicsShareFunc int rsrv_init(void); -epicsShareFunc int rsrv_run(void); -epicsShareFunc int rsrv_pause(void); +epicsShareFunc void rsrv_register_server(void); epicsShareFunc void casr (unsigned level); epicsShareFunc int casClientInitiatingCurrentThread ( diff --git a/src/ioc/rsrv/rsrvIocRegister.c b/src/ioc/rsrv/rsrvIocRegister.c index 7a01fc9f6..74336817d 100644 --- a/src/ioc/rsrv/rsrvIocRegister.c +++ b/src/ioc/rsrv/rsrvIocRegister.c @@ -4,14 +4,14 @@ * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* in file LICENSE that is included with this distribution. \*************************************************************************/ #include "iocsh.h" +#include "epicsExport.h" #define epicsExportSharedSymbols #include "rsrv.h" -#include "rsrvIocRegister.h" /* casr */ static const iocshArg casrArg0 = { "level",iocshArgInt}; @@ -22,8 +22,11 @@ static void casrCallFunc(const iocshArgBuf *args) casr(args[0].ival); } - -void rsrvIocRegister(void) +static +void rsrvRegistrar(void) { + rsrv_register_server(); iocshRegister(&casrFuncDef,casrCallFunc); } + +epicsExportRegistrar(rsrvRegistrar); diff --git a/src/ioc/rsrv/rsrvIocRegister.h b/src/ioc/rsrv/rsrvIocRegister.h deleted file mode 100644 index f4c7e31f1..000000000 --- a/src/ioc/rsrv/rsrvIocRegister.h +++ /dev/null @@ -1,25 +0,0 @@ -/*************************************************************************\ -* Copyright (c) 2007 The University of Chicago, as Operator of Argonne -* National Laboratory. -* Copyright (c) 2002 The Regents of the University of California, as -* Operator of Los Alamos National Laboratory. -* EPICS BASE is distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. -\*************************************************************************/ - -#ifndef INC_rsrvIocRegister_H -#define INC_rsrvIocRegister_H - -#include "shareLib.h" - -#ifdef __cplusplus -extern "C" { -#endif - -epicsShareFunc void rsrvIocRegister(void); - -#ifdef __cplusplus -} -#endif - -#endif /* INC_rsrvIocRegister_H */ diff --git a/src/std/softIoc/base.dbd b/src/std/softIoc/base.dbd index 58f4884ea..564a83845 100644 --- a/src/std/softIoc/base.dbd +++ b/src/std/softIoc/base.dbd @@ -26,3 +26,5 @@ include "asSub.dbd" # IOC Core variables include "dbCore.dbd" +# RSRV server +include "rsrv.dbd" From 6efe292494a8ad41ee5d0d298787195375abc8c1 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Tue, 13 Jun 2017 23:36:30 -0500 Subject: [PATCH 02/18] Add EPICS_IOC_IGNORE_SERVERS environment variable dbServers cannot be registered when their names appear in this variable, so they should remain inactive. This feature is mainly intended for debugging, so the crude name search should be OK. --- configure/CONFIG_ENV | 3 +++ src/ioc/db/dbServer.c | 19 +++++++++++++++++++ src/ioc/db/dbServer.h | 2 ++ src/libCom/env/envDefs.h | 1 + 4 files changed, 25 insertions(+) diff --git a/configure/CONFIG_ENV b/configure/CONFIG_ENV index 1565b2ec9..59e72d883 100644 --- a/configure/CONFIG_ENV +++ b/configure/CONFIG_ENV @@ -47,6 +47,9 @@ EPICS_CAS_SERVER_PORT= EPICS_CAS_INTF_ADDR_LIST="" EPICS_CAS_IGNORE_ADDR_LIST="" +# Servers to disable +EPICS_IOC_IGNORE_SERVERS="" + # Log Server: # EPICS_IOC_LOG_PORT Log server port number etc. EPICS_IOC_LOG_PORT=7004 diff --git a/src/ioc/db/dbServer.c b/src/ioc/db/dbServer.c index cb3830230..e75622904 100644 --- a/src/ioc/db/dbServer.c +++ b/src/ioc/db/dbServer.c @@ -10,8 +10,10 @@ */ #include +#include #include "ellLib.h" +#include "envDefs.h" #include "epicsStdio.h" #define epicsExportSharedSymbols @@ -22,6 +24,23 @@ static ELLLIST serverList = ELLLIST_INIT; void dbRegisterServer(dbServer *psrv) { + const char * ignore = envGetConfigParamPtr(&EPICS_IOC_IGNORE_SERVERS); + + if (!psrv || !psrv->name) + return; + + if (strchr(psrv->name, ' ')) { + fprintf(stderr, "dbRegisterServer: Bad server name '%s'\n", + psrv->name); + return; + } + + if (ignore && strstr(ignore, psrv->name)) { + fprintf(stderr, "dbRegisterServer: Ignoring '%s', per environment\n", + psrv->name); + return; + } + if (ellNext(&psrv->node)) { fprintf(stderr, "dbRegisterServer: '%s' registered twice?\n", psrv->name); diff --git a/src/ioc/db/dbServer.h b/src/ioc/db/dbServer.h index 0ed7d3799..83d065ce9 100644 --- a/src/ioc/db/dbServer.h +++ b/src/ioc/db/dbServer.h @@ -25,6 +25,8 @@ extern "C" { typedef struct dbServer { ELLNODE node; + + /* A short server identifier, printable, no spaces */ const char *name; /* Print level-dependent status report to stdout */ diff --git a/src/libCom/env/envDefs.h b/src/libCom/env/envDefs.h index 650f8ee58..13df94733 100644 --- a/src/libCom/env/envDefs.h +++ b/src/libCom/env/envDefs.h @@ -65,6 +65,7 @@ epicsShareExtern const ENV_PARAM EPICS_BUILD_OS_CLASS; epicsShareExtern const ENV_PARAM EPICS_BUILD_TARGET_ARCH; epicsShareExtern const ENV_PARAM EPICS_TIMEZONE; epicsShareExtern const ENV_PARAM EPICS_TS_NTP_INET; +epicsShareExtern const ENV_PARAM EPICS_IOC_IGNORE_SERVERS; epicsShareExtern const ENV_PARAM EPICS_IOC_LOG_PORT; epicsShareExtern const ENV_PARAM EPICS_IOC_LOG_INET; epicsShareExtern const ENV_PARAM EPICS_IOC_LOG_FILE_LIMIT; From c2d22ed925a90ae965c708801209f3700730022c Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sat, 24 Jun 2017 00:04:20 -0500 Subject: [PATCH 03/18] Added Doxygen comments for dbServer API --- src/ioc/db/dbServer.h | 122 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 108 insertions(+), 14 deletions(-) diff --git a/src/ioc/db/dbServer.h b/src/ioc/db/dbServer.h index 83d065ce9..f1a48ab40 100644 --- a/src/ioc/db/dbServer.h +++ b/src/ioc/db/dbServer.h @@ -5,8 +5,17 @@ * in file LICENSE that is included with this distribution. \*************************************************************************/ -/* - * Author: Andrew Johnson +/** + * @file dbServer.h + * @author Andrew Johnson + * + * @brief The IOC's interface to the server layers that publish its PVs. + * + * All server layers which publish IOC record data should initialize a + * dbServer structure and register it with the IOC. The methods that + * the dbServer interface provides allow the IOC to start, pause and stop + * the servers together, and to provide status and debugging information + * to the IOC user/developer through a common set of commands. */ #ifndef INC_dbServer_H @@ -21,47 +30,132 @@ extern "C" { #endif -/* Server information structure */ +/** @brief Server information structure. + * + * Every server layer should initialize and register an instance of this + * structure with the IOC by passing it to the dbRegisterServer() routine. + * + * All methods in this struct are optional; use @c NULL if a server is + * unable to support a particular operation (or if it hasn't been + * implemented yet). + */ typedef struct dbServer { + /** @brief Linked list node; initialize to @c ELLNODE_INIT */ ELLNODE node; - /* A short server identifier, printable, no spaces */ + /** @brief A short server identifier; printable, with no spaces */ const char *name; - /* Print level-dependent status report to stdout */ + /** @brief Print level-dependent status report to stdout. + * + * @param level Interest level, specifies how much detail to print. + */ void (* report) (unsigned level); - /* Get number of channels and clients connected */ + /** @brief Get number of channels and clients currently connected. + * + * @param channels NULL or pointer for returning channel count. + * @param clients NULL or pointer for returning client count. + */ void (* stats) (unsigned *channels, unsigned *clients); - /* Get identity of client initiating the calling thread */ - /* Must return 0 (OK), or -1 (ERROR) from unknown threads */ + /** @brief Get identity of client initiating the calling thread. + * + * Must fill in the buffer with the client's identity when called from a + * thread that belongs to this server layer. For other threads, the + * method should do nothing, just return -1. + * @param pBuf Buffer for client identity string. + * @param bufSize Number of chars available in pBuf. + * @return -1 means calling thread is not owned by this server. + * 0 means the thread was recognized and pBuf has been filled in. + */ int (* client) (char *pBuf, size_t bufSize); - /* Start/stop methods */ + /** @name Control Methods + * These control methods for the server will be called by routines + * related to iocInit for all registered servers in turn when the IOC + * is being initialized, run, paused and stopped respectively. + * + * @{ + */ + + /** @brief Server init method. + * + * Called for all registered servers by dbInitServers(). + */ void (* init) (void); + + /** @brief Server run method. + * + * Called for all registered servers by dbRunServers(). + */ void (* run) (void); + + /** @brief Server pause method. + * + * Called for all registered servers by dbPauseServers(). + */ void (* pause) (void); + + /** @brief Server stop method. + * + * Called for all registered servers by dbStopServers(). + */ void (* stop) (void); + + /** @} + */ } dbServer; +/** @brief Register a server layer with the IOC + * + * This should only be called once for each server layer. + * @param psrv Server information structure for the server + */ epicsShareFunc void dbRegisterServer(dbServer *psrv); -/* Extra routines could be added if/when needed: - * - * epicsShareFunc const dbServer* dbFindServer(const char *name); - * epicsShareFunc void dbIterateServers(srvIterFunc func, void *user); +/** @brief Print dbServer Reports. +* + * Calls the report methods of all registered servers. + * This routine is provided as an IOC Shell command. + * @param level Interest level, specifies how much detail to print. */ - epicsShareFunc void dbsr(unsigned level); +/** @brief Query servers for client's identity. + * + * This routine is called by code that wants to identify who (or what) + * is responsible for the thread which is currently running. Setting + * the @c TPRO field of a record is one way to trigger this; the identity + * of the calling thread is printed along with the record name whenever + * the record is subsequently processed. + */ epicsShareFunc int dbServerClient(char *pBuf, size_t bufSize); +/** @brief Initialize all registered servers. + * + * Calls all dbServer::init() methods. + */ epicsShareFunc void dbInitServers(void); + +/** @brief Run all registered servers. + * + * Calls all dbServer::run() methods. + */ epicsShareFunc void dbRunServers(void); + +/** @brief Pause all registered servers. + * + * Calls all dbServer::pause() methods. + */ epicsShareFunc void dbPauseServers(void); + +/** @brief Stop all registered servers. + * + * Calls all dbServer::stop() methods. + */ epicsShareFunc void dbStopServers(void); #ifdef __cplusplus From 17e6060d163cfccab1da3d9ecdcef30603e5bff8 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sun, 23 Jul 2017 23:30:04 -0500 Subject: [PATCH 04/18] Added some dbServer unit tests --- src/ioc/db/dbServer.h | 4 + src/ioc/db/test/Makefile | 8 +- src/ioc/db/test/dbServerTest.c | 131 ++++++++++++++++++++++++++++++ src/ioc/db/test/epicsRunDbTests.c | 2 + 4 files changed, 143 insertions(+), 2 deletions(-) create mode 100644 src/ioc/db/test/dbServerTest.c diff --git a/src/ioc/db/dbServer.h b/src/ioc/db/dbServer.h index f1a48ab40..c2ebdb467 100644 --- a/src/ioc/db/dbServer.h +++ b/src/ioc/db/dbServer.h @@ -16,6 +16,10 @@ * the dbServer interface provides allow the IOC to start, pause and stop * the servers together, and to provide status and debugging information * to the IOC user/developer through a common set of commands. + * + * @todo Should dbRegisterServer() return an error status value? + * No API is provided yet for calling stats() methods. + * Nothing in the IOC calls dbStopServers(), not sure where it should go. */ #ifndef INC_dbServer_H diff --git a/src/ioc/db/test/Makefile b/src/ioc/db/test/Makefile index 37ec3da74..ff464f02a 100644 --- a/src/ioc/db/test/Makefile +++ b/src/ioc/db/test/Makefile @@ -4,7 +4,7 @@ # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found -# in the file LICENSE that is included with this distribution. +# in the file LICENSE that is included with this distribution. #************************************************************************* TOP=../../../.. @@ -94,6 +94,11 @@ dbStateTest_SRCS += dbStateTest.c testHarness_SRCS += dbStateTest.c TESTS += dbStateTest +TESTPROD_HOST += dbServerTest +dbServerTest_SRCS += dbServerTest.c +testHarness_SRCS += dbServerTest.c +TESTS += dbServerTest + TESTPROD_HOST += dbCaStatsTest dbCaStatsTest_SRCS += dbCaStatsTest.c dbCaStatsTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp @@ -192,4 +197,3 @@ dbStressLock$(DEP): $(COMMON_DIR)/xRecord.h devx$(DEP): $(COMMON_DIR)/xRecord.h scanIoTest$(DEP): $(COMMON_DIR)/xRecord.h xRecord$(DEP): $(COMMON_DIR)/xRecord.h - diff --git a/src/ioc/db/test/dbServerTest.c b/src/ioc/db/test/dbServerTest.c new file mode 100644 index 000000000..6c474ecb4 --- /dev/null +++ b/src/ioc/db/test/dbServerTest.c @@ -0,0 +1,131 @@ +/*************************************************************************\ +* Copyright (c) 2017 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. + \*************************************************************************/ + +/* + * Author: Andrew Johnson + */ + +#include + +#include "dbServer.h" + +#include +#include + +enum { + NOTHING_CALLED, + REPORT_CALLED, + CLIENT_CALLED_UNKNOWN, + CLIENT_CALLED_KNOWN, + STATS_CALLED, + INIT_CALLED, + RUN_CALLED, + PAUSE_CALLED, + STOP_CALLED +} oneState; + +char *oneSim; + +void oneReport(unsigned level) +{ + oneState = REPORT_CALLED; +} + +void oneStats(unsigned *channels, unsigned *clients) +{ + oneState = STATS_CALLED; +} + +int oneClient(char *pbuf, size_t len) +{ + if (oneSim) { + strncpy(pbuf, oneSim, len); + oneState = CLIENT_CALLED_KNOWN; + oneSim = NULL; + return 0; + } + oneState = CLIENT_CALLED_UNKNOWN; + return -1; +} + +void oneInit(void) +{ + oneState = INIT_CALLED; +} + +void oneRun(void) +{ + oneState = RUN_CALLED; +} + +void onePause(void) +{ + oneState = PAUSE_CALLED; +} + +void oneStop(void) +{ + oneState = STOP_CALLED; +} + +dbServer one = { + ELLNODE_INIT, + "one", + oneReport, oneStats, oneClient, + oneInit, oneRun, onePause, oneStop +}; + +dbServer no_routines = { + ELLNODE_INIT, + "no-routines", + NULL, NULL, NULL, NULL, NULL, NULL, NULL +}; + +MAIN(dbServerTest) +{ + char name[16]; + char *theName = "The One"; + int status; + + testPlan(0); + + testDiag("Registering dbServer 'one'"); + dbRegisterServer(&one); + testOk1(oneState == NOTHING_CALLED); + + testDiag("Registering dbServer 'no-routines'"); + dbRegisterServer(&no_routines); + + dbInitServers(); + testOk(oneState == INIT_CALLED, "dbInitServers"); + + dbRunServers(); + testOk(oneState == RUN_CALLED, "dbRunServers"); + + dbPauseServers(); + testOk(oneState == PAUSE_CALLED, "dbPauseServers"); + + dbStopServers(); + testOk(oneState == STOP_CALLED, "dbStopServers"); + + dbsr(0); + testOk(oneState == REPORT_CALLED, "dbsr"); + + oneSim = NULL; + name[0] = 0; + status = dbServerClient(name, sizeof(name)); + testOk(status == -1 && name[0] == 0, + "dbServerClient mismatch"); + + oneSim = theName; + name[0] = 0; + status = dbServerClient(name, sizeof(name)); + testOk(status == 0 && strcmp(name, theName) == 0, + "dbServerClient match"); + + return testDone(); +} diff --git a/src/ioc/db/test/epicsRunDbTests.c b/src/ioc/db/test/epicsRunDbTests.c index 69f6b082e..4036268fa 100644 --- a/src/ioc/db/test/epicsRunDbTests.c +++ b/src/ioc/db/test/epicsRunDbTests.c @@ -20,6 +20,7 @@ int testdbConvert(void); int callbackTest(void); int callbackParallelTest(void); int dbStateTest(void); +int dbServerTest(void); int dbCaStatsTest(void); int dbShutdownTest(void); int dbScanTest(void); @@ -41,6 +42,7 @@ void epicsRunDbTests(void) runTest(callbackTest); runTest(callbackParallelTest); runTest(dbStateTest); + runTest(dbServerTest); runTest(dbCaStatsTest); runTest(dbShutdownTest); runTest(dbScanTest); From ba9763f5513da59fd36b0d0699bcfffb3a5fd90d Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sun, 23 Jul 2017 23:35:03 -0500 Subject: [PATCH 05/18] Set test plan --- src/ioc/db/test/dbServerTest.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ioc/db/test/dbServerTest.c b/src/ioc/db/test/dbServerTest.c index 6c474ecb4..769a07f41 100644 --- a/src/ioc/db/test/dbServerTest.c +++ b/src/ioc/db/test/dbServerTest.c @@ -91,7 +91,7 @@ MAIN(dbServerTest) char *theName = "The One"; int status; - testPlan(0); + testPlan(8); testDiag("Registering dbServer 'one'"); dbRegisterServer(&one); From 2cf997ddbfb66f573a58c45d5ed23de158d8c914 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Tue, 25 Jul 2017 00:38:34 -0500 Subject: [PATCH 06/18] Test EPICS_IOC_IGNORE_SERVERS variable --- src/ioc/db/test/dbServerTest.c | 37 ++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/src/ioc/db/test/dbServerTest.c b/src/ioc/db/test/dbServerTest.c index 769a07f41..83b3eb684 100644 --- a/src/ioc/db/test/dbServerTest.c +++ b/src/ioc/db/test/dbServerTest.c @@ -13,6 +13,7 @@ #include "dbServer.h" +#include #include #include @@ -73,25 +74,44 @@ void oneStop(void) } dbServer one = { - ELLNODE_INIT, - "one", + ELLNODE_INIT, "one", oneReport, oneStats, oneClient, oneInit, oneRun, onePause, oneStop }; + +/* Server layer for testing NULL methods */ + dbServer no_routines = { - ELLNODE_INIT, - "no-routines", - NULL, NULL, NULL, NULL, NULL, NULL, NULL + ELLNODE_INIT, "no-routines", + NULL, NULL, NULL, + NULL, NULL, NULL, NULL }; + +/* Server layer which should be disabled */ + +int disInitialized = 0; + +void disInit(void) +{ + disInitialized = 1; +} + +dbServer disabled = { + ELLNODE_INIT, "disabled", + NULL, NULL, NULL, + disInit, NULL, NULL, NULL +}; + + MAIN(dbServerTest) { char name[16]; char *theName = "The One"; int status; - testPlan(8); + testPlan(9); testDiag("Registering dbServer 'one'"); dbRegisterServer(&one); @@ -100,8 +120,13 @@ MAIN(dbServerTest) testDiag("Registering dbServer 'no-routines'"); dbRegisterServer(&no_routines); + epicsEnvSet("EPICS_IOC_IGNORE_SERVERS", "none disabled nonexistent"); + testDiag("Registering dbServer 'disabled'"); + dbRegisterServer(&disabled); + dbInitServers(); testOk(oneState == INIT_CALLED, "dbInitServers"); + testOk(disInitialized == 0, "Disabled server not initialized"); dbRunServers(); testOk(oneState == RUN_CALLED, "dbRunServers"); From 461daadd5bf229dfc8ef871ddcd2ed0626037f04 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Tue, 25 Jul 2017 00:39:40 -0500 Subject: [PATCH 07/18] Release Notes entry --- documentation/RELEASE_NOTES.html | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html index 7c5ade339..2735e40da 100644 --- a/documentation/RELEASE_NOTES.html +++ b/documentation/RELEASE_NOTES.html @@ -17,6 +17,19 @@ --> +

Extend the dbServer API with init/run/pause/stop methods

+ +

This change permits the IOC to be built that omits the CA server (RSRV) by +removing its registrar entry which is now provided in the new rsrv.dbd +file. Other server layers can be built into the IOC (alongside RSRV or in place +of it) by registering them in a similar manner. The dbServer API is documented +with Doxygen comments in the header file.

+ +

Specific IOC server layers can be disabled at runtime by adding their name to +the environment variable EPICS_IOC_IGNORE_SERVERS (separated by spaces if more +than one should be ignored).

+ +

Changes made between 3.16.0.1 and 3.16.1

IOC Database Support for 64-bit integers

From 7c6b037252e90e13f2e219e88976c0c67ccacf4a Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sat, 29 Jul 2017 00:51:31 -0500 Subject: [PATCH 08/18] Add explicit "no servers" message to dbsr() Make dbsr explicitly say so if no dbServer layers have been registered with the dbServer API. --- src/ioc/db/dbServer.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/ioc/db/dbServer.c b/src/ioc/db/dbServer.c index e75622904..9b7cccb8d 100644 --- a/src/ioc/db/dbServer.c +++ b/src/ioc/db/dbServer.c @@ -54,6 +54,11 @@ void dbsr(unsigned level) { dbServer *psrv = (dbServer *)ellFirst(&serverList); + if (!psrv) { + printf("No server layers registered with IOC\n"); + return; + } + while (psrv) { printf("Server '%s':\n", psrv->name); if (psrv->report) From d1f60e4c8c9babbf23046cc0e4506d2877e391a1 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sat, 29 Jul 2017 01:30:45 -0500 Subject: [PATCH 09/18] dbRegisterServer: Don't ignore sub-string names Also adjusts the tests to check we do it properly. --- src/ioc/db/dbServer.c | 18 ++++++++++++++---- src/ioc/db/test/dbServerTest.c | 5 ++++- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/ioc/db/dbServer.c b/src/ioc/db/dbServer.c index 9b7cccb8d..f20c63c4f 100644 --- a/src/ioc/db/dbServer.c +++ b/src/ioc/db/dbServer.c @@ -35,10 +35,20 @@ void dbRegisterServer(dbServer *psrv) return; } - if (ignore && strstr(ignore, psrv->name)) { - fprintf(stderr, "dbRegisterServer: Ignoring '%s', per environment\n", - psrv->name); - return; + if (ignore) { + size_t len = strlen(psrv->name); + const char *found; + while ((found = strstr(ignore, psrv->name))) { + /* Make sure the name isn't just a substring */ + if ((found == ignore || (found > ignore && found[-1] == ' ')) && + (found[len] == 0 || found[len] == ' ')) { + fprintf(stderr, "dbRegisterServer: Ignoring '%s', per environment\n", + psrv->name); + return; + } + /* It was, try again further down */ + ignore = found + len; + } } if (ellNext(&psrv->node)) { diff --git a/src/ioc/db/test/dbServerTest.c b/src/ioc/db/test/dbServerTest.c index 83b3eb684..c6dae1529 100644 --- a/src/ioc/db/test/dbServerTest.c +++ b/src/ioc/db/test/dbServerTest.c @@ -113,6 +113,9 @@ MAIN(dbServerTest) testPlan(9); + /* Prove that we handle substring names properly */ + epicsEnvSet("EPICS_IOC_IGNORE_SERVERS", "none ones"); + testDiag("Registering dbServer 'one'"); dbRegisterServer(&one); testOk1(oneState == NOTHING_CALLED); @@ -120,7 +123,7 @@ MAIN(dbServerTest) testDiag("Registering dbServer 'no-routines'"); dbRegisterServer(&no_routines); - epicsEnvSet("EPICS_IOC_IGNORE_SERVERS", "none disabled nonexistent"); + epicsEnvSet("EPICS_IOC_IGNORE_SERVERS", "disabled nonexistent"); testDiag("Registering dbServer 'disabled'"); dbRegisterServer(&disabled); From d1018d27a4b0cbd51dd6261c10b10fb075055530 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Tue, 19 Sep 2017 22:09:41 -0500 Subject: [PATCH 10/18] Fix check for double-registration, add test --- src/ioc/db/dbServer.c | 2 +- src/ioc/db/test/dbServerTest.c | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/ioc/db/dbServer.c b/src/ioc/db/dbServer.c index f20c63c4f..31ba11d80 100644 --- a/src/ioc/db/dbServer.c +++ b/src/ioc/db/dbServer.c @@ -51,7 +51,7 @@ void dbRegisterServer(dbServer *psrv) } } - if (ellNext(&psrv->node)) { + if (ellNext(&psrv->node) || ellLast(&serverList) == &psrv->node) { fprintf(stderr, "dbRegisterServer: '%s' registered twice?\n", psrv->name); return; diff --git a/src/ioc/db/test/dbServerTest.c b/src/ioc/db/test/dbServerTest.c index c6dae1529..31ffbd8b6 100644 --- a/src/ioc/db/test/dbServerTest.c +++ b/src/ioc/db/test/dbServerTest.c @@ -120,6 +120,9 @@ MAIN(dbServerTest) dbRegisterServer(&one); testOk1(oneState == NOTHING_CALLED); + testDiag("Expect double-registration warning for 'one':"); + dbRegisterServer(&one); + testDiag("Registering dbServer 'no-routines'"); dbRegisterServer(&no_routines); From a861760dbb84bcca8c92ced22e5c64dbbaaa8e10 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Tue, 19 Sep 2017 22:24:06 -0500 Subject: [PATCH 11/18] Give dbRegisterServer a return value (0/-1) Add tests to check registration failures. --- src/ioc/db/dbServer.c | 11 ++++++----- src/ioc/db/dbServer.h | 5 ++--- src/ioc/db/test/dbServerTest.c | 19 +++++++++++++------ 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/ioc/db/dbServer.c b/src/ioc/db/dbServer.c index 31ba11d80..47ed5d8e8 100644 --- a/src/ioc/db/dbServer.c +++ b/src/ioc/db/dbServer.c @@ -22,17 +22,17 @@ static ELLLIST serverList = ELLLIST_INIT; -void dbRegisterServer(dbServer *psrv) +int dbRegisterServer(dbServer *psrv) { const char * ignore = envGetConfigParamPtr(&EPICS_IOC_IGNORE_SERVERS); if (!psrv || !psrv->name) - return; + return -1; if (strchr(psrv->name, ' ')) { fprintf(stderr, "dbRegisterServer: Bad server name '%s'\n", psrv->name); - return; + return -1; } if (ignore) { @@ -44,7 +44,7 @@ void dbRegisterServer(dbServer *psrv) (found[len] == 0 || found[len] == ' ')) { fprintf(stderr, "dbRegisterServer: Ignoring '%s', per environment\n", psrv->name); - return; + return 0; } /* It was, try again further down */ ignore = found + len; @@ -54,10 +54,11 @@ void dbRegisterServer(dbServer *psrv) if (ellNext(&psrv->node) || ellLast(&serverList) == &psrv->node) { fprintf(stderr, "dbRegisterServer: '%s' registered twice?\n", psrv->name); - return; + return -1; } ellAdd(&serverList, &psrv->node); + return 0; } void dbsr(unsigned level) diff --git a/src/ioc/db/dbServer.h b/src/ioc/db/dbServer.h index c2ebdb467..1d184329c 100644 --- a/src/ioc/db/dbServer.h +++ b/src/ioc/db/dbServer.h @@ -17,8 +17,7 @@ * the servers together, and to provide status and debugging information * to the IOC user/developer through a common set of commands. * - * @todo Should dbRegisterServer() return an error status value? - * No API is provided yet for calling stats() methods. + * @todo No API is provided yet for calling stats() methods. * Nothing in the IOC calls dbStopServers(), not sure where it should go. */ @@ -118,7 +117,7 @@ typedef struct dbServer { * This should only be called once for each server layer. * @param psrv Server information structure for the server */ -epicsShareFunc void dbRegisterServer(dbServer *psrv); +epicsShareFunc int dbRegisterServer(dbServer *psrv); /** @brief Print dbServer Reports. * diff --git a/src/ioc/db/test/dbServerTest.c b/src/ioc/db/test/dbServerTest.c index 31ffbd8b6..e9c6556c5 100644 --- a/src/ioc/db/test/dbServerTest.c +++ b/src/ioc/db/test/dbServerTest.c @@ -104,6 +104,12 @@ dbServer disabled = { disInit, NULL, NULL, NULL }; +dbServer illegal = { + ELLNODE_INIT, "bad name", + NULL, NULL, NULL, + disInit, NULL, NULL, NULL +}; + MAIN(dbServerTest) { @@ -111,24 +117,24 @@ MAIN(dbServerTest) char *theName = "The One"; int status; - testPlan(9); + testPlan(14); /* Prove that we handle substring names properly */ epicsEnvSet("EPICS_IOC_IGNORE_SERVERS", "none ones"); testDiag("Registering dbServer 'one'"); - dbRegisterServer(&one); + testOk(dbRegisterServer(&one) == 0, "Registered 'one'"); testOk1(oneState == NOTHING_CALLED); - testDiag("Expect double-registration warning for 'one':"); - dbRegisterServer(&one); + testOk(dbRegisterServer(&one) != 0, "Duplicate registration rejected"); + testOk(dbRegisterServer(&illegal) != 0, "Illegal registration rejected"); testDiag("Registering dbServer 'no-routines'"); - dbRegisterServer(&no_routines); + testOk(dbRegisterServer(&no_routines) == 0, "Registered 'no-routines'"); epicsEnvSet("EPICS_IOC_IGNORE_SERVERS", "disabled nonexistent"); testDiag("Registering dbServer 'disabled'"); - dbRegisterServer(&disabled); + testOk(dbRegisterServer(&disabled) == 0, "Registration accepted"); dbInitServers(); testOk(oneState == INIT_CALLED, "dbInitServers"); @@ -143,6 +149,7 @@ MAIN(dbServerTest) dbStopServers(); testOk(oneState == STOP_CALLED, "dbStopServers"); + testDiag("Printing server report"); dbsr(0); testOk(oneState == REPORT_CALLED, "dbsr"); From d8d52e433cff58e7db6e8e38c017025f6578fe7d Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 15 Sep 2017 18:36:48 -0500 Subject: [PATCH 12/18] rsrv: epicsExport.h is last import --- src/ioc/rsrv/rsrvIocRegister.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ioc/rsrv/rsrvIocRegister.c b/src/ioc/rsrv/rsrvIocRegister.c index 74336817d..00c58e828 100644 --- a/src/ioc/rsrv/rsrvIocRegister.c +++ b/src/ioc/rsrv/rsrvIocRegister.c @@ -9,10 +9,11 @@ #include "iocsh.h" -#include "epicsExport.h" #define epicsExportSharedSymbols #include "rsrv.h" +#include "epicsExport.h" + /* casr */ static const iocshArg casrArg0 = { "level",iocshArgInt}; static const iocshArg * const casrArgs[1] = {&casrArg0}; From a93932427088c190103424795b1c0074561baf75 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Wed, 18 Oct 2017 17:13:46 -0500 Subject: [PATCH 13/18] Added call to dbStopServers() in iocShutdown() Renamed buildRSRV => buildServers. Don't call other dbXxxServers() routines or announce their related initHook states when in buildIsolated mode. --- src/ioc/misc/iocInit.c | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/src/ioc/misc/iocInit.c b/src/ioc/misc/iocInit.c index 738d9b472..c6d6e4a14 100644 --- a/src/ioc/misc/iocInit.c +++ b/src/ioc/misc/iocInit.c @@ -75,7 +75,7 @@ static enum { iocVirgin, iocBuilding, iocBuilt, iocRunning, iocPaused, iocStopped } iocState = iocVirgin; static enum { - buildRSRV, buildIsolated + buildServers, buildIsolated } iocBuildMode; /* define forward references*/ @@ -210,7 +210,7 @@ int iocBuild(void) if (dbThreadRealtimeLock) epicsThreadRealtimeLock(); - if (!status) iocBuildMode = buildRSRV; + if (!status) iocBuildMode = buildServers; return status; } @@ -246,9 +246,11 @@ int iocRun(void) if (iocState == iocBuilt) initHookAnnounce(initHookAfterInterruptAccept); - dbRunServers(); + if (iocBuildMode == buildServers) { + dbRunServers(); + initHookAnnounce(initHookAfterCaServerRunning); + } - initHookAnnounce(initHookAfterCaServerRunning); if (iocState == iocBuilt) initHookAnnounce(initHookAtEnd); @@ -268,8 +270,10 @@ int iocPause(void) } initHookAnnounce(initHookAtIocPause); - dbPauseServers(); - initHookAnnounce(initHookAfterCaServerPaused); + if (iocBuildMode == buildServers) { + dbPauseServers(); + initHookAnnounce(initHookAfterCaServerPaused); + } dbCaPause(); scanPause(); @@ -702,27 +706,37 @@ static void doFreeRecord(dbRecordType *pdbRecordType, dbCommon *precord, int iocShutdown(void) { - if (iocState == iocVirgin || iocState == iocStopped) return 0; + if (iocState == iocVirgin || iocState == iocStopped) + return 0; + iterateRecords(doCloseLinks, NULL); - if (iocBuildMode==buildIsolated) { + + if (iocBuildMode == buildIsolated) { /* stop and "join" threads */ scanStop(); callbackStop(); } + else + dbStopServers(); + dbCaShutdown(); /* must be before dbFreeRecord and dbChannelExit */ - if (iocBuildMode==buildIsolated) { + + if (iocBuildMode == buildIsolated) { /* free resources */ scanCleanup(); callbackCleanup(); + iterateRecords(doFreeRecord, NULL); dbLockCleanupRecords(pdbbase); + asShutdown(); dbChannelExit(); dbProcessNotifyExit(); iocshFree(); } + iocState = iocStopped; - iocBuildMode = buildRSRV; + iocBuildMode = buildServers; return 0; } From 0406ecebe95ddf29cd9e44ab7c34e3c80997e59f Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Wed, 18 Oct 2017 17:35:37 -0500 Subject: [PATCH 14/18] Ignore dbServer registrations after init Actually store all server states, and display it in dbsr() --- src/ioc/db/dbServer.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/ioc/db/dbServer.c b/src/ioc/db/dbServer.c index 47ed5d8e8..a18477723 100644 --- a/src/ioc/db/dbServer.c +++ b/src/ioc/db/dbServer.c @@ -20,13 +20,17 @@ #include "dbServer.h" static ELLLIST serverList = ELLLIST_INIT; - +static enum { registering, initialized, running, paused, stopped } + state = registering; +static char *stateNames[] = { + "registering", "initialized", "running", "paused", "stopped" +}; int dbRegisterServer(dbServer *psrv) { const char * ignore = envGetConfigParamPtr(&EPICS_IOC_IGNORE_SERVERS); - if (!psrv || !psrv->name) + if (!psrv || !psrv->name || state != registering) return -1; if (strchr(psrv->name, ' ')) { @@ -70,6 +74,8 @@ void dbsr(unsigned level) return; } + printf("Server state: %s\n", stateNames[state]); + while (psrv) { printf("Server '%s':\n", psrv->name); if (psrv->report) @@ -91,7 +97,7 @@ int dbServerClient(char *pBuf, size_t bufSize) return -1; } -#define STARTSTOP(routine, method) \ +#define STARTSTOP(routine, method, newState) \ void routine(void) \ { \ dbServer *psrv = (dbServer *)ellFirst(&serverList); \ @@ -101,9 +107,10 @@ void routine(void) \ psrv->method(); \ psrv = (dbServer *)ellNext(&psrv->node); \ } \ + state = newState; \ } -STARTSTOP(dbInitServers, init) -STARTSTOP(dbRunServers, run) -STARTSTOP(dbPauseServers, pause) -STARTSTOP(dbStopServers, stop) +STARTSTOP(dbInitServers, init, initialized) +STARTSTOP(dbRunServers, run, running) +STARTSTOP(dbPauseServers, pause, paused) +STARTSTOP(dbStopServers, stop, stopped) From fb5a2d84759d1cf3bc66e3476b2eae6bd1de5dce Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Wed, 18 Oct 2017 18:12:21 -0500 Subject: [PATCH 15/18] Added dbUnregisterServer() routine, plus tests --- src/ioc/db/dbServer.c | 15 +++++++++++++++ src/ioc/db/dbServer.h | 7 +++++++ src/ioc/db/test/dbServerTest.c | 12 +++++++++++- 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/src/ioc/db/dbServer.c b/src/ioc/db/dbServer.c index a18477723..495156dcf 100644 --- a/src/ioc/db/dbServer.c +++ b/src/ioc/db/dbServer.c @@ -65,6 +65,21 @@ int dbRegisterServer(dbServer *psrv) return 0; } +int dbUnregisterServer(dbServer *psrv) +{ + if (state != registering && state != stopped) { + fprintf(stderr, "dbUnregisterServer: Servers still active!\n"); + return -1; + } + if (ellFind(&serverList, &psrv->node) < 0) { + fprintf(stderr, "dbUnregisterServer: '%s' not registered.\n", + psrv->name); + return -1; + } + ellDelete(&serverList, &psrv->node); + return 0; +} + void dbsr(unsigned level) { dbServer *psrv = (dbServer *)ellFirst(&serverList); diff --git a/src/ioc/db/dbServer.h b/src/ioc/db/dbServer.h index 1d184329c..ce5244e11 100644 --- a/src/ioc/db/dbServer.h +++ b/src/ioc/db/dbServer.h @@ -119,6 +119,13 @@ typedef struct dbServer { */ epicsShareFunc int dbRegisterServer(dbServer *psrv); +/** @brief Unregister a server layer + * + * This should only be called when the servers are inactive. + * @param psrv Server information structure for the server + */ +epicsShareFunc int dbUnregisterServer(dbServer *psrv); + /** @brief Print dbServer Reports. * * Calls the report methods of all registered servers. diff --git a/src/ioc/db/test/dbServerTest.c b/src/ioc/db/test/dbServerTest.c index e9c6556c5..5aba92770 100644 --- a/src/ioc/db/test/dbServerTest.c +++ b/src/ioc/db/test/dbServerTest.c @@ -110,6 +110,12 @@ dbServer illegal = { disInit, NULL, NULL, NULL }; +dbServer toolate = { + ELLNODE_INIT, "toolate", + NULL, NULL, NULL, + disInit, NULL, NULL, NULL +}; + MAIN(dbServerTest) { @@ -117,7 +123,7 @@ MAIN(dbServerTest) char *theName = "The One"; int status; - testPlan(14); + testPlan(18); /* Prove that we handle substring names properly */ epicsEnvSet("EPICS_IOC_IGNORE_SERVERS", "none ones"); @@ -139,15 +145,19 @@ MAIN(dbServerTest) dbInitServers(); testOk(oneState == INIT_CALLED, "dbInitServers"); testOk(disInitialized == 0, "Disabled server not initialized"); + testOk(dbRegisterServer(&toolate) != 0, "No registration while active"); dbRunServers(); testOk(oneState == RUN_CALLED, "dbRunServers"); + testOk(dbUnregisterServer(&one) != 0, "No unregistration while active"); dbPauseServers(); testOk(oneState == PAUSE_CALLED, "dbPauseServers"); dbStopServers(); testOk(oneState == STOP_CALLED, "dbStopServers"); + testOk(dbUnregisterServer(&toolate) != 0, "No unregistration if not reg'ed"); + testOk(dbUnregisterServer(&no_routines) == 0, "Unregistered 'no-routines'"); testDiag("Printing server report"); dbsr(0); From 2b15bc5f70afc46ae04a60988166bee31fe879ba Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Wed, 18 Oct 2017 19:12:50 -0500 Subject: [PATCH 16/18] Can't dbUnregisterServer() if stopped & no stop() method Unregistration is allowed if we're still registering though. This allows for cases like rsrv, which doesn't have a stop method. However the semantics of restarting servers after they have been stopped hasn't been defined, and rsrv probably wouldn't work if you tried that anyway, so I'm not convinced this is useful. --- src/ioc/db/dbServer.c | 6 ++++++ src/ioc/db/test/dbServerTest.c | 8 +++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/ioc/db/dbServer.c b/src/ioc/db/dbServer.c index 495156dcf..09917da55 100644 --- a/src/ioc/db/dbServer.c +++ b/src/ioc/db/dbServer.c @@ -76,6 +76,12 @@ int dbUnregisterServer(dbServer *psrv) psrv->name); return -1; } + if (state == stopped && psrv->stop == NULL) { + fprintf(stderr, "dbUnregisterServer: '%s' has no stop() method.\n", + psrv->name); + return -1; + } + ellDelete(&serverList, &psrv->node); return 0; } diff --git a/src/ioc/db/test/dbServerTest.c b/src/ioc/db/test/dbServerTest.c index 5aba92770..325b77644 100644 --- a/src/ioc/db/test/dbServerTest.c +++ b/src/ioc/db/test/dbServerTest.c @@ -123,7 +123,7 @@ MAIN(dbServerTest) char *theName = "The One"; int status; - testPlan(18); + testPlan(20); /* Prove that we handle substring names properly */ epicsEnvSet("EPICS_IOC_IGNORE_SERVERS", "none ones"); @@ -137,6 +137,8 @@ MAIN(dbServerTest) testDiag("Registering dbServer 'no-routines'"); testOk(dbRegisterServer(&no_routines) == 0, "Registered 'no-routines'"); + testOk(dbUnregisterServer(&no_routines) == 0, "'no-routines' unreg'd"); + testOk(dbRegisterServer(&no_routines) == 0, "Re-registered 'no-routines'"); epicsEnvSet("EPICS_IOC_IGNORE_SERVERS", "disabled nonexistent"); testDiag("Registering dbServer 'disabled'"); @@ -156,8 +158,8 @@ MAIN(dbServerTest) dbStopServers(); testOk(oneState == STOP_CALLED, "dbStopServers"); - testOk(dbUnregisterServer(&toolate) != 0, "No unregistration if not reg'ed"); - testOk(dbUnregisterServer(&no_routines) == 0, "Unregistered 'no-routines'"); + testOk(dbUnregisterServer(&toolate) != 0, "No unreg' if not reg'ed"); + testOk(dbUnregisterServer(&no_routines) != 0, "No unreg' of 'no-routines'"); testDiag("Printing server report"); dbsr(0); From 11d2a64507537f8fd498393f1b0720a79c80df1e Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Wed, 18 Oct 2017 20:12:20 -0500 Subject: [PATCH 17/18] Clarify release entry wording --- documentation/RELEASE_NOTES.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html index 2735e40da..8c9810306 100644 --- a/documentation/RELEASE_NOTES.html +++ b/documentation/RELEASE_NOTES.html @@ -19,7 +19,7 @@

Extend the dbServer API with init/run/pause/stop methods

-

This change permits the IOC to be built that omits the CA server (RSRV) by +

This change permits IOCs to be built that omit the CA server (RSRV) by removing its registrar entry which is now provided in the new rsrv.dbd file. Other server layers can be built into the IOC (alongside RSRV or in place of it) by registering them in a similar manner. The dbServer API is documented From 77de87cfb0e930c8e217a01964b04c9d5c4f0cc7 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Thu, 26 Oct 2017 13:02:20 -0500 Subject: [PATCH 18/18] dbServer: Only call client() and report() methods when running Also adjusts the tests to confirm that behaviour. --- src/ioc/db/dbServer.c | 7 +++++-- src/ioc/db/test/dbServerTest.c | 31 ++++++++++++++++++++----------- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/src/ioc/db/dbServer.c b/src/ioc/db/dbServer.c index 09917da55..5374f02ad 100644 --- a/src/ioc/db/dbServer.c +++ b/src/ioc/db/dbServer.c @@ -98,8 +98,8 @@ void dbsr(unsigned level) printf("Server state: %s\n", stateNames[state]); while (psrv) { - printf("Server '%s':\n", psrv->name); - if (psrv->report) + printf("Server '%s'\n", psrv->name); + if (state == running && psrv->report) psrv->report(level); psrv = (dbServer *)ellNext(&psrv->node); } @@ -109,6 +109,9 @@ int dbServerClient(char *pBuf, size_t bufSize) { dbServer *psrv = (dbServer *)ellFirst(&serverList); + if (state != running) + return -1; + while (psrv) { if (psrv->client && psrv->client(pBuf, bufSize) == 0) diff --git a/src/ioc/db/test/dbServerTest.c b/src/ioc/db/test/dbServerTest.c index 325b77644..f0d5bf6ac 100644 --- a/src/ioc/db/test/dbServerTest.c +++ b/src/ioc/db/test/dbServerTest.c @@ -123,7 +123,7 @@ MAIN(dbServerTest) char *theName = "The One"; int status; - testPlan(20); + testPlan(24); /* Prove that we handle substring names properly */ epicsEnvSet("EPICS_IOC_IGNORE_SERVERS", "none ones"); @@ -144,6 +144,7 @@ MAIN(dbServerTest) testDiag("Registering dbServer 'disabled'"); testOk(dbRegisterServer(&disabled) == 0, "Registration accepted"); + testDiag("Changing server state"); dbInitServers(); testOk(oneState == INIT_CALLED, "dbInitServers"); testOk(disInitialized == 0, "Disabled server not initialized"); @@ -153,29 +154,37 @@ MAIN(dbServerTest) testOk(oneState == RUN_CALLED, "dbRunServers"); testOk(dbUnregisterServer(&one) != 0, "No unregistration while active"); - dbPauseServers(); - testOk(oneState == PAUSE_CALLED, "dbPauseServers"); - - dbStopServers(); - testOk(oneState == STOP_CALLED, "dbStopServers"); - testOk(dbUnregisterServer(&toolate) != 0, "No unreg' if not reg'ed"); - testOk(dbUnregisterServer(&no_routines) != 0, "No unreg' of 'no-routines'"); - - testDiag("Printing server report"); + testDiag("Checking server methods called"); dbsr(0); - testOk(oneState == REPORT_CALLED, "dbsr"); + testOk(oneState == REPORT_CALLED, "dbsr called report()"); oneSim = NULL; name[0] = 0; status = dbServerClient(name, sizeof(name)); + testOk(oneState == CLIENT_CALLED_UNKNOWN, "Client unknown"); testOk(status == -1 && name[0] == 0, "dbServerClient mismatch"); oneSim = theName; name[0] = 0; status = dbServerClient(name, sizeof(name)); + testOk(oneState == CLIENT_CALLED_KNOWN, "Client known"); testOk(status == 0 && strcmp(name, theName) == 0, "dbServerClient match"); + dbPauseServers(); + testOk(oneState == PAUSE_CALLED, "dbPauseServers"); + + dbsr(0); + testOk(oneState != REPORT_CALLED, "No call to report() when paused"); + + status = dbServerClient(name, sizeof(name)); + testOk(oneState != CLIENT_CALLED_KNOWN, "No call to client() when paused"); + + dbStopServers(); + testOk(oneState == STOP_CALLED, "dbStopServers"); + testOk(dbUnregisterServer(&toolate) != 0, "No unreg' if not reg'ed"); + testOk(dbUnregisterServer(&no_routines) != 0, "No unreg' of 'no-routines'"); + return testDone(); }