From b42a1458245a219555c6358f78fdda69a6c1c842 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Mon, 3 May 2021 18:12:39 -0500 Subject: [PATCH 001/169] Regression tests for RSET::special() return status Adds tests to ensure that a record's special() routine can return an error status that gets fed back to the client from both before and after special() calls. This was broken from 3.15 through 7.0.5 inclusive. --- modules/database/test/ioc/db/dbPutGetTest.c | 22 +++++++++++++++++++- modules/database/test/ioc/db/dbPutGetTest.db | 2 ++ modules/database/test/ioc/db/xRecord.c | 14 ++++++++++++- modules/database/test/ioc/db/xRecord.dbd | 12 +++++++++++ 4 files changed, 48 insertions(+), 2 deletions(-) diff --git a/modules/database/test/ioc/db/dbPutGetTest.c b/modules/database/test/ioc/db/dbPutGetTest.c index e1492a5a9..a515d590b 100644 --- a/modules/database/test/ioc/db/dbPutGetTest.c +++ b/modules/database/test/ioc/db/dbPutGetTest.c @@ -313,11 +313,29 @@ void testPutArr(void) testdbGetArrFieldEqual("arr", DBR_LONG, 4, 3, buf); } +static +void testPutSpecial(void) +{ + const char val[] = "special"; + const char sfx[] = "special.SFX"; + testDiag("testPutSpecial()"); + + testdbPutFieldOk(val, DBR_LONG, 1); + testdbPutFieldOk(sfx, DBR_LONG, SFX_Before); /* Reject early */ + testdbPutFieldFail(S_db_Blocked, val, DBR_LONG, 2); + testdbGetFieldEqual(val, DBR_LONG, 1); /* Wasn't modified */ + testdbPutFieldOk(sfx, DBR_LONG, SFX_After); /* Reject late */ + testdbPutFieldFail(S_db_Blocked, val, DBR_LONG, 3); + testdbPutFieldOk(sfx, DBR_LONG, SFX_None); + testdbPutFieldOk(val, DBR_LONG, 4); +} + + void dbTestIoc_registerRecordDeviceDriver(struct dbBase *); MAIN(dbPutGet) { - testPlan(111); + testPlan(119); testdbPrepare(); testdbMetaDoubleSizes(); @@ -343,6 +361,8 @@ MAIN(dbPutGet) testPutArr(); + testPutSpecial(); + testIocShutdownOk(); testdbCleanup(); diff --git a/modules/database/test/ioc/db/dbPutGetTest.db b/modules/database/test/ioc/db/dbPutGetTest.db index 68485e4ba..ccfc43998 100644 --- a/modules/database/test/ioc/db/dbPutGetTest.db +++ b/modules/database/test/ioc/db/dbPutGetTest.db @@ -46,3 +46,5 @@ record(arr, "arr") { } record(x, "recmeta") {} + +record(x, "special") {} diff --git a/modules/database/test/ioc/db/xRecord.c b/modules/database/test/ioc/db/xRecord.c index 83ca9b15c..001b5ea16 100644 --- a/modules/database/test/ioc/db/xRecord.c +++ b/modules/database/test/ioc/db/xRecord.c @@ -76,6 +76,19 @@ static long process(struct dbCommon *pcommon) return ret; } +static long special(struct dbAddr *paddr, int after) +{ + struct xRecord *prec = (struct xRecord *) paddr->precord; + if (dbGetFieldIndex(paddr) != xRecordVAL) { + recGblRecordError(S_db_badField, prec, "x: special"); + return S_db_badField; + } + if (prec->sfx == after) { + return S_db_Blocked; + } + return 0; +} + long get_units(struct dbAddr *paddr, char *units) { if(dbGetFieldIndex(paddr)==xRecordOTST) { @@ -125,7 +138,6 @@ long get_alarm_double(struct dbAddr *paddr, struct dbr_alDouble *p) #define report NULL #define initialize NULL -#define special NULL #define get_value NULL #define cvt_dbaddr NULL #define get_array_info NULL diff --git a/modules/database/test/ioc/db/xRecord.dbd b/modules/database/test/ioc/db/xRecord.dbd index 13fcb3f3e..7cbbdabe5 100644 --- a/modules/database/test/ioc/db/xRecord.dbd +++ b/modules/database/test/ioc/db/xRecord.dbd @@ -1,9 +1,16 @@ # This is a minimal record definition +menu(xSFX) { + choice(SFX_Before, "Before") + choice(SFX_After, "After") + choice(SFX_None, "None") +} + recordtype(x) { include "dbCommon.dbd" field(VAL, DBF_LONG) { prompt("Value") + special(SPC_MOD) } field(C8, DBF_CHAR) { prompt("Char") @@ -50,4 +57,9 @@ recordtype(x) { prompt("dbGet() options test") special(SPC_NOMOD) } + field(SFX, DBF_MENU) { + prompt("Special effects") + menu(xSFX) + initial("None") + } } From 6c7214ee064bbfd21ec76dea607fc0ab545d09ac Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Thu, 6 May 2021 18:20:59 -0500 Subject: [PATCH 002/169] Add tests for special link fields too --- modules/database/test/ioc/db/dbPutGetTest.c | 12 +++++++++++- modules/database/test/ioc/db/xRecord.c | 3 ++- modules/database/test/ioc/db/xRecord.dbd | 1 + 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/modules/database/test/ioc/db/dbPutGetTest.c b/modules/database/test/ioc/db/dbPutGetTest.c index a515d590b..8f539407d 100644 --- a/modules/database/test/ioc/db/dbPutGetTest.c +++ b/modules/database/test/ioc/db/dbPutGetTest.c @@ -317,17 +317,27 @@ static void testPutSpecial(void) { const char val[] = "special"; + const char inp[] = "special.INP"; const char sfx[] = "special.SFX"; testDiag("testPutSpecial()"); + /* There are separate sets of calls to dbPutSpecial() in + * dbPut() and in dbPutFieldLink() so we need to check + * both regular fields and link fields. + */ testdbPutFieldOk(val, DBR_LONG, 1); + testdbPutFieldOk(inp, DBR_STRING, "1.0"); testdbPutFieldOk(sfx, DBR_LONG, SFX_Before); /* Reject early */ testdbPutFieldFail(S_db_Blocked, val, DBR_LONG, 2); testdbGetFieldEqual(val, DBR_LONG, 1); /* Wasn't modified */ + testdbPutFieldFail(S_db_Blocked, inp, DBR_STRING, "2.0"); + testdbGetFieldEqual(inp, DBR_STRING, "1.0"); /* Wasn't modified */ testdbPutFieldOk(sfx, DBR_LONG, SFX_After); /* Reject late */ testdbPutFieldFail(S_db_Blocked, val, DBR_LONG, 3); + testdbPutFieldFail(S_db_Blocked, inp, DBR_STRING, "3.0"); testdbPutFieldOk(sfx, DBR_LONG, SFX_None); testdbPutFieldOk(val, DBR_LONG, 4); + testdbPutFieldOk(inp, DBR_STRING, "4.0"); } @@ -335,7 +345,7 @@ void dbTestIoc_registerRecordDeviceDriver(struct dbBase *); MAIN(dbPutGet) { - testPlan(119); + testPlan(124); testdbPrepare(); testdbMetaDoubleSizes(); diff --git a/modules/database/test/ioc/db/xRecord.c b/modules/database/test/ioc/db/xRecord.c index 001b5ea16..cd8fa74df 100644 --- a/modules/database/test/ioc/db/xRecord.c +++ b/modules/database/test/ioc/db/xRecord.c @@ -79,7 +79,8 @@ static long process(struct dbCommon *pcommon) static long special(struct dbAddr *paddr, int after) { struct xRecord *prec = (struct xRecord *) paddr->precord; - if (dbGetFieldIndex(paddr) != xRecordVAL) { + if (dbGetFieldIndex(paddr) != xRecordVAL && + dbGetFieldIndex(paddr) != xRecordINP) { recGblRecordError(S_db_badField, prec, "x: special"); return S_db_badField; } diff --git a/modules/database/test/ioc/db/xRecord.dbd b/modules/database/test/ioc/db/xRecord.dbd index 7cbbdabe5..620deac61 100644 --- a/modules/database/test/ioc/db/xRecord.dbd +++ b/modules/database/test/ioc/db/xRecord.dbd @@ -47,6 +47,7 @@ recordtype(x) { } field(INP, DBF_INLINK) { prompt("Input Link") + special(SPC_MOD) } field(CLBK, DBF_NOACCESS) { prompt("Processing callback") From 3c329c1b48fd25f337745764dd853c3d343f12b5 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Thu, 10 Jun 2021 10:15:08 -0500 Subject: [PATCH 003/169] Disable some RTEMS targets, tests on RTEMS-pc686-qemu --- .github/workflows/ci-scripts-build.yml | 34 ++++++++++++++------------ 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/.github/workflows/ci-scripts-build.yml b/.github/workflows/ci-scripts-build.yml index d94fbf312..a159de457 100644 --- a/.github/workflows/ci-scripts-build.yml +++ b/.github/workflows/ci-scripts-build.yml @@ -85,6 +85,7 @@ jobs: configuration: default rtems: "5" rtems_target: RTEMS-pc686-qemu + test: NO name: "Ub-20 gcc-9 + RT-5.1 pc686" - os: ubuntu-20.04 @@ -95,21 +96,24 @@ jobs: test: NO name: "Ub-20 gcc-9 + RT-5.1 beatnik" - - os: ubuntu-20.04 - cmp: gcc - configuration: default - rtems: "5" - rtems_target: RTEMS-mvme3100 - test: NO - name: "Ub-20 gcc-9 + RT-5.1 mvme3100" - - - os: ubuntu-20.04 - cmp: gcc - configuration: default - rtems: "5" - rtems_target: RTEMS-qoriq_e500 - test: NO - name: "Ub-20 gcc-9 + RT-5.1 qoriq_e500" + # Only build one RTEMS target per CPU family + # unless it's running the tests + # + # - os: ubuntu-20.04 + # cmp: gcc + # configuration: default + # rtems: "5" + # rtems_target: RTEMS-mvme3100 + # test: NO + # name: "Ub-20 gcc-9 + RT-5.1 mvme3100" + # + # - os: ubuntu-20.04 + # cmp: gcc + # configuration: default + # rtems: "5" + # rtems_target: RTEMS-qoriq_e500 + # test: NO + # name: "Ub-20 gcc-9 + RT-5.1 qoriq_e500" - os: ubuntu-20.04 cmp: gcc From f825136f652de0cc99b818891a5cefd3844c53c4 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Mon, 14 Jun 2021 18:22:23 -0500 Subject: [PATCH 004/169] makeBaseApp.pl: Allow relative paths to $0 --- src/template/base/makeBaseApp.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/template/base/makeBaseApp.pl b/src/template/base/makeBaseApp.pl index e2df38dd6..f5b36d250 100644 --- a/src/template/base/makeBaseApp.pl +++ b/src/template/base/makeBaseApp.pl @@ -168,7 +168,7 @@ sub get_commandline_opts { #no args Cleanup(0) if $opt_h; # Locate epics_base - my ($command) = UnixPath($0); + my ($command) = UnixPath(AbsPath($0)); if ($opt_b) { # first choice is -b base $epics_base = UnixPath($opt_b); } elsif ($release{"EPICS_BASE"}) { # second choice is configure/RELEASE From b471e8388fa0b7b79471e4a883277ca8f8d5601b Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Mon, 14 Jun 2021 18:23:47 -0500 Subject: [PATCH 005/169] GHA: Don't worry if no artifacts to upload --- .github/workflows/ci-scripts-build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci-scripts-build.yml b/.github/workflows/ci-scripts-build.yml index a159de457..b9b3a0c12 100644 --- a/.github/workflows/ci-scripts-build.yml +++ b/.github/workflows/ci-scripts-build.yml @@ -230,6 +230,7 @@ jobs: with: name: tapfiles ${{ matrix.name }} path: '**/O.*/*.tap' + if-no-files-found: ignore - name: Collect and show test results if: ${{ always() }} run: python .ci/cue.py -T 5M test-results From 256babf9614a199709e2728f0ecd79bf57b668ec Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 15 Jun 2021 08:01:14 -0700 Subject: [PATCH 006/169] quiet use "may be used uninitialized" warnings --- modules/database/src/ioc/db/dbUnitTest.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/database/src/ioc/db/dbUnitTest.c b/modules/database/src/ioc/db/dbUnitTest.c index a4830a37c..805031bc2 100644 --- a/modules/database/src/ioc/db/dbUnitTest.c +++ b/modules/database/src/ioc/db/dbUnitTest.c @@ -266,7 +266,7 @@ done: void testdbPutArrFieldOk(const char* pv, short dbrType, unsigned long count, const void *pbuf) { dbChannel *chan = dbChannelCreate(pv); - long status; + long status = -1; if(!chan || (status=dbChannelOpen(chan))) { testFail("Channel error (%p, %ld) : %s", chan, status, pv); @@ -289,7 +289,7 @@ void testdbGetArrFieldEqual(const char* pv, short dbfType, long nRequest, unsign const long vSize = dbValueSize(dbfType); const long nStore = vSize * nRequest; long status = S_dbLib_recNotFound; - char *gbuf, *gstore; + char *gbuf, *gstore = NULL; const char *pbuf = pbufraw; if(!chan || (status=dbChannelOpen(chan))) { From b35064d26ccc2d94e95331225f8638a5f02777d0 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Sun, 23 Jun 2019 10:36:52 -0700 Subject: [PATCH 007/169] Revert "dbEvent simplify db_close_events() with join" This reverts commit 37a76b433a9e7d5a8d26a13fd21ad62f20a0c1c1. # Conflicts: # modules/database/src/ioc/db/dbEvent.c --- modules/database/src/ioc/db/dbEvent.c | 74 ++++++++++++++++++--------- 1 file changed, 51 insertions(+), 23 deletions(-) diff --git a/modules/database/src/ioc/db/dbEvent.c b/modules/database/src/ioc/db/dbEvent.c index 7342a0852..65e3488ad 100644 --- a/modules/database/src/ioc/db/dbEvent.c +++ b/modules/database/src/ioc/db/dbEvent.c @@ -84,6 +84,7 @@ struct event_user { epicsMutexId lock; epicsEventId ppendsem; /* Wait while empty */ epicsEventId pflush_sem; /* wait for flush */ + epicsEventId pexitsem; /* wait for event task to join */ EXTRALABORFUNC *extralabor_sub;/* off load to event task */ void *extralabor_arg;/* parameter to above */ @@ -122,6 +123,8 @@ static char *EVENT_PEND_NAME = "eventTask"; static struct evSubscrip canceledEvent; +static epicsMutexId stopSync; + static unsigned short ringSpace ( const struct event_que *pevq ) { if ( pevq->evque[pevq->putix] == EVENTQEMPTY ) { @@ -260,6 +263,10 @@ int dbel ( const char *pname, unsigned level ) */ void db_init_event_freelists (void) { + if (!stopSync) { + stopSync = epicsMutexMustCreate(); + } + if (!dbevEventUserFreeList) { freeListInitPvt(&dbevEventUserFreeList, sizeof(struct event_user),8); @@ -299,6 +306,9 @@ dbEventCtx db_init_events (void) return NULL; } + /* Flag will be cleared when event task starts */ + evUser->pendexit = TRUE; + evUser->firstque.evUser = evUser; evUser->firstque.writelock = epicsMutexCreate(); if (!evUser->firstque.writelock) @@ -313,6 +323,9 @@ dbEventCtx db_init_events (void) evUser->lock = epicsMutexCreate(); if (!evUser->lock) goto fail; + evUser->pexitsem = epicsEventCreate(epicsEventEmpty); + if (!evUser->pexitsem) + goto fail; evUser->flowCtrlMode = FALSE; evUser->extraLaborBusy = FALSE; @@ -327,6 +340,8 @@ fail: epicsEventDestroy (evUser->ppendsem); if(evUser->pflush_sem) epicsEventDestroy (evUser->pflush_sem); + if(evUser->pexitsem) + epicsEventDestroy (evUser->pexitsem); freeListFree(dbevEventUserFreeList,evUser); return NULL; } @@ -347,6 +362,7 @@ DBCORE_API void db_cleanup_events(void) dbevFieldLogFreeList = NULL; } + /* intentionally leak stopSync to avoid possible shutdown races */ /* * DB_CLOSE_EVENTS() * @@ -368,15 +384,30 @@ void db_close_events (dbEventCtx ctx) * hazardous to the system's health. */ epicsMutexMustLock ( evUser->lock ); - evUser->pendexit = TRUE; + if(!evUser->pendexit) { /* event task running */ + evUser->pendexit = TRUE; + epicsMutexUnlock ( evUser->lock ); + + /* notify the waiting task */ + epicsEventSignal(evUser->ppendsem); + /* wait for task to exit */ + epicsEventMustWait(evUser->pexitsem); + + epicsMutexMustLock ( evUser->lock ); + } + epicsMutexUnlock ( evUser->lock ); - /* notify the waiting task */ - epicsEventSignal(evUser->ppendsem); + epicsMutexMustLock (stopSync); - if(evUser->taskid) - epicsThreadMustJoin(evUser->taskid); - /* evUser has been deleted by the worker */ + epicsEventDestroy(evUser->pexitsem); + epicsEventDestroy(evUser->ppendsem); + epicsEventDestroy(evUser->pflush_sem); + epicsMutexDestroy(evUser->lock); + + epicsMutexUnlock (stopSync); + + freeListFree(dbevEventUserFreeList, evUser); } /* @@ -1063,18 +1094,17 @@ static void event_task (void *pParm) } } - epicsEventDestroy(evUser->ppendsem); - epicsEventDestroy(evUser->pflush_sem); - epicsMutexDestroy(evUser->lock); - - if (dbevEventUserFreeList) - freeListFree(dbevEventUserFreeList, evUser); - else - fprintf(stderr, "%s exiting but dbevEventUserFreeList already NULL\n", - __FUNCTION__); - taskwdRemove(epicsThreadGetIdSelf()); + /* use stopSync to ensure pexitsem is not destroy'd + * until epicsEventSignal() has returned. + */ + epicsMutexMustLock (stopSync); + + epicsEventSignal(evUser->pexitsem); + + epicsMutexUnlock(stopSync); + return; } @@ -1086,11 +1116,6 @@ int db_start_events ( void *init_func_arg, unsigned osiPriority ) { struct event_user * const evUser = (struct event_user *) ctx; - epicsThreadOpts opts = EPICS_THREAD_OPTS_INIT; - - opts.stackSize = epicsThreadGetStackSize(epicsThreadStackMedium); - opts.priority = osiPriority; - opts.joinable = 1; epicsMutexMustLock ( evUser->lock ); @@ -1108,12 +1133,15 @@ int db_start_events ( if (!taskname) { taskname = EVENT_PEND_NAME; } - evUser->taskid = epicsThreadCreateOpt ( - taskname, event_task, (void *)evUser, &opts); + evUser->taskid = epicsThreadCreate ( + taskname, osiPriority, + epicsThreadGetStackSize(epicsThreadStackMedium), + event_task, (void *)evUser); if (!evUser->taskid) { epicsMutexUnlock ( evUser->lock ); return DB_EVENT_ERROR; } + evUser->pendexit = FALSE; epicsMutexUnlock ( evUser->lock ); return DB_EVENT_OK; } From ca2ea14082bdadb3ea8f7b7ad967fd42b41e6a41 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 2 Apr 2021 19:00:46 -0700 Subject: [PATCH 008/169] dbEvent: join worker --- modules/database/src/ioc/db/dbEvent.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/modules/database/src/ioc/db/dbEvent.c b/modules/database/src/ioc/db/dbEvent.c index 65e3488ad..b3c641798 100644 --- a/modules/database/src/ioc/db/dbEvent.c +++ b/modules/database/src/ioc/db/dbEvent.c @@ -392,6 +392,7 @@ void db_close_events (dbEventCtx ctx) epicsEventSignal(evUser->ppendsem); /* wait for task to exit */ epicsEventMustWait(evUser->pexitsem); + epicsThreadMustJoin(evUser->taskid); epicsMutexMustLock ( evUser->lock ); } @@ -1116,6 +1117,11 @@ int db_start_events ( void *init_func_arg, unsigned osiPriority ) { struct event_user * const evUser = (struct event_user *) ctx; + epicsThreadOpts opts = EPICS_THREAD_OPTS_INIT; + + opts.stackSize = epicsThreadGetStackSize(epicsThreadStackMedium); + opts.priority = osiPriority; + opts.joinable = 1; epicsMutexMustLock ( evUser->lock ); @@ -1133,10 +1139,8 @@ int db_start_events ( if (!taskname) { taskname = EVENT_PEND_NAME; } - evUser->taskid = epicsThreadCreate ( - taskname, osiPriority, - epicsThreadGetStackSize(epicsThreadStackMedium), - event_task, (void *)evUser); + evUser->taskid = epicsThreadCreateOpt ( + taskname, event_task, (void *)evUser, &opts); if (!evUser->taskid) { epicsMutexUnlock ( evUser->lock ); return DB_EVENT_ERROR; From 983f77e1196f81c527cf9b7114e1260157981b1e Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Thu, 17 Jun 2021 13:34:00 -0500 Subject: [PATCH 009/169] Let build find convertRelease.pl before it's installed There are several different cases that FIND_TOOL has to handle, and all 3 file paths given are needed in different circumstances: 1. First build of Base after checkout/untar 2. During builds after the script has been installed 3. In a submodule during 'make distclean' or 'make cvsclean' Fixes lp: #1932033 --- configure/CONFIG_BASE | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/configure/CONFIG_BASE b/configure/CONFIG_BASE index 03d0ff7be..4b15e4cdc 100644 --- a/configure/CONFIG_BASE +++ b/configure/CONFIG_BASE @@ -31,10 +31,12 @@ endif # BASE_TOP # Where to find the installed build tools # Windows does not like commands with relative paths starting ../ # so TOOLS must be an absolute path, although Perl scripts are OK. -# FIND_TOOL is for scripts run before the build reaches src/tools. +# FIND_TOOL is for scripts run before the build reaches src/tools +# and must also work in submodules when EPICS_BASE is not built. TOOLS = $(abspath $(EPICS_BASE_HOST_BIN)) -FIND_TOOL = $(firstword $(wildcard $(TOOLS)/$(1) $(EPICS_BASE)/src/tools/$(1))) +FIND_TOOL = $(firstword $(wildcard $(TOOLS)/$(1) \ + $(TOP)/src/tools/$(1)) $(EPICS_BASE)/src/tools/$(1)) #--------------------------------------------------------------- # EPICS Base build tools and tool flags From b5265ed853457e546e320758ee7e4ab6dff93b27 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Sun, 20 Jun 2021 12:22:15 -0500 Subject: [PATCH 010/169] Fix vxWorks version of epicsAtomicCmpAndSwapIntT() vxCas() returns TRUE or FALSE, not the original target value. Fixes lp: #1932118 --- .../src/osi/os/vxWorks/epicsAtomicOSD.h | 72 +++++++------------ 1 file changed, 27 insertions(+), 45 deletions(-) diff --git a/modules/libcom/src/osi/os/vxWorks/epicsAtomicOSD.h b/modules/libcom/src/osi/os/vxWorks/epicsAtomicOSD.h index 2ea1924ae..bdf48870a 100644 --- a/modules/libcom/src/osi/os/vxWorks/epicsAtomicOSD.h +++ b/modules/libcom/src/osi/os/vxWorks/epicsAtomicOSD.h @@ -24,6 +24,33 @@ #include "vxWorks.h" /* obtain the version of vxWorks */ #include "epicsAssert.h" +#include "vxLib.h" +#include "intLib.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifndef EPICS_ATOMIC_LOCK +#define EPICS_ATOMIC_LOCK + +typedef struct EpicsAtomicLockKey { int m_key; } EpicsAtomicLockKey; + +EPICS_ATOMIC_INLINE void epicsAtomicLock ( EpicsAtomicLockKey * pKey ) +{ + pKey->m_key = intLock (); +} + +EPICS_ATOMIC_INLINE void epicsAtomicUnlock ( EpicsAtomicLockKey * pKey ) +{ + intUnlock ( pKey->m_key ); +} +#endif + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif /* __cplusplus */ + /* * With vxWorks 6.6 and later we need to use vxAtomicLib * to implement this functionality correctly on SMP systems @@ -123,26 +150,6 @@ EPICS_ATOMIC_INLINE size_t epicsAtomicSubSizeT ( size_t * pTarget, size_t delta } #endif -#ifndef EPICS_ATOMIC_CAS_SIZET -#define EPICS_ATOMIC_CAS_SIZET -EPICS_ATOMIC_INLINE size_t epicsAtomicCmpAndSwapSizeT ( size_t * pTarget, - size_t oldVal, size_t newVal ) -{ - atomic_t * const pTarg = ( atomic_t * ) ( pTarget ); - return ( size_t ) vxCas ( pTarg, (atomic_t) oldVal, (atomic_t) newVal ); -} -#endif - -#ifndef EPICS_ATOMIC_CAS_PTRT -#define EPICS_ATOMIC_CAS_PTRT -EPICS_ATOMIC_INLINE EpicsAtomicPtrT epicsAtomicCmpAndSwapPtrT ( EpicsAtomicPtrT * pTarget, - EpicsAtomicPtrT oldVal, EpicsAtomicPtrT newVal ) -{ - atomic_t * const pTarg = ( atomic_t * ) ( pTarget ); - return (EpicsAtomicPtrT) vxCas ( pTarg, (atomic_t) oldVal, (atomic_t) newVal ); -} -#endif - #else /* ULONG_MAX == UINT_MAX */ /* @@ -190,15 +197,6 @@ EPICS_ATOMIC_INLINE int epicsAtomicAddIntT ( int * pTarget, int delta ) } #endif -#ifndef EPICS_ATOMIC_CAS_INTT -#define EPICS_ATOMIC_CAS_INTT -EPICS_ATOMIC_INLINE int epicsAtomicCmpAndSwapIntT ( int * pTarget, - int oldVal, int newVal ) -{ - atomic_t * const pTarg = ( atomic_t * ) ( pTarget ); - return ( int ) vxCas ( pTarg, (atomic_t) oldVal, (atomic_t) newVal ); -} -#endif #ifdef __cplusplus } /* end of extern "C" */ @@ -215,22 +213,6 @@ EPICS_ATOMIC_INLINE int epicsAtomicCmpAndSwapIntT ( int * pTarget, extern "C" { #endif /* __cplusplus */ -#ifndef EPICS_ATOMIC_LOCK -#define EPICS_ATOMIC_LOCK - -typedef struct EpicsAtomicLockKey { int m_key; } EpicsAtomicLockKey; - -EPICS_ATOMIC_INLINE void epicsAtomicLock ( EpicsAtomicLockKey * pKey ) -{ - pKey->m_key = intLock (); -} - -EPICS_ATOMIC_INLINE void epicsAtomicUnlock ( EpicsAtomicLockKey * pKey ) -{ - intUnlock ( pKey->m_key ); -} -#endif - #ifndef EPICS_ATOMIC_READ_MEMORY_BARRIER #define EPICS_ATOMIC_READ_MEMORY_BARRIER /* From ac6eb5e212270efbd8dcc95bb73ea9ac46ae7c32 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Sun, 20 Jun 2021 12:29:41 -0500 Subject: [PATCH 011/169] Protect callbackRequest() from failed callbackInit() Fixes lp: #1932120 --- modules/database/src/ioc/db/callback.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/modules/database/src/ioc/db/callback.c b/modules/database/src/ioc/db/callback.c index 3fa249392..4a26e27ac 100644 --- a/modules/database/src/ioc/db/callback.c +++ b/modules/database/src/ioc/db/callback.c @@ -263,7 +263,9 @@ void callbackCleanup(void) assert(epicsAtomicGetIntT(&mySet->threadsRunning)==0); epicsEventDestroy(mySet->semWakeUp); + mySet->semWakeUp = NULL; epicsRingPointerDelete(mySet->queue); + mySet->queue = NULL; } epicsTimerQueueRelease(timerQueue); @@ -333,6 +335,10 @@ int callbackRequest(epicsCallback *pcallback) return S_db_badChoice; } mySet = &callbackQueue[priority]; + if (!mySet->queue) { + epicsInterruptContextMessage("callbackRequest: Callbacks not initialized\n"); + return S_db_notInit; + } if (mySet->queueOverflow) return S_db_bufFull; pushOK = epicsRingPointerPush(mySet->queue, pcallback); From cb5f68994fbe43ebaf3c06361ae7e793aef8892c Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sun, 20 Jun 2021 12:47:38 -0500 Subject: [PATCH 012/169] Squish various compiler warnings * CPP's defined() is UB outside of a #if line * Use (void)! cast to prevent recent GCCs & glibc from warning about ignoring the return status from chdir() --- modules/ca/src/client/caRepeater.cpp | 2 +- modules/ca/src/perl/Cap5.xs | 8 ++++---- modules/libcom/src/osi/os/posix/osdThread.c | 7 ++++++- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/modules/ca/src/client/caRepeater.cpp b/modules/ca/src/client/caRepeater.cpp index 7eaab508b..c8385ba3b 100644 --- a/modules/ca/src/client/caRepeater.cpp +++ b/modules/ca/src/client/caRepeater.cpp @@ -90,7 +90,7 @@ int main(int argc, char* argv[]) (void)detachinout; #endif - chdir ( "/" ); + (void)! chdir ( "/" ); ca_repeater (); return ( 0 ); } diff --git a/modules/ca/src/perl/Cap5.xs b/modules/ca/src/perl/Cap5.xs index cc729340a..6d06a15f1 100644 --- a/modules/ca/src/perl/Cap5.xs +++ b/modules/ca/src/perl/Cap5.xs @@ -606,12 +606,12 @@ void CA_put(SV *ca_ref, SV *val, ...) { } } else { union { + void *dbr; dbr_char_t *dbr_char; dbr_long_t *dbr_long; dbr_double_t *dbr_double; char *dbr_string; - void *dbr; - } p; + } p = {0}; int i; chtype type = best_type(pch); @@ -699,12 +699,12 @@ void CA_put_callback(SV *ca_ref, SV *sub, SV *val, ...) { } } else { union { + void *dbr; dbr_char_t *dbr_char; dbr_long_t *dbr_long; dbr_double_t *dbr_double; char *dbr_string; - void *dbr; - } p; + } p = {0}; int i; chtype type = best_type(pch); diff --git a/modules/libcom/src/osi/os/posix/osdThread.c b/modules/libcom/src/osi/os/posix/osdThread.c index 50bb714e0..61a67f62c 100644 --- a/modules/libcom/src/osi/os/posix/osdThread.c +++ b/modules/libcom/src/osi/os/posix/osdThread.c @@ -24,7 +24,12 @@ #include #include -#define USE_MEMLOCK (defined(_POSIX_MEMLOCK) && (_POSIX_MEMLOCK > 0) && !defined(__rtems__)) +#if (defined(_POSIX_MEMLOCK) && (_POSIX_MEMLOCK > 0) && !defined(__rtems__)) +# define USE_MEMLOCK 1 +#else +# define USE_MEMLOCK 0 +#endif + #if USE_MEMLOCK #include #endif From ce876d6f114fa1e293c1b278bc11f05bba4cb886 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sun, 20 Jun 2021 13:13:31 -0500 Subject: [PATCH 013/169] Adjust RTEMS-mvme2100 and -mvme2700 build config's Untested. These changes let them build, but `make -j` is still broken. --- configure/os/CONFIG.Common.RTEMS-mvme2100 | 1 + configure/os/CONFIG.Common.RTEMS-mvme2700 | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/configure/os/CONFIG.Common.RTEMS-mvme2100 b/configure/os/CONFIG.Common.RTEMS-mvme2100 index 1e222b5ab..930b33bad 100644 --- a/configure/os/CONFIG.Common.RTEMS-mvme2100 +++ b/configure/os/CONFIG.Common.RTEMS-mvme2100 @@ -19,6 +19,7 @@ define MUNCH_CMD $(PROJECT_RELEASE)/lib/bootloader.o \ --just-symbols=$< \ -b binary rtems.gz \ + --no-warn-mismatch \ -T $(PROJECT_RELEASE)/lib/ppcboot.lds \ -Map $<.map rm -f rtems.gz diff --git a/configure/os/CONFIG.Common.RTEMS-mvme2700 b/configure/os/CONFIG.Common.RTEMS-mvme2700 index 655e0a5b0..0ee8ac862 100644 --- a/configure/os/CONFIG.Common.RTEMS-mvme2700 +++ b/configure/os/CONFIG.Common.RTEMS-mvme2700 @@ -1,7 +1,7 @@ # # Author: Matt Rippa # -RTEMS_BSP = mvme2700 +RTEMS_BSP = mvme2307 RTEMS_TARGET_CPU = powerpc ARCH_DEP_CFLAGS += -DMY_DO_BOOTP=NULL ARCH_DEP_CFLAGS += -DHAVE_PPCBUG @@ -15,6 +15,7 @@ define MUNCH_CMD $(PROJECT_RELEASE)/lib/bootloader.o \ --just-symbols=$< \ -b binary rtems.gz \ + --no-warn-mismatch \ -T $(PROJECT_RELEASE)/lib/ppcboot.lds \ -Map $<.map rm -f rtems.gz From 1cacd058cd3bd1d9b6cabc6219c48217913eb1e8 Mon Sep 17 00:00:00 2001 From: Kathryn Baker Date: Sun, 20 Jun 2021 19:17:44 -0500 Subject: [PATCH 014/169] Stdio doxygen annotations --- modules/libcom/src/osi/epicsStdio.h | 113 ++++++++++++++++++++++++---- 1 file changed, 100 insertions(+), 13 deletions(-) diff --git a/modules/libcom/src/osi/epicsStdio.h b/modules/libcom/src/osi/epicsStdio.h index b37adfeb0..9bb5eb3e0 100644 --- a/modules/libcom/src/osi/epicsStdio.h +++ b/modules/libcom/src/osi/epicsStdio.h @@ -7,8 +7,42 @@ * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ - -/* epicsStdio.h */ +/** + * \file epicsStdio.h + * \brief Standardize the behaviour of stdio across EPICS targets + * + * \details + * The `epicsStdio.h` header includes the operating system's `stdio.h` header + * and if used should replace it. + * + * epicsSnprintf() and epicsVsnprintf() have the same semantics as the C99 + * functions snprintf() and vsnprintf(), but correct the behaviour of the + * implementations on some operating systems. + * + * The routines epicsGetStdin(), epicsGetStdout(), epicsGetStderr(), + * epicsStdoutPrintf(), epicsStdoutPuts(), and epicsStdoutPutchar() + * are not normally named directly in user code. They are provided for use by + * various macros which redefine several standard C identifiers. + * This is done so that any I/O through these standard streams can be + * redirected, usually to or from a file. The primary use of this facility + * is for iocsh() and any commands called from it, allowing redirection of + * the standard input and/or output streams while running those commands. + * In order for this redirection to work, all modules involved in such I/O + * must include `epicsStdio.h` instead of the system header `stdio.h`. + * The redirections are: + * - `stdin` becomes epicsGetStdin() + * - `stdout` becomes epicsGetStdout() + * - `stderr` becomes epicsGetStderr() + * - `printf` becomes epicsStdoutPrintf() + * - `puts` becomes epicsStdoutPuts() + * - `putchar` becomes epicsStdoutPutchar() + * + * The epicsSetThreadStdin(), epicsSetThreadStdout() and epicsSetThreadStderr() + * routines allow the standard file streams to be redirected on a per thread + * basis, e.g. calling epicsThreadStdout() will affect only the thread which + * calls it. To cancel a stream redirection, pass a NULL argument in another + * call to the same redirection routine that was used to set it. + */ #ifndef epicsStdioh #define epicsStdioh @@ -52,23 +86,76 @@ extern "C" { # define putchar epicsStdoutPutchar #endif +/** + * \brief epicsSnprintf() is meant to have the same semantics as the C99 + * function snprintf() + * + * \details + * This is provided because some architectures do not implement these functions, + * while others implement them incorrectly. + * Standardized as a C99 function, snprintf() acts like sprintf() except that + * the `size` argument gives the maximum number of characters (including the + * trailing zero byte) that may be placed in `str`. + * + * On some operating systems though the implementation of this function does + * not always return the correct value. If the OS implementation does not + * correctly return the number of characters that would have been written when + * the output gets truncated, it is not worth trying to fix this as long as + * they return `size-1` instead; the resulting string must always be correctly + * terminated with a zero byte. + * + * In some scenarios the epicsSnprintf() implementation may not provide the + * correct C99 semantics for the return value when `size` is given as zero. + * On these systems epicsSnprintf() can return an error (a value less than + * zero) when a buffer length of zero is passed in, so callers should not use + * that technique to calculate the length of the buffer required. + * + * \return The number of characters (not counting the terminating zero byte) + * that would be written to `str` if it was large enough to hold them all; the + * output has been truncated if the return value is `size` or more. + */ LIBCOM_API int epicsStdCall epicsSnprintf( char *str, size_t size, const char *format, ...) EPICS_PRINTF_STYLE(3,4); +/** + * \brief epicsVsnprintf() is meant to have the same semantics as the C99 + * function vsnprintf() + * + * \details + * This is provided because some architectures do not implement these functions, + * while others implement them incorrectly. + * Standardized as a C99 function, vsnprintf() acts like vsprintf() except that + * the `size` argument gives the maximum number of characters (including the + * trailing zero byte) that may be placed in `str`. + * + * On some operating systems though the implementation of this function does + * not always return the correct value. If the OS implementation does not + * correctly return the number of characters that would have been written when + * the output gets truncated, it is not worth trying to fix this as long as + * they return `size-1` instead; the resulting string must always be correctly + * terminated with a zero byte. + * + * In some scenarios the epicsSnprintf() implementation may not provide the + * correct C99 semantics for the return value when `size` is given as zero. + * On these systems epicsSnprintf() can return an error (a value less than + * zero) when a buffer length of zero is passed in, so callers should not use + * that technique to calculate the length of the buffer required. + * + * \return The number of characters (not counting the terminating zero byte) + * that would be written to `str` if it was large enough to hold them all; the + * output has been truncated if the return value is `size` or more. + */ LIBCOM_API int epicsStdCall epicsVsnprintf( char *str, size_t size, const char *format, va_list ap); -/* - * truncate to specified size (we dont use truncate() - * because it is not portable) - * - * pFileName - name (and optionally path) of file - * size - the new file size (if file is curretly larger) - * - * returns TF_OK if the file is less than size bytes - * or if it was successfully truncated. Returns - * TF_ERROR if the file could not be truncated. - */ enum TF_RETURN {TF_OK=0, TF_ERROR=1}; +/** + * \brief Truncate a file to a specified size + * \note The BSD function truncate() was not available on all targets + * \param pFileName Name (and optionally path) of file + * \param size The new file size (if files is currently larger) + * \return `TF_OK` if the file is less that size bytes or if it was successfully + * truncated; `TF_ERROR` if the file could not be truncated + */ LIBCOM_API enum TF_RETURN truncateFile ( const char *pFileName, unsigned long size ); /* The following are for redirecting stdin,stdout,stderr */ From c36485ab2e307e0370d1e3c47b4474b9f38c995e Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Thu, 24 Jun 2021 10:54:14 +0200 Subject: [PATCH 015/169] fix missing declaration of 'close' function in vxWorks 6.3 and lower --- .../libcom/src/osi/os/vxWorks/osdFindSymbol.c | 1 + modules/libcom/src/osi/os/vxWorks/osiUnistd.h | 21 +++++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 modules/libcom/src/osi/os/vxWorks/osiUnistd.h diff --git a/modules/libcom/src/osi/os/vxWorks/osdFindSymbol.c b/modules/libcom/src/osi/os/vxWorks/osdFindSymbol.c index 59bfc4fdf..b60cf0e62 100644 --- a/modules/libcom/src/osi/os/vxWorks/osdFindSymbol.c +++ b/modules/libcom/src/osi/os/vxWorks/osdFindSymbol.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "dbmf.h" #include "epicsString.h" diff --git a/modules/libcom/src/osi/os/vxWorks/osiUnistd.h b/modules/libcom/src/osi/os/vxWorks/osiUnistd.h new file mode 100644 index 000000000..3f767ae09 --- /dev/null +++ b/modules/libcom/src/osi/os/vxWorks/osiUnistd.h @@ -0,0 +1,21 @@ +/*************************************************************************\ +* Copyright (c) 2002 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. +* SPDX-License-Identifier: EPICS +* EPICS Base is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include + +/* VxWorks has some functions in ioLib.h instead of unistd.h, + for example close() and read() + */ +#include From 4dad8ca50351cec1a6addcc8edb89f4ba46ee818 Mon Sep 17 00:00:00 2001 From: Freddie Akeroyd Date: Fri, 25 Jun 2021 11:27:20 +0100 Subject: [PATCH 016/169] Use rather than Fix compile issue building PCAS module, which just includes resulting in LIBCOM_API being undefined --- modules/ca/src/client/caProto.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ca/src/client/caProto.h b/modules/ca/src/client/caProto.h index 397b1edaf..c6e211c11 100644 --- a/modules/ca/src/client/caProto.h +++ b/modules/ca/src/client/caProto.h @@ -18,7 +18,7 @@ #define INC_caProto_H // Pick up definition of IPPORT_USERRESERVED -#include +#include #define capStrOf(A) #A #define capStrOfX(A) capStrOf ( A ) From e8c5748f89c1a52ea1523285a0c1a660720b9148 Mon Sep 17 00:00:00 2001 From: Freddie Akeroyd Date: Fri, 25 Jun 2021 11:37:07 +0100 Subject: [PATCH 017/169] Use rather than --- src/libCom/osi/os/default/epicsMMIODef.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libCom/osi/os/default/epicsMMIODef.h b/src/libCom/osi/os/default/epicsMMIODef.h index dc4bb4cb7..d517b7031 100644 --- a/src/libCom/osi/os/default/epicsMMIODef.h +++ b/src/libCom/osi/os/default/epicsMMIODef.h @@ -123,7 +123,7 @@ bswap32(epicsUInt32 value) #elif EPICS_BYTE_ORDER == EPICS_ENDIAN_LITTLE /* Get hton[sl] declarations: */ -#include +#include /** @ingroup mmio *@{ From 5c3ecf9054000310e38ab96b59b4dee99a070009 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sat, 26 Jun 2021 17:55:11 -0500 Subject: [PATCH 018/169] Dump the Darwin README file, very dated --- documentation/README.darwin.html | 184 ------------------------------- 1 file changed, 184 deletions(-) delete mode 100644 documentation/README.darwin.html diff --git a/documentation/README.darwin.html b/documentation/README.darwin.html deleted file mode 100644 index dd11ce857..000000000 --- a/documentation/README.darwin.html +++ /dev/null @@ -1,184 +0,0 @@ - - -Installation notes for EPICS on Mac OS X (Darwin) - - - - -

Building EPICS base

-
    -
  • -To build base: -
      -
    1. -Set the EPICS_HOST_ARCH environment variable to darwin-ppc, darwin-x86 or darwin-ppcx86. -The scripts in the -base/startup directory can automate this. For example, here's part -of my Bash login script (~/.bash_login): -
      -#
      -# EPICS
      -#
      -EPICS_BASE="${HOME}/src/EPICS/base"
      -EPICS_EXTENSIONS="${HOME}/src/EPICS/extensions"
      -. "${EPICS_BASE}"/startup/unix.sh
      -
      -
    2. -
    3. -cd to the EPICS base top-level source directory. -
    4. -
    5. -Uncomment the appropriate line in the relevent -EPICS_BASE/configure/os/CONFIG_SITE.Common.darwin-xxx file for your EPICS_HOST_ARCH value. -Newer versions of OS X (e.g. Snow Leopard) may include only 64 bit versions of some OS libraries, -so should only have the x86_64 ARCH_CLASS. -
    6. -
    7. -Run make. -
    8. -
    - -
  • - -
  • -As distributed, EPICS on Mac OS X uses the readline command line input -routines. IOC applications are more pleasant to interact with if -either the readline or libtecla library is used. The easiest -way to get either or both of these libraries on to your system is to -download and install them using the either the DarwinPorts -distribution or the Fink package manager. If you don't want to install -the readline library, set the COMMANDLINE_LIBRARY variable in one of -the CONFIG_SITE files to EPICS. -

    -Information on DarwinPorts is available from -the DarwinPorts -project page. -DarwinPorts binary packages are available from -here. -

    -Fink may be downloaded from -the Source Forge. -

  • - -
  • -If broadcasts are not seen locally, try adding "localhost" (127.0.0.1) -to the EPICS_CA_ADDR_LIST. -
  • -
- -

Building EPICS extensions

-

-Many extensions build and run properly on OS X. To build and run medm, first -obtain the X11 run-time and developer packages from Apple and the OpenMotif3 -package from Fink. - -

Objective-C and AppleScript

-

-Code written in Objective-C can be included in host or IOC applications. -Here are a couple of short Objective-C examples which can be used to send -AppleScript events to other applications on the OS X machine. - -

-/*
- * exampleAppleScriptRecord.m 
- *
- * Simple Objective-C/AppleScript subroutine record
- *
- * To use this record in an application:
- *
- * 1) Make the following changes to the application Makefile:
- *    - Add exampleAppleScriptRecord.m to the application SRCS.
- *    - Add -framework Foundation to the application LDFLAGS.
- * 2) Add the following line to the application database description:
- *      registrar(registerExampleAppleScript)
- * 3) Add a record to the application database:
- *      record(sub,"setVolume")
- *      {
- *          field(SNAM,"exampleAppleScriptProcess")
- *      }
- */
-#import <Foundation/Foundation.h>
-#include <registryFunction.h>
-#include <subRecord.h>
-#include <alarm.h>
-#include <errlog.h>
-#include <recGbl.h>
-#include <epicsExport.h>
-
-/*
- * Shim between EPICS and NSAppleScript class.
- */
-static long
-exampleAppleScriptProcess(struct subRecord *psub)
- {
-    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-    NSDictionary *err;
-    NSAppleScript *nsa;
-    
-    nsa = [[NSAppleScript alloc] initWithSource:[NSString stringWithFormat:
-                @"tell application \"Finder\" to set volume %g\n", psub->a]];
-    if ([nsa executeAndReturnError:&err] == nil) {
-        errlogPrintf("Failed to run AppleScript: %s\n",
-                        [[err objectForKey:NSAppleScriptErrorMessage] cString]);
-        recGblSetSevr(psub, SOFT_ALARM, INVALID_ALARM);
-    }
-    [nsa release];
-    [pool release];
-    return 0;
-}
-
-static registryFunctionRef subRef[] = {
-    {"exampleAppleScriptProcess",(REGISTRYFUNCTION)exampleAppleScriptProcess}
-};
-
-static void registerExampleAppleScript(void)
-{
-    registryFunctionRefAdd(subRef,NELEMENTS(subRef));
-}
-epicsExportRegistrar(registerExampleAppleScript);
-
-
-==============================================================================
-/*
- * runAppleScript.m 
- *
- * Simple Objective-C/AppleScript shim to allow EPICS application to
- * send arbitrary AppleScript messages to other applications.
- *
- * To use this subroutine in an application make the following
- * changes to the application Makefile:
- * - Add runAppleScript.m to the application SRCS.
- * - Add -framework Foundation to the application LDFLAGS.
- */
-#import <Foundation/Foundation.h>
-#include <errlog.h>
-
-int
-runAppleScript(const char *format, ...)
-{
-    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-    NSString *script;
-    NSMutableDictionary *err;
-    NSAppleScript *nsa;
-    va_list args;
-    int ret = 0;
-    
-    va_start(args, format);
-    script = [[NSString alloc] initWithFormat:
-                            [NSString stringWithCString:format] arguments:args];
-    va_end(args);
-    err = [NSMutableDictionary dictionaryWithCapacity:10];
-    nsa = [[NSAppleScript alloc] initWithSource:script];
-    if ([nsa executeAndReturnError:&err] == nil) {
-        errlogPrintf("Failed to run AppleScript: %s\n",
-                        [[err objectForKey:NSAppleScriptErrorMessage] cString]);
-        ret = -1;
-    }
-    [script release];
-    [nsa release];
-    [pool release];
-    return ret;
-}
-
- - From 28531b0dbb835b0ab6e9f3205f4044da0fa25501 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sat, 26 Jun 2021 17:57:33 -0500 Subject: [PATCH 019/169] Reworked the README.md file Lots of updates; removed version number --- documentation/README.md | 405 +++++++++++++++++++++------------------- 1 file changed, 217 insertions(+), 188 deletions(-) diff --git a/documentation/README.md b/documentation/README.md index 770f14bad..7698d14e2 100644 --- a/documentation/README.md +++ b/documentation/README.md @@ -1,28 +1,28 @@ # Installation Instructions -## EPICS Base Release 3.15.8 +## EPICS Base Release 3.15.x ----- ### Table of Contents - - [What is EPICS base?](#0_0_1) - - [What is new in this release?](#0_0_2) - - [Copyright](#0_0_3) - - [Supported platforms](#0_0_4) - - [Supported compilers](#0_0_5) - - [Software requirements](#0_0_6) - - [Host system storage requirements](#0_0_7) - - [Documentation](#0_0_8) - - [Directory Structure](#0_0_10) - - [Build related components](#0_0_11) - - [Building EPICS base (Unix and Win32)](#0_0_12) - - [Example application and extension](#0_0_13) - - [Multiple host platforms](#0_0_14) + - [What is EPICS base?](#what-is-epics-base?) + - [What is new in this release?](#what-is-new-in-this-release?) + - [Copyright](#copyright) + - [Supported platforms](#supported-platforms) + - [Supported compilers](#supported-compilers) + - [Software requirements](#software-requirements) + - [Host system storage requirements](#host-system-storage-requirements) + - [Documentation](#documentation) + - [Directory Structure](#directory-structure) + - [Site-specific build configuration](#site-specific-build-configuration) + - [Building EPICS base](#building-epics-base) + - [Example application and extension](#example-application-and-extension) + - [Multiple host platforms](#multiple-host-platforms) ----- -### What is EPICS base? +### What is EPICS base? The Experimental Physics and Industrial Control Systems (EPICS) is an extensible set of software components and tools with which application @@ -34,17 +34,17 @@ function. EPICS base allows an arbitrary number of target systems, IOCs (input/output controllers), and host systems, OPIs (operator interfaces) of various types. -### What is new in this release? +### What is new in this release? -Please check the `RELEASE_NOTES` file in the distribution for +Please check the `documentation/RELEASE_NOTES.md` file for description of changes and release migration details. -### Copyright +### Copyright -Please review the LICENSE file included in the distribution for legal -terms of usage. +Please review the `LICENSE` file included in the distribution for +legal terms of usage. -### Supported platforms +### Supported platforms The list of platforms supported by this version of EPICS base is given in the `configure/CONFIG_SITE` file. If you are trying to build EPICS @@ -55,7 +55,7 @@ base/configure/os/directory. You can start by copying existing configuration files in the configure/os directory and then make changes for your new platforms. -### Supported compilers +### Supported compilers This version of EPICS base has been built and tested using the host vendor's C and C++ compilers, as well as the GNU gcc and g++ @@ -64,27 +64,32 @@ targets. You may need the C and C++ compilers to be in your search path to do EPICS builds; check the definitions of CC and CCC in `base/configure/os/CONFIG..` if you have problems. -### Software requirements +### Software requirements -**GNU make** -You must use GNU make, gnumake, for any EPICS builds. Set your path so -that a gnumake version 3.81 or later is available. +#### GNU make + +You must use the GNU version of `make` for EPICS builds. Set your path +so that version 3.81 or later is available (4.0 or later on Windows). + +#### Perl -**Perl** You must have Perl version 5.8.1 or later installed. The EPICS configuration files do not specify the perl full pathname, so the perl executable must be found through your normal search path. -**Unzip and tar (Winzip on WIN32 systems)** +#### Unzip and tar (Winzip on WIN32 systems) + You must have tools available to unzip and untar the EPICS base distribution file. -**Target systems** +#### Target systems + EPICS supports IOCs running on embedded platforms such as VxWorks and RTEMS built using a cross-compiler, and also supports soft IOCs running as processes on the host platform. -**vxWorks** +#### vxWorks + You must have vxWorks 5.5.x or 6.x installed if any of your target systems are vxWorks systems; the C++ compiler for vxWorks 5.4 is now too old to support. The vxWorks installation provides the @@ -98,134 +103,148 @@ Consult the [vxWorks 5.x](https://epics.anl.gov/base/tornado.php) or about and the vxWorks documentation for information about configuring your vxWorks operating system for use with EPICS. -**RTEMS** +#### RTEMS + For RTEMS targets, you need RTEMS core and toolset version 4.9.2 or -later. +4.10.x. RTEMS 5 and above are only supported in EPICS 7.0.6 or later. -**GNU readline or Tecla library** -GNU readline and Tecla libraries can be used by the IOC shell to -provide command line editing and command line history recall and edit. -GNU readline (or Tecla library) must be installed on your target -system when `COMMANDLINE_LIBRARY` is set to READLINE (or TECLA) for -that target. EPICS (EPICS shell) is the default specified in -`CONFIG_COMMON`. A READLINE override is defined for linux-x86 in the -EPICS distribution. Comment out `COMMANDLINE_LIBRARY=READLINE` in -`configure/os/CONFIG_SITE.Common.linux-x86` if readline is not -installed on linux-x86. Command-line editing and history will then be -those supplied by the os. On vxWorks the ledLib command-line input -library is used instead. +#### Command Line Editing -### Host system storage requirements -The compressed tar file is approximately 1.6 MB in size. The +GNU readline and other similar libraries can be used by the IOC shell +to provide command line editing and command line history recall. The +GNU readline development package (or Apple's emulator on macOS) must +be installed for a target when its build configuration variable +`COMMANDLINE_LIBRARY` is set to `READLINE`. The default specified in +`CONFIG_COMMON` is `EPICS`, but most linux target builds can detect if +readline is available and will then use it. RTEMS targets may be +configured to use `LIBTECLA` if available, and on vxWorks the OS's +ledLib line-editing library is normally used. + +### Host system storage requirements + +The compressed tar file is approximately 1.7 MB in size. The distribution source tree takes up approximately 12 MB. Each host -target will need around 40 MB for build files, and each cross-compiled -target around 20 MB. +target will need around 50 MB for build files, and each cross-compiled +target around 30 MB. -### Documentation +### Documentation EPICS documentation is available through the [EPICS website](https://epics.anl.gov/) at Argonne. Release specific documentation can also be found in the -base/documentation directory of the distribution. +`base/documentation` directory of the distribution. -### Directory Structure +### Directory Structure -#### Distribution directory structure: +#### Distribution directory structure ``` - base Root directory of the base distribution - base/configure Operating system independent build config files - base/configure/os Operating system dependent build config files - base/documentation Distribution documentation - base/src Source code in various subdirectories - base/startup Scripts for setting up path and environment + base Root directory of the distribution + base/configure Build rules and OS-independent config files + base/configure/os OS-dependent build config files + base/documentation Distribution documentation + base/src Source code in various subdirectories + base/startup Scripts for setting up path and environment ``` -#### Install directories created by the build: +#### Directories created by the build + +These are created in the root directory of the installation (`base` +above) or under the directory pointed to by the `INSTALL_LOCATION` +configuration variable if that has been set. ``` - bin Installed scripts and executables in subdirs - cfg Installed build configuration files - db Installed data bases - dbd Installed data base definitions - doc Installed documentation files - html Installed html documentation - include Installed header files - include/os Installed os specific header files in subdirs - include/compiler Installed compiler-specific header files - lib Installed libraries in arch subdirectories - lib/perl Installed perl modules - templates Installed templates + bin Installed scripts and executables in subdirs + cfg Installed build configuration files + db Installed database files + dbd Installed database definition files + html Installed html documentation + include Installed header files + include/os Installed OS-specific header files in subdirs + include/compiler Installed compiler-specific header files + lib Installed libraries in arch subdirectories + lib/perl Installed perl modules + templates Installed templates ``` -### Build related components +#### `base/documentation` Directory -#### base/documentation directory - contains setup, build, and install documents +This contains documents on how to setup, build, and install EPICS. ``` - README.md Instructions for setup and building epics base - README.darwin.html Installation notes for Mac OS X (Darwin) - RELEASE_NOTES.html Notes on release changes - KnownProblems.html List of known problems and workarounds + README.md This file + RELEASE_NOTES.md Notes on release changes + KnownProblems.html List of known problems and workarounds ``` -#### base/startup directory - contains scripts to set environment and path +#### `base/startup` Directory + +This contains several example scripts that show how to set up the +build environment and PATH for using EPICS. Sites would usually copy and/or modify these files as appropriate for their environment; they are not used by the build system at all. ``` - EpicsHostArch Shell script to set EPICS_HOST_ARCH env variable - unix.csh C shell script to set path and env variables - unix.sh Bourne shell script to set path and env variables - win32.bat Bat file example to configure win32-x86 target - windows.bat Bat file example to configure windows-x64 target + EpicsHostArch Shell script to set EPICS_HOST_ARCH env variable + unix.csh C shell script to set path and env variables + unix.sh Bourne shell script to set path and env variables + win32.bat Bat file example to configure win32-x86 target + windows.bat Bat file example to configure windows-x64 target ``` -#### base/configure directory - contains build definitions and rules +#### `base/configure` directory + +This contains build-system files providing definitions and rules +required by GNU Make to build EPICS. Users should only need to modify the `CONFIG_SITE` files to configure the EPICS build. ``` - CONFIG Includes configure files and allows variable overrides - CONFIG.CrossCommon Cross build definitions - CONFIG.gnuCommon Gnu compiler build definitions for all archs - CONFIG_ADDONS Definitions for and DEFAULT options - CONFIG_APP_INCLUDE - CONFIG_BASE EPICS base tool and location definitions - CONFIG_BASE_VERSION Definitions for EPICS base version number - CONFIG_COMMON Definitions common to all builds - CONFIG_ENV Definitions of EPICS environment variables - CONFIG_FILE_TYPE - CONFIG_SITE Site specific make definitions - CONFIG_SITE_ENV Site defaults for EPICS environment variables - MAKEFILE Installs CONFIG* RULES* creates - RELEASE Location of external products - RULES Includes appropriate rules file - RULES.Db Rules for database and database definition files - RULES.ioc Rules for application iocBoot/ioc* directory - RULES_ARCHS Definitions and rules for building architectures - RULES_BUILD Build and install rules and definitions - RULES_DIRS Definitions and rules for building subdirectories - RULES_EXPAND - RULES_FILE_TYPE - RULES_TARGET - RULES_TOP Rules specific to a dir (uninstall and tar) - Sample.Makefile Sample makefile with comments + CONFIG Main entry point for building EPICS + CONFIG.CrossCommon Cross build definitions + CONFIG.gnuCommon Gnu compiler build definitions for all archs + CONFIG_ADDONS Definitions for and DEFAULT options + CONFIG_APP_INCLUDE + CONFIG_BASE EPICS base tool and location definitions + CONFIG_BASE_VERSION Definitions for EPICS base version number + CONFIG_COMMON Definitions common to all builds + CONFIG_ENV Definitions of EPICS environment variables + CONFIG_FILE_TYPE + CONFIG_SITE Site specific make definitions + CONFIG_SITE_ENV Site defaults for EPICS environment variables + MAKEFILE Installs CONFIG* RULES* creates + RELEASE Location of external products + RULES Includes appropriate rules file + RULES.Db Rules for database and database definition files + RULES.ioc Rules for application iocBoot/ioc* directory + RULES_ARCHS Definitions and rules for building architectures + RULES_BUILD Build and install rules and definitions + RULES_DIRS Definitions and rules for building subdirectories + RULES_EXPAND + RULES_FILE_TYPE + RULES_TARGET + RULES_TOP Rules specific to a dir only + Sample.Makefile Sample makefile with comments ``` -#### base/configure/os directory - contains os-arch specific definitions +#### `base/configure/os` Directory + +Files in here provide definitions that are shared by or specific to particular host and/or target architectures. Users should only need to modify the `CONFIG_SITE` files in this directory to configure the EPICS build. ``` - CONFIG.. Specific host-target build definitions - CONFIG.Common. Specific target definitions for all hosts - CONFIG..Common Specific host definitions for all targets - CONFIG.UnixCommon.Common Definitions for Unix hosts and all targets - CONFIG.Common.UnixCommon Definitions for Unix targets and all hosts - CONFIG.Common.vxWorksCommon Specific host definitions for all vx targets - CONFIG_SITE.. Site specific host-target definitions - CONFIG_SITE.Common. Site specific target defs for all hosts - CONFIG_SITE..Common Site specific host defs for all targets + CONFIG.. Definitions for a specific host-target combination + CONFIG.Common. Definitions for a specific target, any host + CONFIG..Common Definitions for a specific host, any target + CONFIG.UnixCommon.Common Definitions for Unix hosts, any target + CONFIG.Common.UnixCommon Definitions for Unix targets, any host + CONFIG.Common.RTEMS Definitions for all RTEMS targets, any host + CONFIG.Common.vxWorksCommon Definitions for all vxWorks targets, any host + CONFIG_SITE.. Local settings for a specific host-target combination + CONFIG_SITE.Common. Local settings for a specific target, any host + CONFIG_SITE..Common Local settings for a specific host, any target + CONFIG_SITE.Common.RTEMS Local settings for all RTEMS targets, any host + CONFIG_SITE.Common.vxWorksCommon Local settings for all vxWorks targets, any host ``` -### Building EPICS base (Unix and Win32) +### Building EPICS base #### Unpack file @@ -237,7 +256,7 @@ systems. Files in the base/startup directory have been provided to help set required path and other environment variables. -* `EPICS_HOST_ARCH` +* **`EPICS_HOST_ARCH`** Before you can build or use EPICS R3.15, the environment variable `EPICS_HOST_ARCH` must be defined. A perl script EpicsHostArch.pl in the base/startup directory has been provided to help set @@ -250,65 +269,66 @@ compilers on a solaris host or "-mingw" for MinGW c/c++ compilers on a WIN32 host). See `configure/CONFIG_SITE` for a list of supported `EPICS_HOST_ARCH` values. -* `PERLLIB` -On WIN32, some versions of Perl require that the environment -variable PERLLIB be set to <perl directory location>. +* **`PATH`** +As already mentioned, you must have the `perl` executable and you may +need C and C++ compilers in your search path. When building base you +must have `echo` in your search path. For Unix host builds you will +also need `cp`, `rm`, `mv`, and `mkdir` in your search path. Some Unix +systems may also need `ar` and `ranlib`, and the C/C++ compilers may +require `as` and `ld` in your path. On Solaris systems you need +`uname` in your path. -* `PATH` -As already mentioned, you must have the perl executable and you may -need C and C++ compilers in your search path. For building base you -also must have echo in your search path. For Unix host builds you -also need ln, cpp, cp, rm, mv, and mkdir in your search path and -/bin/chmod must exist. On some Unix systems you may also need ar and -ranlib in your path, and the C compiler may require as and ld in -your path. On solaris systems you need uname in your path. +* **`LD_LIBRARY_PATH`** +R3.15 shared libraries and executables normally contain the full path +to any libraries they require, so setting this variable is not usually +necessary. However, if you move the EPICS installation to a new +location after building it then in order for the shared libraries to +be found at runtime it may need to be set, or some equivalent +OS-specific mechanism such as `/etc/ld.so.conf` on Linux must be used. +Shared libraries are now built by default on all Unix type hosts. -* `LD_LIBRARY_PATH` -R3.15 shared libraries and executables normally contain the full -path to any libraries they require. However, if you move the EPICS -files or directories from their build-time location then in order -for the shared libraries to be found at runtime `LD_LIBRARY_PATH` -must include the full pathname to -`$(INSTALL_LOCATION)/lib/$(EPICS_HOST_ARCH)` when invoking -executables, or some equivalent OS-specific mechanism (such as -/etc/ld.so.conf on Linux) must be used. Shared libraries are now -built by default on all Unix type hosts. +### Site-specific build configuration -#### Do site-specific build configuration +#### Site configuration -**Site configuration** -To configure EPICS, you may want to modify the default definitions -in the following files: +To configure EPICS, you may want to modify some values set in the +following files: ``` - configure/CONFIG_SITE Build choices. Specify target archs. - configure/CONFIG_SITE_ENV Environment variable defaults - configure/RELEASE TORNADO2 full path location + configure/CONFIG_SITE Build settings. Specify target archs. + configure/CONFIG_SITE_ENV Environment variable defaults ``` -**Host configuration** -To configure each host system, you may override the default -definitions by adding a new file in the configure/os directory with -override definitions. The new file should have the same name as the -distribution file to be overridden except with CONFIG in the name -changed to `CONFIG_SITE`. +#### Host configuration + +To configure each host system, you can override the default +definitions by adding a new settings file (or editing an existing +settings file) in the `configure/os` directory with your override +definitions. The settings file has the same name as the definitions +file to be overridden except with `CONFIG` in the name changed to +`CONFIG_SITE`. ``` - configure/os/CONFIG.. Host build settings - configure/os/CONFIG..Common Host common build settings + configure/os/CONFIG.. Host self-build definitions + configure/os/CONFIG..Common Host common build definitions + configure/os/CONFIG_SITE.. Host self-build overrides + configure/os/CONFIG_SITE..Common Host common build overrides ``` -**Target configuration** +#### Target configuration + To configure each target system, you may override the default -definitions by adding a new file in the configure/os directory with -override definitions. The new file should have the same name as the -distribution file to be overridden except with CONFIG in the name -replaced by `CONFIG_SITE`. This step is necessary even if the host -system is the only target system. +definitions by adding a new settings file (or editing an existing +settings file) in the `configure/os` directory with your override +definitions. The settings file has the same name as the definitions +file to be overridden except with `CONFIG` in the name changed to +`CONFIG_SITE`. ``` - configure/os/CONFIG.Common. Target common settings - configure/os/CONFIG.. Host-target settings + configure/os/CONFIG.Common. Target common definitions + configure/os/CONFIG.. Host-target definitions + configure/os/CONFIG_SITE.Common. Target common overrides + configure/os/CONFIG_SITE.. Host-target overrides ``` #### Build EPICS base @@ -318,24 +338,29 @@ by issuing the following commands in the distribution's root directory (base): ``` - gnumake clean uninstall - gnumake + make distclean + make ``` -The command "gnumake clean uninstall" will remove all files and -directories generated by a previous build. The command "gnumake" +The command `make distclean` will remove all files and +directories generated by a previous build. The command `make` will build and install everything for the configured host and targets. -It is recommended that you do a "gnumake clean uninstall" at the +It is recommended that you do a `make distclean` at the root directory of an EPICS directory structure before each complete rebuild to ensure that all components will be rebuilt. -### Example application and extension +In some cases GNU Make may have been installed as `gmake` or +`gnumake`, in which case the above commands will have to be adjusted +to match. -A perl tool, makeBaseApp.pl is included in the distribution file. This -script will create a sample application that can be built and then -executed to try out this release of base. +### Example application and extension + +A perl tool `makeBaseApp.pl` and several template applications are +included in the distribution. This script instantiates the selected +template into an empty directory to provide an example application +that can be built and then executed to try out this release of base. Instructions for building and executing the 3.15 example application can be found in the section "Example Application" of Chapter 2, @@ -348,26 +373,30 @@ application as a host-based IOC, you will be able to quickly implement a complete EPICS system and be able to run channel access clients on the host system. -A perl script, makeBaseExt.pl, is included in the distribution file. -This script will create a sample extension that can be built and -executed. The makeBaseApp.pl and makeBaseExt.pl scripts are installed -into the install location `bin/` directory during the base -build. +Another perl script `makeBaseExt.pl` is also included in the +distribution file for creating an extensions tree and sample +application that can also be built and executed. Both these scripts +are installed into the install location `bin/` directory +during the base build. -### Multiple host platforms +### Multiple host platforms You can build using a single EPICS directory structure on multiple host systems and for multiple cross target systems. The intermediate and binary files generated by the build will be created in separate subdirectories and installed into the appropriate separate host/target -install directories. EPICS executables and perl scripts are installed -into the `$(INSTALL_LOCATION)/bin/` directories. Libraries are -installed into $`(INSTALL_LOCATION)/lib/`. The default -definition for `$(INSTALL_LOCATION)` is `$(TOP)` which is the root -directory in the distribution directory structure, base. Created -object files are stored in `O.` source subdirectories, This -allows objects for multiple cross target architectures to be -maintained at the same time. To build EPICS base for a specific +install directories. + +EPICS executables and perl scripts are installed into the +`$(INSTALL_LOCATION)/bin/` directories. Libraries are installed +into $`(INSTALL_LOCATION)/lib/`. The default definition for +`$(INSTALL_LOCATION)` is `$(TOP)` which is the root directory in the +distribution directory structure, `base`. Intermediate object files +are stored in `O.` source subdirectories during the build +process, to allow objects for multiple cross target architectures +to be maintained at the same time. + +To build EPICS base for a specific host/target combination you must have the proper host/target C/C++ cross compiler and target header files and the base/configure/os directory must have the appropriate configure files. From ebf4a155d729748704b1b20c83018db42a5d82d1 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sat, 26 Jun 2021 18:00:28 -0500 Subject: [PATCH 020/169] Added missing entries to Release Notes --- documentation/RELEASE_NOTES.md | 35 +++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/documentation/RELEASE_NOTES.md b/documentation/RELEASE_NOTES.md index 66d843645..befae922b 100644 --- a/documentation/RELEASE_NOTES.md +++ b/documentation/RELEASE_NOTES.md @@ -6,20 +6,41 @@ This version of EPICS Base has not been released yet. +### Use waitable timers on Microsoft Windows + +The `epicsEventWaitWithTimeout()` and `epicsThreadSleep()` functions have +been changed to use waitable timers. On Windows 10 version 1803 or higher +they will use high resolution timers for more consistent timing. + +See [this Google Groups thread](https://groups.google.com/a/chromium.org/g/scheduler-dev/c/0GlSPYreJeY) +for a comparison of the performance of different timers. + +### Build target for documentation + +The build target `inc` now works again after a very long hiatus. It now +generates and installs just the dbd, header and html files, without compiling +any C/C++ code. This can be used to speed up CI jobs that only generate +documentation. + +### Bug fixes + +- The error status returned by a record support's `special()` method is now propagated out of the `dbPut()` routine again (broken since 3.15.0). +- [gh: #80](https://github.com/epics-base/epics-base/issues/80), VS-2015 and +later have working strtod() +- [lp: #1776141](https://bugs.launchpad.net/epics-base/+bug/1776141), Catch +buffer overflow from long link strings +- [lp: #1899697](https://bugs.launchpad.net/epics-base/+bug/1899697), Records +in wrong PHAS order + ### Change to the `junitfiles` self-test build target The names of the generated junit xml test output files have been changed from `.xml` to `-results.xml`, to allow better distinction from other xml files. (I.e., for easy wildcard matching.) -### Use waitable timers on Microsoft Windows +### Fixes and code cleanups -The `epicsEventWaitWithTimeout` and `epicsThreadSleep` functions have -been changed to use waitable timers. On Windows 10 version 1803 or higher -they will use high resolution timers for more consistent timing. - -See https://groups.google.com/a/chromium.org/g/scheduler-dev/c/0GlSPYreJeY -for a comparison of the performance of different timers. +Issues reported by various static code checkers. ## Changes made between 3.15.7 and 3.15.8 From 3be67aca3c38b154324cd2d71d6dcbedf15ebf9f Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sun, 27 Jun 2021 12:18:45 -0500 Subject: [PATCH 021/169] Releasing R3.15.9 --- configure/CONFIG_BASE_VERSION | 8 ++++---- documentation/RELEASE_NOTES.md | 6 +----- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/configure/CONFIG_BASE_VERSION b/configure/CONFIG_BASE_VERSION index f7141a82e..667b9f514 100644 --- a/configure/CONFIG_BASE_VERSION +++ b/configure/CONFIG_BASE_VERSION @@ -27,19 +27,19 @@ EPICS_VERSION = 3 EPICS_REVISION = 15 # EPICS_MODIFICATION must be a number >=0 and <256 -EPICS_MODIFICATION = 8 +EPICS_MODIFICATION = 9 # EPICS_PATCH_LEVEL must be a number (win32 resource file requirement) # Not included if zero -EPICS_PATCH_LEVEL = 1 +EPICS_PATCH_LEVEL = 0 # This will end in -DEV between official releases -EPICS_DEV_SNAPSHOT=-DEV +#EPICS_DEV_SNAPSHOT=-DEV #EPICS_DEV_SNAPSHOT=-pre1 #EPICS_DEV_SNAPSHOT=-pre1-DEV #EPICS_DEV_SNAPSHOT=-rc1 #EPICS_DEV_SNAPSHOT=-rc1-DEV -#EPICS_DEV_SNAPSHOT= +EPICS_DEV_SNAPSHOT= # No changes should be needed below here diff --git a/documentation/RELEASE_NOTES.md b/documentation/RELEASE_NOTES.md index befae922b..fa180b685 100644 --- a/documentation/RELEASE_NOTES.md +++ b/documentation/RELEASE_NOTES.md @@ -1,10 +1,6 @@ # EPICS Base Release 3.15.9 -This version of EPICS Base has not been released yet. - -## Changes made on the 3.15 branch since 3.15.8 - - +## Changes made between 3.15.8 and 3.15.9 ### Use waitable timers on Microsoft Windows From 67fcd656568649b81d5a806910deb024630b8611 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sun, 27 Jun 2021 12:20:41 -0500 Subject: [PATCH 022/169] Update versions after tagging --- configure/CONFIG_BASE_VERSION | 10 +++------- documentation/RELEASE_NOTES.md | 6 ++++++ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/configure/CONFIG_BASE_VERSION b/configure/CONFIG_BASE_VERSION index 667b9f514..35911c310 100644 --- a/configure/CONFIG_BASE_VERSION +++ b/configure/CONFIG_BASE_VERSION @@ -31,15 +31,11 @@ EPICS_MODIFICATION = 9 # EPICS_PATCH_LEVEL must be a number (win32 resource file requirement) # Not included if zero -EPICS_PATCH_LEVEL = 0 +EPICS_PATCH_LEVEL = 1 # This will end in -DEV between official releases -#EPICS_DEV_SNAPSHOT=-DEV -#EPICS_DEV_SNAPSHOT=-pre1 -#EPICS_DEV_SNAPSHOT=-pre1-DEV -#EPICS_DEV_SNAPSHOT=-rc1 -#EPICS_DEV_SNAPSHOT=-rc1-DEV -EPICS_DEV_SNAPSHOT= +EPICS_DEV_SNAPSHOT=-DEV +#EPICS_DEV_SNAPSHOT= # No changes should be needed below here diff --git a/documentation/RELEASE_NOTES.md b/documentation/RELEASE_NOTES.md index fa180b685..948a17c21 100644 --- a/documentation/RELEASE_NOTES.md +++ b/documentation/RELEASE_NOTES.md @@ -1,5 +1,11 @@ # EPICS Base Release 3.15.9 +This version of EPICS Base has not been released yet. + +## Changes made on the 3.15 branch since 3.15.9 + + + ## Changes made between 3.15.8 and 3.15.9 ### Use waitable timers on Microsoft Windows From f2ac69604d04d2145306ab110dc9c9af3ac1a30d Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Tue, 29 Jun 2021 21:55:54 -0500 Subject: [PATCH 023/169] Fix typos in Release Notes --- documentation/RELEASE_NOTES.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/RELEASE_NOTES.md b/documentation/RELEASE_NOTES.md index 193dd962c..1d2979ad9 100644 --- a/documentation/RELEASE_NOTES.md +++ b/documentation/RELEASE_NOTES.md @@ -42,7 +42,7 @@ running on RTEMS 5: - RTEMS-beagleboneblack - RTEMS-pc686 - RTEMS-qoriq_e500 (MVME2500) -- RTEMS-xilinx-zynq-a9_qemu +- RTEMS-xilinx_zynq_a9_qemu - RTEMS-xilinx_zynq_zedboard The EPICS support for RTEMS 4 has always relied on RTEMS-specific @@ -215,7 +215,7 @@ that the variables referenced by output pointers are initialized. ```c #ifndef HAS_ALARM_MESSAGE -# recGblSetSevrMsg(REC, STAT, SEVR, ...) recGblSetSevr(REC, STAT, SEVR) +# define recGblSetSevrMsg(REC, STAT, SEVR, ...) recGblSetSevr(REC, STAT, SEVR) #endif #ifndef dbGetAlarmMsg # define dbGetAlarmMsg(LINK, STAT, SEVR, BUF, BUFLEN) dbGetAlarm(LINK, STAT, SEVR) From dff72029f1b6430740a951630647260fe8fbed75 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Tue, 29 Jun 2021 21:57:42 -0500 Subject: [PATCH 024/169] Fix typos in internal build variable names --- configure/CONFIG.gnuCommon | 1 - configure/CONFIG_COMMON | 4 ++-- configure/os/CONFIG.Common.RTEMS | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/configure/CONFIG.gnuCommon b/configure/CONFIG.gnuCommon index 355614e1a..d3a9a41cf 100644 --- a/configure/CONFIG.gnuCommon +++ b/configure/CONFIG.gnuCommon @@ -50,7 +50,6 @@ CODE_LDFLAGS += $(ASAN_LDFLAGS_$(ENABLE_ASAN)) PIPE_CFLAGS_YES_YES = -pipe PIPE_CFLAGS = $(PIPE_CFLAGS_$(GCC_PIPE)_$(GNU)) -PIPE_CXXFLAGS = $(PIPE_CFLAGS) STATIC_LDFLAGS_YES = -static STATIC_LDFLAGS_NO = diff --git a/configure/CONFIG_COMMON b/configure/CONFIG_COMMON index 8bbc4a624..316f971d7 100644 --- a/configure/CONFIG_COMMON +++ b/configure/CONFIG_COMMON @@ -260,7 +260,7 @@ OPT_CXXFLAGS = $(OPT_CXXFLAGS_$($(BUILD_CLASS)_OPT)) # Static build flags STATIC_CFLAGS = $(STATIC_CFLAGS_$(STATIC_BUILD)) -STATIC_CXXCFLAGS = $(STATIC_CXXFLAGS_$(STATIC_BUILD)) +STATIC_CXXFLAGS = $(STATIC_CXXFLAGS_$(STATIC_BUILD)) STATIC_LDFLAGS = $(STATIC_LDFLAGS_$(STATIC_BUILD)) STATIC_LDLIBS = $(STATIC_LDLIBS_$(STATIC_BUILD)) @@ -297,7 +297,7 @@ CFLAGS = $($(BUILD_CLASS)_CFLAGS) $(POSIX_CFLAGS) $(OPT_CFLAGS)\ CXXFLAGS = $($(BUILD_CLASS)_CXXFLAGS) $(POSIX_CXXFLAGS) $(OPT_CXXFLAGS)\ $(DEBUG_CXXFLAGS) $(PIPE_CFLAGS) $(WARN_CXXFLAGS) $(TARGET_CXXFLAGS)\ $(USR_CXXFLAGS) $(CMD_CXXFLAGS) $(ARCH_DEP_CXXFLAGS) $(CODE_CXXFLAGS)\ - $(STATIC_CXXCFLAGS) $(OP_SYS_CXXFLAGS) $(LIBRARY_SRC_CFLAGS) + $(STATIC_CXXFLAGS) $(OP_SYS_CXXFLAGS) $(LIBRARY_SRC_CFLAGS) LDFLAGS = $(OPT_LDFLAGS) $(TARGET_LDFLAGS) $(USR_LDFLAGS) $(CMD_LDFLAGS)\ $(POSIX_LDFLAGS) $(ARCH_DEP_LDFLAGS) $(DEBUG_LDFLAGS) $(OP_SYS_LDFLAGS)\ diff --git a/configure/os/CONFIG.Common.RTEMS b/configure/os/CONFIG.Common.RTEMS index b19b2071f..b7a42d137 100644 --- a/configure/os/CONFIG.Common.RTEMS +++ b/configure/os/CONFIG.Common.RTEMS @@ -63,7 +63,7 @@ CFLAGS = $($(BUILD_CLASS)_CFLAGS) $(POSIX_CFLAGS) $(OPT_CFLAGS)\ CXXFLAGS = $($(BUILD_CLASS)_CXXFLAGS) $(POSIX_CXXFLAGS) $(OPT_CXXFLAGS)\ $(DEBUG_CXXFLAGS) $(PIPE_CFLAGS) $(WARN_CXXFLAGS) $(TARGET_CXXFLAGS)\ $(USR_CXXFLAGS) $(CMD_CXXFLAGS) $(ARCH_DEP_CXXFLAGS) $(CODE_CXXFLAGS)\ - $(STATIC_CXXCFLAGS) $(OP_SYS_CXXFLAGS) $(LIBRARY_SRC_CFLAGS) + $(STATIC_CXXFLAGS) $(OP_SYS_CXXFLAGS) $(LIBRARY_SRC_CFLAGS) LDFLAGS = $(OPT_LDFLAGS) $(TARGET_LDFLAGS) $(USR_LDFLAGS) $(CMD_LDFLAGS)\ $(POSIX_LDFLAGS) $(ARCH_DEP_LDFLAGS) $(DEBUG_LDFLAGS) $(OP_SYS_LDFLAGS)\ From 7ca25d3c888c56844877d194d91f1f47b69f323c Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Tue, 29 Jun 2021 21:59:57 -0500 Subject: [PATCH 025/169] Check module set EPICS_BASE in a RELEASE file --- configure/CONFIG | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/configure/CONFIG b/configure/CONFIG index 1c1242858..df454b29b 100644 --- a/configure/CONFIG +++ b/configure/CONFIG @@ -16,6 +16,10 @@ ifneq ($(wildcard $(TOP)/configure/CONFIG_BASE_VERSION),) CONFIG = $(TOP)/configure BASE_TOP=YES else + ifneq ($(origin EPICS_BASE),file) + # Essential for the EPICS build system, see convertRelease.pl + $(error EPICS_BASE must be set in a configure/RELEASE file) + endif CONFIG ?= $(EPICS_BASE)/configure endif From 5feb18b0b0a5957ba0a504a4524c9295aadaf226 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Wed, 30 Jun 2021 10:10:36 -0700 Subject: [PATCH 026/169] update PVD/PVA modules --- modules/pvAccess | 2 +- modules/pva2pva | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/pvAccess b/modules/pvAccess index e1c1a4bc1..3e6e6ae74 160000 --- a/modules/pvAccess +++ b/modules/pvAccess @@ -1 +1 @@ -Subproject commit e1c1a4bc1bad6933e57b199d58f74468401218b3 +Subproject commit 3e6e6ae74bf9e21cf36dcbd2165888560d35d82b diff --git a/modules/pva2pva b/modules/pva2pva index ad8b77e19..466d41ebb 160000 --- a/modules/pva2pva +++ b/modules/pva2pva @@ -1 +1 @@ -Subproject commit ad8b77e19f7e2ae50c48b5d871bdbe7b0ee23b61 +Subproject commit 466d41ebb95a163133e07150d7841c03abfebf58 From e2d3b9a246d43495b5acaf3276d32de66b0db9b1 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 28 Jun 2021 22:09:59 -0700 Subject: [PATCH 027/169] osiSockTest: ignore large messages on WIN32 --- modules/libcom/src/osi/os/Darwin/osdSock.h | 1 + modules/libcom/src/osi/os/Linux/osdSock.h | 1 + modules/libcom/src/osi/os/RTEMS-posix/osdSock.h | 1 + modules/libcom/src/osi/os/RTEMS-score/osdSock.h | 1 + modules/libcom/src/osi/os/WIN32/osdSock.h | 1 + modules/libcom/src/osi/os/cygwin32/osdSock.h | 1 + modules/libcom/src/osi/os/freebsd/osdSock.h | 1 + modules/libcom/src/osi/os/iOS/osdSock.h | 1 + modules/libcom/src/osi/os/solaris/osdSock.h | 1 + modules/libcom/src/osi/os/vxWorks/osdSock.h | 1 + modules/libcom/test/osiSockTest.c | 3 +++ 11 files changed, 13 insertions(+) diff --git a/modules/libcom/src/osi/os/Darwin/osdSock.h b/modules/libcom/src/osi/os/Darwin/osdSock.h index 5d266e326..e9350420f 100644 --- a/modules/libcom/src/osi/os/Darwin/osdSock.h +++ b/modules/libcom/src/osi/os/Darwin/osdSock.h @@ -56,6 +56,7 @@ typedef unsigned char osiSockOptMcastTTL_t; #define SOCK_SHUTDOWN ESHUTDOWN #define SOCK_ENOTSOCK ENOTSOCK #define SOCK_EBADF EBADF +#define SOCK_EMSGSIZE EMSGSIZE #ifndef SHUT_RD #define SHUT_RD 0 diff --git a/modules/libcom/src/osi/os/Linux/osdSock.h b/modules/libcom/src/osi/os/Linux/osdSock.h index 4a92be720..3a604c88e 100644 --- a/modules/libcom/src/osi/os/Linux/osdSock.h +++ b/modules/libcom/src/osi/os/Linux/osdSock.h @@ -63,6 +63,7 @@ typedef int osiSockOptMcastTTL_t; #define SOCK_SHUTDOWN ESHUTDOWN #define SOCK_ENOTSOCK ENOTSOCK #define SOCK_EBADF EBADF +#define SOCK_EMSGSIZE EMSGSIZE #ifndef SHUT_RD # define SHUT_RD 0 diff --git a/modules/libcom/src/osi/os/RTEMS-posix/osdSock.h b/modules/libcom/src/osi/os/RTEMS-posix/osdSock.h index 5d765b3a7..f0a57c268 100644 --- a/modules/libcom/src/osi/os/RTEMS-posix/osdSock.h +++ b/modules/libcom/src/osi/os/RTEMS-posix/osdSock.h @@ -58,6 +58,7 @@ typedef char osiSockOptMcastTTL_t; #define SOCK_SHUTDOWN ESHUTDOWN #define SOCK_ENOTSOCK ENOTSOCK #define SOCK_EBADF EBADF +#define SOCK_EMSGSIZE EMSGSIZE #ifndef SHUT_RD # define SHUT_RD 0 diff --git a/modules/libcom/src/osi/os/RTEMS-score/osdSock.h b/modules/libcom/src/osi/os/RTEMS-score/osdSock.h index 3090234af..ea765ece8 100644 --- a/modules/libcom/src/osi/os/RTEMS-score/osdSock.h +++ b/modules/libcom/src/osi/os/RTEMS-score/osdSock.h @@ -71,6 +71,7 @@ typedef unsigned char osiSockOptMcastTTL_t; #define SOCK_SHUTDOWN EPIPE #define SOCK_ENOTSOCK ENOTSOCK #define SOCK_EBADF EBADF +#define SOCK_EMSGSIZE EMSGSIZE #include #include diff --git a/modules/libcom/src/osi/os/WIN32/osdSock.h b/modules/libcom/src/osi/os/WIN32/osdSock.h index 87b0a880e..9eab6e76a 100644 --- a/modules/libcom/src/osi/os/WIN32/osdSock.h +++ b/modules/libcom/src/osi/os/WIN32/osdSock.h @@ -66,6 +66,7 @@ typedef DWORD osiSockOptMcastTTL_t; #define SOCK_SHUTDOWN WSAESHUTDOWN #define SOCK_ENOTSOCK WSAENOTSOCK #define SOCK_EBADF WSAENOTSOCK +#define SOCK_EMSGSIZE WSAEMSGSIZE /* * Under WIN32, FD_SETSIZE is the max. number of sockets, diff --git a/modules/libcom/src/osi/os/cygwin32/osdSock.h b/modules/libcom/src/osi/os/cygwin32/osdSock.h index 1d9aa818b..a725a7fe7 100644 --- a/modules/libcom/src/osi/os/cygwin32/osdSock.h +++ b/modules/libcom/src/osi/os/cygwin32/osdSock.h @@ -71,6 +71,7 @@ typedef int osiSockOptMcastTTL_t; #define SOCK_SHUTDOWN ESHUTDOWN #define SOCK_ENOTSOCK ENOTSOCK #define SOCK_EBADF EBADF +#define SOCK_EMSGSIZE EMSGSIZE #define ifreq_size(pifreq) (sizeof(pifreq->ifr_name)) diff --git a/modules/libcom/src/osi/os/freebsd/osdSock.h b/modules/libcom/src/osi/os/freebsd/osdSock.h index f6800e44f..8ebf37db8 100644 --- a/modules/libcom/src/osi/os/freebsd/osdSock.h +++ b/modules/libcom/src/osi/os/freebsd/osdSock.h @@ -61,6 +61,7 @@ typedef unsigned char osiSockOptMcastTTL_t; #define SOCK_SHUTDOWN ESHUTDOWN #define SOCK_ENOTSOCK ENOTSOCK #define SOCK_EBADF EBADF +#define SOCK_EMSGSIZE EMSGSIZE #ifndef SHUT_RD # define SHUT_RD 0 diff --git a/modules/libcom/src/osi/os/iOS/osdSock.h b/modules/libcom/src/osi/os/iOS/osdSock.h index c2aeabd67..34ee13d73 100644 --- a/modules/libcom/src/osi/os/iOS/osdSock.h +++ b/modules/libcom/src/osi/os/iOS/osdSock.h @@ -57,6 +57,7 @@ typedef unsigned char osiSockOptMcastTTL_t; #define SOCK_SHUTDOWN ESHUTDOWN #define SOCK_ENOTSOCK ENOTSOCK #define SOCK_EBADF EBADF +#define SOCK_EMSGSIZE EMSGSIZE #ifndef SHUT_RD #define SHUT_RD 0 diff --git a/modules/libcom/src/osi/os/solaris/osdSock.h b/modules/libcom/src/osi/os/solaris/osdSock.h index e125ac0c6..9cfabdf0a 100644 --- a/modules/libcom/src/osi/os/solaris/osdSock.h +++ b/modules/libcom/src/osi/os/solaris/osdSock.h @@ -69,6 +69,7 @@ typedef unsigned char osiSockOptMcastTTL_t; #define SOCK_SHUTDOWN ESHUTDOWN #define SOCK_ENOTSOCK ENOTSOCK #define SOCK_EBADF EBADF +#define SOCK_EMSGSIZE EMSGSIZE #ifndef SHUT_RD # define SHUT_RD 0 diff --git a/modules/libcom/src/osi/os/vxWorks/osdSock.h b/modules/libcom/src/osi/os/vxWorks/osdSock.h index 12bd8724e..8787eacbf 100644 --- a/modules/libcom/src/osi/os/vxWorks/osdSock.h +++ b/modules/libcom/src/osi/os/vxWorks/osdSock.h @@ -90,6 +90,7 @@ typedef char osiSockOptMcastTTL_t; #define SOCK_SHUTDOWN ESHUTDOWN #define SOCK_ENOTSOCK ENOTSOCK #define SOCK_EBADF EBADF +#define SOCK_EMSGSIZE EMSGSIZE #ifndef INADDR_LOOPBACK #define INADDR_LOOPBACK 0x7F000001 diff --git a/modules/libcom/test/osiSockTest.c b/modules/libcom/test/osiSockTest.c index f2adb41cd..284f828dc 100644 --- a/modules/libcom/test/osiSockTest.c +++ b/modules/libcom/test/osiSockTest.c @@ -243,6 +243,9 @@ void udpSockFanoutTestRx(void* raw) buf.bytes[sizeof(buf.bytes)-1] = '\0'; if(n<0) { + if(SOCKERRNO==SOCK_EMSGSIZE || SOCKERRNO==SOCK_EINTR) + continue; + testDiag("recvfrom error (%d)", (int)SOCKERRNO); break; } else if((n==sizeof(buf.bytes)) && buf.msg.cmd==htons(6) && buf.msg.size==htons(16) From 8e7d3e9216885ae56b5135aa158ae77059374e3a Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Wed, 30 Jun 2021 10:42:59 -0700 Subject: [PATCH 028/169] re-sync initHookName() with initHookState enum --- modules/libcom/src/iocsh/initHooks.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/modules/libcom/src/iocsh/initHooks.c b/modules/libcom/src/iocsh/initHooks.c index 922bb1d4d..70572934d 100644 --- a/modules/libcom/src/iocsh/initHooks.c +++ b/modules/libcom/src/iocsh/initHooks.c @@ -120,14 +120,25 @@ const char *initHookName(int state) "initHookAfterInitialProcess", "initHookAfterCaServerInit", "initHookAfterIocBuilt", + "initHookAtIocRun", "initHookAfterDatabaseRunning", "initHookAfterCaServerRunning", "initHookAfterIocRunning", + "initHookAtIocPause", "initHookAfterCaServerPaused", "initHookAfterDatabasePaused", "initHookAfterIocPaused", + + "initHookAtShutdown", + "initHookAfterCloseLinks", + "initHookAfterStopScan", + "initHookAfterStopCallback", + "initHookAfterStopLinks", + "initHookBeforeFree", + "initHookAfterShutdown", + "initHookAfterInterruptAccept", "initHookAtEnd" }; From 710c50b5ed957f2fcfdc80bd8426dac58565710e Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Wed, 30 Jun 2021 10:39:58 -0700 Subject: [PATCH 029/169] add initHookTest --- modules/libcom/test/Makefile | 5 ++++ modules/libcom/test/epicsRunLibComTests.c | 2 ++ modules/libcom/test/initHookTest.c | 30 +++++++++++++++++++++++ 3 files changed, 37 insertions(+) create mode 100644 modules/libcom/test/initHookTest.c diff --git a/modules/libcom/test/Makefile b/modules/libcom/test/Makefile index ef8605002..809ae7338 100644 --- a/modules/libcom/test/Makefile +++ b/modules/libcom/test/Makefile @@ -144,6 +144,11 @@ epicsThreadPoolTest_SRCS += epicsThreadPoolTest.c testHarness_SRCS += epicsThreadPoolTest.c TESTS += epicsThreadPoolTest +TESTPROD_HOST += initHookTest +initHookTest_SRCS += initHookTest.c +testHarness_SRCS += initHookTest.c +TESTS += initHookTest + TESTPROD_HOST += epicsExitTest epicsExitTest_SRCS += epicsExitTest.c testHarness_SRCS += epicsExitTest.c diff --git a/modules/libcom/test/epicsRunLibComTests.c b/modules/libcom/test/epicsRunLibComTests.c index d469fcf83..54c46a68e 100644 --- a/modules/libcom/test/epicsRunLibComTests.c +++ b/modules/libcom/test/epicsRunLibComTests.c @@ -50,6 +50,7 @@ int epicsTimeZoneTest(void); #endif int epicsTypesTest(void); int epicsInlineTest(void); +int initHookTest(void); int ipAddrToAsciiTest(void); int macDefExpandTest(void); int macLibTest(void); @@ -105,6 +106,7 @@ void epicsRunLibComTests(void) runTest(epicsTimeZoneTest); #endif runTest(epicsTypesTest); + runTest(initHookTest); runTest(ipAddrToAsciiTest); runTest(macDefExpandTest); runTest(macLibTest); diff --git a/modules/libcom/test/initHookTest.c b/modules/libcom/test/initHookTest.c new file mode 100644 index 000000000..688fea37b --- /dev/null +++ b/modules/libcom/test/initHookTest.c @@ -0,0 +1,30 @@ +/*************************************************************************\ +* Copyright (c) 2021 Michael Davidsaver +* SPDX-License-Identifier: EPICS +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include +#include + +#include +#include + +#include + +static +void testHookNames(void) +{ + const char* s; + + s = initHookName(initHookAtEnd); + testOk(strcmp(s, "initHookAtEnd")==0, "'%s' == 'initHookAtEnd'", s); +} + +MAIN(initHookTest) +{ + testPlan(1); + testHookNames(); + return testDone(); +} From ba3550c287db547af5ab82c5281bc63d808d6a62 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 2 Jul 2021 16:28:22 -0500 Subject: [PATCH 030/169] Update submodules for release --- modules/normativeTypes | 2 +- modules/pvData | 2 +- modules/pvDatabase | 2 +- modules/pvaClient | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/normativeTypes b/modules/normativeTypes index 7a2d264f2..1250a3c23 160000 --- a/modules/normativeTypes +++ b/modules/normativeTypes @@ -1 +1 @@ -Subproject commit 7a2d264f2cb107bfd10adb23bc2b73d8323a79e4 +Subproject commit 1250a3c236f0aa92e0b5bd73647fd71d8a09360d diff --git a/modules/pvData b/modules/pvData index d3b4976ea..b1c830387 160000 --- a/modules/pvData +++ b/modules/pvData @@ -1 +1 @@ -Subproject commit d3b4976ea2b0d78075511f14d7f7bf9620dd4d14 +Subproject commit b1c8303870a04f1c3ee5a01a84aad2b2596e918c diff --git a/modules/pvDatabase b/modules/pvDatabase index 93a259cbd..8cac3975c 160000 --- a/modules/pvDatabase +++ b/modules/pvDatabase @@ -1 +1 @@ -Subproject commit 93a259cbde56668c1bbe495b15cc3ede8b42ce30 +Subproject commit 8cac3975cce67828a19e600fee85dd28df52fe2c diff --git a/modules/pvaClient b/modules/pvaClient index efb263190..a34876e36 160000 --- a/modules/pvaClient +++ b/modules/pvaClient @@ -1 +1 @@ -Subproject commit efb2631905cd61cc06041f5aac5be9017383a004 +Subproject commit a34876e36a56c9de9b172d6a83a9439bb330783d From 67bf1a72e77efd62d07fdca919a3c738a6258283 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sat, 3 Jul 2021 20:14:04 -0500 Subject: [PATCH 031/169] Substitute version in @since UNRELEASED annotations --- modules/database/src/ioc/db/dbLink.h | 8 ++++---- modules/database/src/ioc/db/recGbl.h | 2 +- modules/libcom/src/misc/epicsString.h | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/database/src/ioc/db/dbLink.h b/modules/database/src/ioc/db/dbLink.h index 1933952b9..d1e454485 100644 --- a/modules/database/src/ioc/db/dbLink.h +++ b/modules/database/src/ioc/db/dbLink.h @@ -371,7 +371,7 @@ typedef struct lset { * Implementations must write a trailing nil to msgbuf whenever * @code msgbuf!=NULL && msgbuflen>0 @endcode . * - * @since UNRELEASED + * @since 7.0.6 */ long (*getAlarmMsg)(const struct link *plink, epicsEnum16 *status, epicsEnum16 *severity, char *msgbuf, size_t msgbuflen); @@ -381,7 +381,7 @@ typedef struct lset { * Equivalent of getTimeStamp() and also copy out time tag. * ptag may be NULL. * - * @since Added after UNRELEASED + * @since Added after 7.0.6 */ long (*getTimeStampTag)(const struct link *plink, epicsTimeStamp *pstamp, epicsUTag *ptag); } lset; @@ -428,14 +428,14 @@ DBCORE_API long dbGetAlarm(const struct link *plink, epicsEnum16 *status, /** Get link alarm and message string. * To ensure the complete message string is copied, ensure @code msgbuflen >= sizeof (dbCommon::amsg) @endcode . * A trailing nil will be added whenever @code msgbuflen > 0 @endcode . - * @since UNRELEASED + * @since 7.0.6 */ DBCORE_API long dbGetAlarmMsg(const struct link *plink, epicsEnum16 *status, epicsEnum16 *severity, char *msgbuf, size_t msgbuflen); #define dbGetAlarmMsg(LINK, STAT, SEVR, BUF, BUFLEN) dbGetAlarmMsg(LINK, STAT, SEVR, BUF, BUFLEN) DBCORE_API long dbGetTimeStamp(const struct link *plink, epicsTimeStamp *pstamp); -/** @since UNRELEASED */ +/** @since 7.0.6 */ DBCORE_API long dbGetTimeStampTag(const struct link *plink, epicsTimeStamp *pstamp, epicsUTag *ptag); #define dbGetTimeStampTag(LINK, STAMP, TAG) dbGetTimeStampTag(LINK, STAMP, TAG) diff --git a/modules/database/src/ioc/db/recGbl.h b/modules/database/src/ioc/db/recGbl.h index 269777ec6..e362b8bd4 100644 --- a/modules/database/src/ioc/db/recGbl.h +++ b/modules/database/src/ioc/db/recGbl.h @@ -29,7 +29,7 @@ extern "C" { * * Covers addition of dbCommon::amsg, recGblSetSevrMsg(), lset::getAlarmMsg() * - * @since UNRELEASED + * @since 7.0.6 */ #define HAS_ALARM_MESSAGE 1 diff --git a/modules/libcom/src/misc/epicsString.h b/modules/libcom/src/misc/epicsString.h index 5bb8c87c1..24f1efb62 100644 --- a/modules/libcom/src/misc/epicsString.h +++ b/modules/libcom/src/misc/epicsString.h @@ -56,7 +56,7 @@ LIBCOM_API int epicsStrGlobMatch(const char *str, const char *pattern); * * @returns 1 if the first len characters of str match the pattern, 0 if not. * - * @since UNRELEASED + * @since 7.0.6 */ LIBCOM_API int epicsStrnGlobMatch(const char *str, size_t len, const char *pattern); From 591244599106c73e3a2c6bcb2ec9de3cc963c125 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sat, 3 Jul 2021 20:46:58 -0500 Subject: [PATCH 032/169] Set version numbers for release --- configure/CONFIG_BASE_VERSION | 6 +++--- configure/CONFIG_CA_VERSION | 6 +++--- configure/CONFIG_DATABASE_VERSION | 6 +++--- configure/CONFIG_LIBCOM_VERSION | 6 +++--- documentation/RELEASE_NOTES.md | 14 +++++--------- 5 files changed, 17 insertions(+), 21 deletions(-) diff --git a/configure/CONFIG_BASE_VERSION b/configure/CONFIG_BASE_VERSION index b0c57b763..efa0afee7 100644 --- a/configure/CONFIG_BASE_VERSION +++ b/configure/CONFIG_BASE_VERSION @@ -48,15 +48,15 @@ EPICS_VERSION = 7 EPICS_REVISION = 0 # EPICS_MODIFICATION must be a number >=0 and <256 -EPICS_MODIFICATION = 5 +EPICS_MODIFICATION = 6 # EPICS_PATCH_LEVEL must be a number (win32 resource file requirement) # Not included in the official EPICS version number if zero -EPICS_PATCH_LEVEL = 1 +EPICS_PATCH_LEVEL = 0 # Immediately after an official release the EPICS_PATCH_LEVEL is incremented # and the -DEV suffix is added (similar to the Maven -SNAPSHOT versions) -EPICS_DEV_SNAPSHOT=-DEV +EPICS_DEV_SNAPSHOT= # No changes should be needed below here diff --git a/configure/CONFIG_CA_VERSION b/configure/CONFIG_CA_VERSION index af570b0e7..ce95a0ad3 100644 --- a/configure/CONFIG_CA_VERSION +++ b/configure/CONFIG_CA_VERSION @@ -1,12 +1,12 @@ # Version number for the Channel Access API and shared library EPICS_CA_MAJOR_VERSION = 4 -EPICS_CA_MINOR_VERSION = 13 -EPICS_CA_MAINTENANCE_VERSION = 9 +EPICS_CA_MINOR_VERSION = 14 +EPICS_CA_MAINTENANCE_VERSION = 0 # Development flag, set to zero for release versions -EPICS_CA_DEVELOPMENT_FLAG = 1 +EPICS_CA_DEVELOPMENT_FLAG = 0 # Immediately after a release the MAINTENANCE_VERSION # will be incremented and the DEVELOPMENT_FLAG set to 1 diff --git a/configure/CONFIG_DATABASE_VERSION b/configure/CONFIG_DATABASE_VERSION index 2205a5386..348a69b11 100644 --- a/configure/CONFIG_DATABASE_VERSION +++ b/configure/CONFIG_DATABASE_VERSION @@ -1,12 +1,12 @@ # Version number for the database APIs and shared library EPICS_DATABASE_MAJOR_VERSION = 3 -EPICS_DATABASE_MINOR_VERSION = 19 -EPICS_DATABASE_MAINTENANCE_VERSION = 1 +EPICS_DATABASE_MINOR_VERSION = 20 +EPICS_DATABASE_MAINTENANCE_VERSION = 0 # Development flag, set to zero for release versions -EPICS_DATABASE_DEVELOPMENT_FLAG = 1 +EPICS_DATABASE_DEVELOPMENT_FLAG = 0 # Immediately after a release the MAINTENANCE_VERSION # will be incremented and the DEVELOPMENT_FLAG set to 1 diff --git a/configure/CONFIG_LIBCOM_VERSION b/configure/CONFIG_LIBCOM_VERSION index 96f25ebaa..76a5a5fcf 100644 --- a/configure/CONFIG_LIBCOM_VERSION +++ b/configure/CONFIG_LIBCOM_VERSION @@ -1,12 +1,12 @@ # Version number for the libcom APIs and shared library EPICS_LIBCOM_MAJOR_VERSION = 3 -EPICS_LIBCOM_MINOR_VERSION = 19 -EPICS_LIBCOM_MAINTENANCE_VERSION = 1 +EPICS_LIBCOM_MINOR_VERSION = 20 +EPICS_LIBCOM_MAINTENANCE_VERSION = 0 # Development flag, set to zero for release versions -EPICS_LIBCOM_DEVELOPMENT_FLAG = 1 +EPICS_LIBCOM_DEVELOPMENT_FLAG = 0 # Immediately after a release the MAINTENANCE_VERSION # will be incremented and the DEVELOPMENT_FLAG set to 1 diff --git a/documentation/RELEASE_NOTES.md b/documentation/RELEASE_NOTES.md index 1d2979ad9..c406b6506 100644 --- a/documentation/RELEASE_NOTES.md +++ b/documentation/RELEASE_NOTES.md @@ -2,20 +2,16 @@ These release notes describe changes that have been made since the previous release of this series of EPICS Base. **Note that changes which were merged up -from commits to new releases in an older Base series are not described at the -top of this file but have entries that appear lower down, under the series to -which they were originally committed.** Thus it is important to read more than -just the first section to understand everything that has changed in each -release. +from commits to the 3.15 branch are not described at the top of this file but +lower down, under the 3.15 release to which they were originally committed.** +Thus it is important to read more than just the first section to understand +everything that has changed in each release. The PVA submodules each have their own individual sets of release notes which should also be read to understand what has changed since earlier releases. -**This version of EPICS has not been released yet.** -## Changes made on the 7.0 branch since 7.0.5 - - +## EPICS Release 7.0.6 ### Support for obsolete architectures removed From f5cb3cf8f60948d45263f24382c8d949155cdda3 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sat, 3 Jul 2021 20:56:32 -0500 Subject: [PATCH 033/169] Update version numbers after tagging --- configure/CONFIG_BASE_VERSION | 4 +- configure/CONFIG_CA_VERSION | 4 +- configure/CONFIG_DATABASE_VERSION | 4 +- configure/CONFIG_LIBCOM_VERSION | 4 +- documentation/RELEASE_NOTES.md | 6 + documentation/ReleaseChecklist.html | 177 ++++++++-------------------- 6 files changed, 60 insertions(+), 139 deletions(-) diff --git a/configure/CONFIG_BASE_VERSION b/configure/CONFIG_BASE_VERSION index efa0afee7..fda0f13f3 100644 --- a/configure/CONFIG_BASE_VERSION +++ b/configure/CONFIG_BASE_VERSION @@ -52,11 +52,11 @@ EPICS_MODIFICATION = 6 # EPICS_PATCH_LEVEL must be a number (win32 resource file requirement) # Not included in the official EPICS version number if zero -EPICS_PATCH_LEVEL = 0 +EPICS_PATCH_LEVEL = 1 # Immediately after an official release the EPICS_PATCH_LEVEL is incremented # and the -DEV suffix is added (similar to the Maven -SNAPSHOT versions) -EPICS_DEV_SNAPSHOT= +EPICS_DEV_SNAPSHOT=-DEV # No changes should be needed below here diff --git a/configure/CONFIG_CA_VERSION b/configure/CONFIG_CA_VERSION index ce95a0ad3..5c3127d4c 100644 --- a/configure/CONFIG_CA_VERSION +++ b/configure/CONFIG_CA_VERSION @@ -2,11 +2,11 @@ EPICS_CA_MAJOR_VERSION = 4 EPICS_CA_MINOR_VERSION = 14 -EPICS_CA_MAINTENANCE_VERSION = 0 +EPICS_CA_MAINTENANCE_VERSION = 1 # Development flag, set to zero for release versions -EPICS_CA_DEVELOPMENT_FLAG = 0 +EPICS_CA_DEVELOPMENT_FLAG = 1 # Immediately after a release the MAINTENANCE_VERSION # will be incremented and the DEVELOPMENT_FLAG set to 1 diff --git a/configure/CONFIG_DATABASE_VERSION b/configure/CONFIG_DATABASE_VERSION index 348a69b11..4e3d22b71 100644 --- a/configure/CONFIG_DATABASE_VERSION +++ b/configure/CONFIG_DATABASE_VERSION @@ -2,11 +2,11 @@ EPICS_DATABASE_MAJOR_VERSION = 3 EPICS_DATABASE_MINOR_VERSION = 20 -EPICS_DATABASE_MAINTENANCE_VERSION = 0 +EPICS_DATABASE_MAINTENANCE_VERSION = 1 # Development flag, set to zero for release versions -EPICS_DATABASE_DEVELOPMENT_FLAG = 0 +EPICS_DATABASE_DEVELOPMENT_FLAG = 1 # Immediately after a release the MAINTENANCE_VERSION # will be incremented and the DEVELOPMENT_FLAG set to 1 diff --git a/configure/CONFIG_LIBCOM_VERSION b/configure/CONFIG_LIBCOM_VERSION index 76a5a5fcf..42d5f086c 100644 --- a/configure/CONFIG_LIBCOM_VERSION +++ b/configure/CONFIG_LIBCOM_VERSION @@ -2,11 +2,11 @@ EPICS_LIBCOM_MAJOR_VERSION = 3 EPICS_LIBCOM_MINOR_VERSION = 20 -EPICS_LIBCOM_MAINTENANCE_VERSION = 0 +EPICS_LIBCOM_MAINTENANCE_VERSION = 1 # Development flag, set to zero for release versions -EPICS_LIBCOM_DEVELOPMENT_FLAG = 0 +EPICS_LIBCOM_DEVELOPMENT_FLAG = 1 # Immediately after a release the MAINTENANCE_VERSION # will be incremented and the DEVELOPMENT_FLAG set to 1 diff --git a/documentation/RELEASE_NOTES.md b/documentation/RELEASE_NOTES.md index c406b6506..df60dff63 100644 --- a/documentation/RELEASE_NOTES.md +++ b/documentation/RELEASE_NOTES.md @@ -10,6 +10,12 @@ everything that has changed in each release. The PVA submodules each have their own individual sets of release notes which should also be read to understand what has changed since earlier releases. +**This version of EPICS has not been released yet.** + +## Changes made on the 7.0 branch since 7.0.6 + + + ## EPICS Release 7.0.6 diff --git a/documentation/ReleaseChecklist.html b/documentation/ReleaseChecklist.html index 036f56a65..05b76ece9 100644 --- a/documentation/ReleaseChecklist.html +++ b/documentation/ReleaseChecklist.html @@ -37,26 +37,11 @@ that should be performed when creating production releases of EPICS Base.

The Release Process

-

Full Process

- -

The version released on the Feature Freeze date is designated the first -pre-release, -pre1. The first release candidate -rc1 is the -first version that has undergone testing by the developers and has shown no -problems that must be fixed before release. New versions should be made at about -2-weekly intervals after the -pre1 release, and designated as either -pre-release or release candidate versions by the Release Manager. Release -candidates are announced to the whole community via the tech-talk mailing list, -pre-releases are announced to to the developers via the core-talk list. After a -release candidate has been available for 2 weeks without any new problems being -reported or major changes having to be committed, the final release can be -made.

- -

Short Process for Patch Releases

- -

The Patch Release date and its scope are agreed upon a few weeks ahead of the -release. If no blocking issues are raised, the release is made by the Release -Manager on or as soon as possible after that date, following the steps below -starting at Release Approval.

+

We used to have one written down here, but we weren't following it very +closely so now the decision to make a new release is taken during the Core +Developers bi-weekly meetings in an informal manner. The steps detailed below +were written to remind Andrew (or anyone else who does the release) about +everything that has to be done since it's so easy to miss steps.

Roles

@@ -65,11 +50,11 @@ starting at Release Approval.

Release Manager ()
Responsible for managing and tagging the release
-
Platform Developers (optional)
+
Platform Developers (informal)
Responsible for individual operating system platforms
Application Developers
Responsible for support modules that depend on EPICS Base.
-
Website Manager (Andrew Johnson)
+
Website Editor (Andrew Johnson)
Responsible for the EPICS website
@@ -111,9 +96,7 @@ starting at Release Approval.

& all developers Ensure that documentation will be updated before the release date:
    -
  • Application Developers Guide
  • Release Notes
  • -
  • Known Problems
  • Other documents
@@ -125,87 +108,9 @@ starting at Release Approval.

  - Website Manager + Release Manager Create a release milestone on Launchpad. If a target release date is - known set "Date Targeted" to the expected release date. Note that - pre-release and release-candidate versions should not get Launchpad - milestones, only the final release. - - - Creating pre-release and release-candidate versions - - - - Release Manager - - Edit and commit changes to the EPICS version number file - configure/CONFIG_BASE_VERSION. - - - - Release Manager - Tag the module in Git, using these tag conventions: -
    -
  • - R7.0.5-pren - — pre-release tag -
  • -
  • - R7.0.5-rcn - — release candidate tag -
  • -
-
- cd base-7.0
- git tag -m 'ANJ: Tagged for 7.0.5-rc1' R7.0.5-rc1 -
- Note that submodules must not be tagged with the version used - for the top-level, they each have their own separate version numbers - that are only tagged at the final release. - - - - Release Manager - Export the tagged version into a tarfile. The make-tar.sh - script generates a gzipped tarfile directly from the tag, excluding the - files and directories that are only used for continuous integration: -
- cd base-7.0
- ./.tools/make-tar.sh R7.0.5-rc1 base-7.0.5-rc1.tar.gz base-7.0.5-rc1/ -
- Create a GPG signature file of the tarfile as follows: -
- gpg --armor --sign --detach-sig base-7.0.5-rc1.tar.gz -
- - - - - Release Manager - Test the tarfile by extracting its contents and building it on at - least one supported platform. - - - - Website Manager - Copy the tarfile and its signature to the Base download area of the - website and add the new files to the website Base download index - page. - - - - Website Manager - Create or update a website subdirectory to hold the release - documentation, and copy in selected files from the base/documentation - and base/html directories of the tarfile. - - - - Website Manager - Create or modify the webpage for the new release with links to the - release documents and tar file. Pre-release and release-candidate - versions should use the page and URL for the final release version - number. + known set "Date Targeted" to the expected release date. Testing @@ -250,11 +155,8 @@ starting at Release Approval.

Release Manager Check that documentation has been updated: @@ -266,9 +168,7 @@ starting at Release Approval.

Release Manager - Obtain a positive Ok to release from all platform developers - once a release candidate version has gone for 2 weeks without any major - new issues being reported. + Obtain a positive Ok to release from developers. Creating the final release version @@ -277,8 +177,8 @@ starting at Release Approval.

Release Manager -

For each external submodule in turn (assuming it has not been tagged - yet):

+

For each external submodule in turn (assuming it has not been + tagged yet):

  1. Check that the module's Release Notes have been updated to cover all changes; add items as necessary, and set the module version @@ -298,7 +198,7 @@ starting at Release Approval.

  2. Tag the module:
    - git tag -m 'ANJ: Tag for EPICS 7.0.5' <module-version> + git tag -m 'ANJ: Tag for EPICS 7.0.6.1' <module-version>
  3. @@ -326,15 +226,24 @@ starting at Release Approval.

-

Commit all the submodule updates to the 7.0 branch.

+

After all submodules complete commit the submodule updates + which were added for each submodule in step 4 above to the 7.0 branch + (don't push). After committing, make sure that the output from + git submodule status --cached only shows the appropriate + version tags in the right-most parenthesized column with no + -n-gxxxxxxx suffix.

Release Manager - Edit the main EPICS Base version file and the built-in module version - files: + +

git grep UNRELEASED and insert the release version to any + doxygen annotations that have a @since UNRELEASED comment. + Commit (don't push).

+

Edit the main EPICS Base version file and the built-in module version + files:

  • configure/CONFIG_BASE_VERSION
  • configure/CONFIG_LIBCOM_VERSION
  • @@ -346,6 +255,9 @@ starting at Release Approval.

    PATCH_LEVEL value should have been incremented after the previous release tag was applied. Set all DEVELOPMENT_FLAG values to 0 and EPICS_DEV_SNAPSHOT to the empty string.

    +

    Edit the headings in the Release Notes to show the appropriate + version number and remove the warning about this being an unreleased + version of EPICS.

    Commit these changes (don't push).

    @@ -355,9 +267,9 @@ starting at Release Approval.

    Tag the epics-base module in Git:
    cd base-7.0
    - git tag -m 'ANJ: Tagged for release' R7.0.5 + git tag -m 'ANJ: Tagged for release' R7.0.6.1
    -

    Don't push these commits or the new tag to the Launchpad repository +

    Don't push anything to the Launchpad repository yet.

    @@ -376,6 +288,9 @@ starting at Release Approval.

    release by incrementing the MAINTENANCE_VERSION or PATCH_LEVEL value in each file. Set all DEVELOPMENT_FLAG values to 1 and EPICS_DEV_SNAPSHOT to "-DEV".

    +

    Set up the headings in the Release Notes for the next release + version number and restore the warning about this being an unreleased + version of EPICS.

    Commit these changes (don't push).

    @@ -387,12 +302,12 @@ starting at Release Approval.

    files and directories that are only used for continuous integration:
    cd base-7.0
    - ./.tools/make-tar.sh R7.0.5 ../base-7.0.5.tar.gz base-7.0.5/ + ./.tools/make-tar.sh R7.0.6.1 ../base-7.0.6.1.tar.gz base-7.0.6.1/
    Create a GPG signature file of the tarfile as follows:
    cd ..
    - gpg --armor --sign --detach-sig base-7.0.5.tar.gz + gpg --armor --sign --detach-sig base-7.0.6.1.tar.gz
    @@ -412,38 +327,38 @@ starting at Release Approval.

    - Release Manager + Website Editor Copy the tarfile and its signature to the Base download area of the website. - Website Manager + Website Editor Update the website subdirectory that holds the release documentation, and copy in the files from the base/documentation directory of the tarfile. - Website Manager + Website Editor Update the webpage for the new release with links to the release documents and tar file. - Website Manager + Website Editor Add the new release tar file to the website Base download index page. - Website Manager + Website Editor Link to the release webpage from other relevent areas of the website - update front page and sidebars. - Website Manager + Website Editor Add an entry to the website News page, linking to the new version webpage. @@ -453,17 +368,17 @@ starting at Release Approval.

    - Website Manager + Website Editor Upload the tar file and its .asc signature file to the epics-controls web-server.
    - scp base-7.0.5.tar.gz base-7.0.5.tar.gz.asc epics-controls:download/base
    + scp base-7.0.6.1.tar.gz base-7.0.6.1.tar.gz.asc epics-controls:download/base
    - Website Manager + Website Editor Follow instructions on Add a page for a new release to create a new release webpage (not @@ -478,7 +393,7 @@ starting at Release Approval.

    - Website Manager + Release Manager Go to the Launchpad milestone for this release. Click the Create release button and add the release date. Put a URL for the release page in the Release notes box, and click the Create release button. Upload From 9363052956fdf63b88f787371ab97c9eedcfd7b3 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sat, 3 Jul 2021 20:56:53 -0500 Subject: [PATCH 034/169] Update submodules after release --- modules/normativeTypes | 2 +- modules/pvAccess | 2 +- modules/pvData | 2 +- modules/pvDatabase | 2 +- modules/pva2pva | 2 +- modules/pvaClient | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/normativeTypes b/modules/normativeTypes index 1250a3c23..7a2d264f2 160000 --- a/modules/normativeTypes +++ b/modules/normativeTypes @@ -1 +1 @@ -Subproject commit 1250a3c236f0aa92e0b5bd73647fd71d8a09360d +Subproject commit 7a2d264f2cb107bfd10adb23bc2b73d8323a79e4 diff --git a/modules/pvAccess b/modules/pvAccess index 3e6e6ae74..a5cae7ad9 160000 --- a/modules/pvAccess +++ b/modules/pvAccess @@ -1 +1 @@ -Subproject commit 3e6e6ae74bf9e21cf36dcbd2165888560d35d82b +Subproject commit a5cae7ad9242d099ee0602cb3061987e1ff95ef4 diff --git a/modules/pvData b/modules/pvData index b1c830387..d3b4976ea 160000 --- a/modules/pvData +++ b/modules/pvData @@ -1 +1 @@ -Subproject commit b1c8303870a04f1c3ee5a01a84aad2b2596e918c +Subproject commit d3b4976ea2b0d78075511f14d7f7bf9620dd4d14 diff --git a/modules/pvDatabase b/modules/pvDatabase index 8cac3975c..0cf706511 160000 --- a/modules/pvDatabase +++ b/modules/pvDatabase @@ -1 +1 @@ -Subproject commit 8cac3975cce67828a19e600fee85dd28df52fe2c +Subproject commit 0cf706511ea4f9f0cf769c1d3a6317d826b35af4 diff --git a/modules/pva2pva b/modules/pva2pva index 466d41ebb..61ec0715b 160000 --- a/modules/pva2pva +++ b/modules/pva2pva @@ -1 +1 @@ -Subproject commit 466d41ebb95a163133e07150d7841c03abfebf58 +Subproject commit 61ec0715be3ce1acb8d9f819bfd89d788dabc21b diff --git a/modules/pvaClient b/modules/pvaClient index a34876e36..8ed07fef9 160000 --- a/modules/pvaClient +++ b/modules/pvaClient @@ -1 +1 @@ -Subproject commit a34876e36a56c9de9b172d6a83a9439bb330783d +Subproject commit 8ed07fef96e41d35d47ab61276e29eb1a81e7fec From f801ca0501b455bee5e12b836bc24ae74b010535 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sat, 3 Jul 2021 21:26:18 -0500 Subject: [PATCH 035/169] Drop version number from README --- documentation/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/README.md b/documentation/README.md index a5310deb4..2d4386aed 100644 --- a/documentation/README.md +++ b/documentation/README.md @@ -1,6 +1,6 @@ # Installation Instructions {#install} -## EPICS Base Release 7.0.5 +## EPICS Base Release 7.0.x ----- From e5aece682e9df5d3bf40ab2dc3439da076a6ecae Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Thu, 15 Jul 2021 07:59:48 -0700 Subject: [PATCH 036/169] ci: replace GHA deprecated ubuntu-16.04 with CentOS/Fedora builds Maintain coverage of older GCC on Linux --- .ci | 2 +- .github/workflows/ci-scripts-build.yml | 128 +++++++++++++++++-------- 2 files changed, 89 insertions(+), 41 deletions(-) diff --git a/.ci b/.ci index d675de24e..75bae77c1 160000 --- a/.ci +++ b/.ci @@ -1 +1 @@ -Subproject commit d675de24e6a2be018f6ff1dc35618c16dd621727 +Subproject commit 75bae77c1d20707a53e0ff57937491f6b8b557ba diff --git a/.github/workflows/ci-scripts-build.yml b/.github/workflows/ci-scripts-build.yml index b9b3a0c12..6aa6eefb4 100644 --- a/.github/workflows/ci-scripts-build.yml +++ b/.github/workflows/ci-scripts-build.yml @@ -34,7 +34,7 @@ env: EPICS_TEST_IMPRECISE_TIMING: YES jobs: - build-base: + native: name: ${{ matrix.name }} runs-on: ${{ matrix.os }} # Set environment variables from matrix parameters @@ -69,10 +69,10 @@ jobs: extra: "CMD_CXXFLAGS=-std=c++11" name: "Ub-20 gcc-9 C++11, static" - - os: ubuntu-16.04 + - os: ubuntu-20.04 cmp: clang configuration: default - name: "Ub-16 clang-9" + name: "Ub-20 clang-10" - os: ubuntu-20.04 cmp: clang @@ -145,35 +145,6 @@ jobs: name: "Ub-20 gcc-9 + RT-4.9" rtems_target: RTEMS-pc386-qemu - - os: ubuntu-16.04 - cmp: gcc-4.8 - utoolchain: "4.8" - configuration: default - name: "Ub-16 gcc-4.8" - - - os: ubuntu-16.04 - cmp: gcc-4.9 - utoolchain: "4.9" - configuration: default - name: "Ub-16 gcc-4.9" - - - os: ubuntu-20.04 - cmp: gcc-8 - utoolchain: "8" - configuration: default - name: "Ub-20 gcc-8" - - - os: ubuntu-20.04 - cmp: gcc-9 - utoolchain: "9" - configuration: default - name: "Ub-20 gcc-9" - - - os: ubuntu-20.04 - cmp: clang - configuration: default - name: "Ub-20 clang-10" - - os: macos-latest cmp: clang configuration: default @@ -210,14 +181,91 @@ jobs: sudo apt-get update sudo apt-get -y install qemu-system-x86 g++-mingw-w64-x86-64 gdb if: runner.os == 'Linux' - - name: "apt-get install ${{ matrix.cmp }}" - run: | - sudo apt-get update - sudo apt-get -y install software-properties-common - sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test - sudo apt-get update - sudo apt-get -y install g++-${{ matrix.utoolchain }} - if: matrix.utoolchain + - name: Prepare and compile dependencies + run: python .ci/cue.py prepare + - name: Build main module + run: python .ci/cue.py build + - name: Run main module tests + run: python .ci/cue.py -T 20M test + - name: Upload tapfiles Artifact + if: ${{ always() }} + uses: actions/upload-artifact@v2 + with: + name: tapfiles ${{ matrix.name }} + path: '**/O.*/*.tap' + if-no-files-found: ignore + - name: Collect and show test results + if: ${{ always() }} + run: python .ci/cue.py -T 5M test-results + + docker: + name: ${{ matrix.name }} + runs-on: ubuntu-latest + container: + image: ${{ matrix.image }} + # Set environment variables from matrix parameters + env: + CMP: ${{ matrix.cmp }} + BCFG: ${{ matrix.configuration }} + EXTRA: ${{ matrix.extra }} + TEST: ${{ matrix.test }} + strategy: + fail-fast: false + matrix: + # Job names also name artifacts, character limitations apply + include: + - name: "CentOS-7" + image: centos:7 + cmp: gcc + configuration: default + + - name: "CentOS-8" + image: centos:8 + cmp: gcc + configuration: default + + - name: "Fedora-33" + image: fedora:33 + cmp: gcc + configuration: default + + - name: "Fedora-latest" + image: fedora:latest + cmp: gcc + configuration: default + + steps: + - name: "Build newer Git" + # actions/checkout@v2 wants git >=2.18 + # centos:7 has 1.8 + if: matrix.image=='centos:7' + run: | + yum -y install curl make gcc curl-devel expat-devel gettext-devel openssl-devel zlib-devel perl-ExtUtils-MakeMaker + curl https://mirrors.edge.kernel.org/pub/software/scm/git/git-2.29.0.tar.gz | tar -xz + cd git-* + make -j2 prefix=/usr/local all + make prefix=/usr/local install + cd .. + rm -rf git-* + type -a git + git --version + - name: "Redhat setup" + run: | + dnfyum() { + dnf -y "$@" || yum -y "$@" + return $? + } + dnfyum install python3 gdb make perl gcc-c++ glibc-devel readline-devel ncurses-devel perl-devel perl-Test-Simple + git --version || dnfyum install git + # rather than just bite the bullet and link python3 -> python, + # people would rather just break all existing scripts... + [ -e /usr/bin/python ] || ln -sf python3 /usr/bin/python + python --version + - uses: actions/checkout@v2 + with: + submodules: true + - name: Automatic core dumper analysis + uses: mdavidsaver/ci-core-dumper@master - name: Prepare and compile dependencies run: python .ci/cue.py prepare - name: Build main module From 967846b95084388f16be9307a99214a495a25ab1 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Sun, 27 Jun 2021 07:23:53 -0700 Subject: [PATCH 037/169] tap files are PRECIOUS --- configure/RULES_BUILD | 2 ++ 1 file changed, 2 insertions(+) diff --git a/configure/RULES_BUILD b/configure/RULES_BUILD index 53297af52..d750548e9 100644 --- a/configure/RULES_BUILD +++ b/configure/RULES_BUILD @@ -383,6 +383,8 @@ endif tapfiles: $(TAPFILES) junitfiles: $(JUNITFILES) +# prevent deletion of partial output from failing tests +.PRECIOUS: $(TAPFILES) $(JUNITFILES) test-results: tap-results tap-results: $(TAPFILES) From 8e11406fc6a9225dfc5852430b674397ad3157b3 Mon Sep 17 00:00:00 2001 From: "J. Lewis Muir" Date: Wed, 21 Jul 2021 09:53:11 -0500 Subject: [PATCH 038/169] Fix calcout rec doc typo: s/If it met/If met/ --- modules/database/src/std/rec/calcoutRecord.dbd.pod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/database/src/std/rec/calcoutRecord.dbd.pod b/modules/database/src/std/rec/calcoutRecord.dbd.pod index 9302ba410..f2a203714 100644 --- a/modules/database/src/std/rec/calcoutRecord.dbd.pod +++ b/modules/database/src/std/rec/calcoutRecord.dbd.pod @@ -1218,7 +1218,7 @@ honors the alarm hysteresis factor (HYST). Thus the value must change by at least HYST before the alarm status and severity changes. =item 4. -Determine if the Output Execution Option (OOPT) is met. If it met, either +Determine if the Output Execution Option (OOPT) is met. If met, either execute the output link (and output event) immediately (if ODLY = 0), or schedule a callback after the specified interval. See the explanation for the C routine below. From 16c3202992600a6b5c0daaaf9f29715f4650c458 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Wed, 21 Jul 2021 11:05:41 -0500 Subject: [PATCH 039/169] waveform: Add back lost PACT = TRUE Fixes GitHub Issue #187 --- modules/database/src/std/rec/waveformRecord.c | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/database/src/std/rec/waveformRecord.c b/modules/database/src/std/rec/waveformRecord.c index d075c01cd..72bc63efd 100644 --- a/modules/database/src/std/rec/waveformRecord.c +++ b/modules/database/src/std/rec/waveformRecord.c @@ -139,6 +139,7 @@ static long process(struct dbCommon *pcommon) if (!pact && prec->pact) return 0; + prec->pact = TRUE; prec->udf = FALSE; recGblGetTimeStampSimm(prec, prec->simm, &prec->siol); From 7a6aa3edd11f91e754305d4d3457063b751d09f7 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Wed, 21 Jul 2021 11:06:34 -0500 Subject: [PATCH 040/169] waveform: Update POD, describe BUSY field --- .../src/std/rec/waveformRecord.dbd.pod | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/modules/database/src/std/rec/waveformRecord.dbd.pod b/modules/database/src/std/rec/waveformRecord.dbd.pod index d787b7bbd..db4ca362f 100644 --- a/modules/database/src/std/rec/waveformRecord.dbd.pod +++ b/modules/database/src/std/rec/waveformRecord.dbd.pod @@ -120,9 +120,18 @@ at run-time. VAL references the array where the waveform stores its data. The BPTR field holds the address of the array. -The NORD field holds a counter of the number of elements that have been read -into the array. It is reset to 0 when the device is rearmed. The BUSY field -indicates if the device is armed but has not yet been digitized. +The NORD field indicates the number of elements that were read into the array. + +The BUSY field permits asynchronous device support to collect array elements +sequentially in multiple read cycles which may call the record's C +method many times before completing a read operation. Such a device would set +BUSY to TRUE along with setting PACT at the start of acquisition (it could also +set NORD to 0 and use it to keep track of how many elements have been received). +After receiving the last element the C routine would clear BUSY which +informs the record's C method that the read has finished. Note that +CA clients that perform gets of the VAL field can see partially filled arrays +when this type of device support is used, so the BUSY field is almost never used +today. =fields VAL, BPTR, NORD, BUSY @@ -367,14 +376,14 @@ Other: Error. =head3 Device Support For Soft Records The C<<< Soft Channel >>> device support module is provided to read values from -other records and store them in arrays. If INP is a constant link, then read_wf -does nothing. In this case, the record can be used to hold arrays written via -dbPuts. If INP is a database or channel access link, the new array value is read -from the link. NORD is set to the number of items in the array. +other records and store them in the VAL field. If INP is a constant link, then +C does nothing. In this case, the record can be used to hold a fixed +set of data or array values written from elsewhere. If INP is a valid link, the +new array value is read from that link. NORD is set to the number of items +received. -This module places a value directly in VAL. - -If the INP link type is constant, then NORD is set to zero. +If the INP link type is constant, VAL is set from it in the C +routine and NORD is also set at that time. =cut From ec87b2a867b3d3930d24eb1a8bfd949a08048290 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Wed, 21 Jul 2021 11:08:06 -0500 Subject: [PATCH 041/169] recGbl: Update to using dbGetTimeStampTag() --- modules/database/src/ioc/db/recGbl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/database/src/ioc/db/recGbl.c b/modules/database/src/ioc/db/recGbl.c index 9f5b90479..95387f5de 100644 --- a/modules/database/src/ioc/db/recGbl.c +++ b/modules/database/src/ioc/db/recGbl.c @@ -318,7 +318,7 @@ void recGblGetTimeStampSimm(void *pvoid, const epicsEnum16 simm, struct link *si } else { if (simm != menuSimmNO) { if (siol && !dbLinkIsConstant(siol)) { - if (dbGetTimeStamp(siol, &prec->time)) + if (dbGetTimeStampTag(siol, &prec->time, &prec->utag)) errlogPrintf("recGblGetTimeStampSimm: dbGetTimeStamp (sim mode) failed, %s.SIOL = %s\n", prec->name, siol->value.pv_link.pvname); return; From 3091f7c56f360d2704e37a76064ade4bdd9fef4c Mon Sep 17 00:00:00 2001 From: Kay Kasemir Date: Thu, 29 Jul 2021 14:38:16 -0400 Subject: [PATCH 042/169] int64in: Fix monitor delta test Only the lower 32 bit used to be compared. https://bugs.launchpad.net/epics-base/+bug/1938459 --- modules/database/src/std/rec/int64inRecord.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/database/src/std/rec/int64inRecord.c b/modules/database/src/std/rec/int64inRecord.c index a55b1a7da..2cd804d07 100644 --- a/modules/database/src/std/rec/int64inRecord.c +++ b/modules/database/src/std/rec/int64inRecord.c @@ -359,16 +359,16 @@ static void checkAlarms(int64inRecord *prec, epicsTimeStamp *timeLast) } /* DELTA calculates the absolute difference between its arguments - * expressed as an unsigned 32-bit integer */ + * expressed as an unsigned 64-bit integer */ #define DELTA(last, val) \ - ((epicsUInt32) ((last) > (val) ? (last) - (val) : (val) - (last))) + ((epicsUInt64) ((last) > (val) ? (last) - (val) : (val) - (last))) static void monitor(int64inRecord *prec) { unsigned short monitor_mask = recGblResetAlarms(prec); if (prec->mdel < 0 || - DELTA(prec->mlst, prec->val) > (epicsUInt32) prec->mdel) { + DELTA(prec->mlst, prec->val) > (epicsUInt64) prec->mdel) { /* post events for value change */ monitor_mask |= DBE_VALUE; /* update last value monitored */ @@ -376,7 +376,7 @@ static void monitor(int64inRecord *prec) } if (prec->adel < 0 || - DELTA(prec->alst, prec->val) > (epicsUInt32) prec->adel) { + DELTA(prec->alst, prec->val) > (epicsUInt64) prec->adel) { /* post events for archive value change */ monitor_mask |= DBE_LOG; /* update last archive value monitored */ From 78d2f20fa8cbc6a357c6340f890f8d3f84e0be84 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Thu, 24 Jun 2021 10:05:42 -0700 Subject: [PATCH 043/169] Com: Adjust epicsAtomic conditionals for GCC cf. https://bugs.launchpad.net/epics-base/+bug/1932118 --- .../src/osi/compiler/gcc/epicsAtomicCD.h | 72 +++++-------------- modules/libcom/test/epicsAtomicTest.cpp | 6 +- 2 files changed, 20 insertions(+), 58 deletions(-) diff --git a/modules/libcom/src/osi/compiler/gcc/epicsAtomicCD.h b/modules/libcom/src/osi/compiler/gcc/epicsAtomicCD.h index 2f6a23ff2..a7bcd8f3c 100644 --- a/modules/libcom/src/osi/compiler/gcc/epicsAtomicCD.h +++ b/modules/libcom/src/osi/compiler/gcc/epicsAtomicCD.h @@ -23,50 +23,36 @@ #define EPICS_ATOMIC_CMPLR_NAME "GCC" +/* expands __GCC_HAVE_SYNC_COMPARE_AND_SWAP_ concatentating the numeric value __SIZEOF_*__ */ #define GCC_ATOMIC_CONCAT( A, B ) GCC_ATOMIC_CONCATR(A,B) #define GCC_ATOMIC_CONCATR( A, B ) ( A ## B ) +/* + * As of GCC 8, the __sync_synchronize() is inlined for all + * known targets (aarch64, arm, i386, powerpc, and x86_64) + * except for arm <=6. + * Note that i386 inlines __sync_synchronize() but does not + * define __GCC_HAVE_SYNC_COMPARE_AND_SWAP_* + */ +#define GCC_ATOMIC_INTRINSICS_AVAIL_SYNC \ + defined(GCC_ATOMIC_INTRINSICS_AVAIL_INT_T) || defined(GCC_ATOMIC_INTRINSICS_AVAIL_SIZE_T) || defined(__i386) + #define GCC_ATOMIC_INTRINSICS_AVAIL_INT_T \ GCC_ATOMIC_CONCAT ( \ __GCC_HAVE_SYNC_COMPARE_AND_SWAP_, \ __SIZEOF_INT__ ) +/* we assume __SIZEOF_POINTER__ == __SIZEOF_SIZE_T__ */ #define GCC_ATOMIC_INTRINSICS_AVAIL_SIZE_T \ GCC_ATOMIC_CONCAT ( \ __GCC_HAVE_SYNC_COMPARE_AND_SWAP_, \ __SIZEOF_SIZE_T__ ) -#define GCC_ATOMIC_INTRINSICS_MIN_X86 \ - ( defined ( __i486 ) || defined ( __pentium ) || \ - defined ( __pentiumpro ) || defined ( __MMX__ ) ) - -#define GCC_ATOMIC_INTRINSICS_GCC4_OR_BETTER \ - ( ( __GNUC__ * 100 + __GNUC_MINOR__ ) >= 401 ) - -#define GCC_ATOMIC_INTRINSICS_AVAIL_EARLIER \ - ( GCC_ATOMIC_INTRINSICS_MIN_X86 && \ - GCC_ATOMIC_INTRINSICS_GCC4_OR_BETTER ) - #ifdef __cplusplus extern "C" { #endif -/* - * We are optimistic that __sync_synchronize is implemented - * in all version four gcc invariant of target. The gnu doc - * seems to say that when not supported by architecture a call - * to an external function is generated but in practice - * this isn`t the case for some of the atomic intrinsics, and - * so there is an undefined symbol. So far we have not seen - * that with __sync_synchronize, but we can only guess based - * on experimental evidence. - * - * For example we know that when generating object code for - * 386 most of the atomic intrinsics are not present and - * we see undefined symbols with mingw, but we don`t have - * troubles with __sync_synchronize. - */ -#if GCC_ATOMIC_INTRINSICS_GCC4_OR_BETTER +#if GCC_ATOMIC_INTRINSICS_AVAIL_SYNC #ifndef EPICS_ATOMIC_READ_MEMORY_BARRIER #define EPICS_ATOMIC_READ_MEMORY_BARRIER @@ -84,32 +70,9 @@ EPICS_ATOMIC_INLINE void epicsAtomicWriteMemoryBarrier (void) } #endif -#else - -#ifndef EPICS_ATOMIC_READ_MEMORY_BARRIER -#if GCC_ATOMIC_INTRINSICS_MIN_X86 -#define EPICS_ATOMIC_READ_MEMORY_BARRIER -EPICS_ATOMIC_INLINE void epicsAtomicReadMemoryBarrier (void) -{ - asm("mfence;"); -} -#endif #endif -#ifndef EPICS_ATOMIC_WRITE_MEMORY_BARRIER -#if GCC_ATOMIC_INTRINSICS_MIN_X86 -#define EPICS_ATOMIC_WRITE_MEMORY_BARRIER -EPICS_ATOMIC_INLINE void epicsAtomicWriteMemoryBarrier (void) -{ - asm("mfence;"); -} -#endif -#endif - -#endif /* if GCC_ATOMIC_INTRINSICS_GCC4_OR_BETTER */ - -#if GCC_ATOMIC_INTRINSICS_AVAIL_INT_T \ - || GCC_ATOMIC_INTRINSICS_AVAIL_EARLIER +#if GCC_ATOMIC_INTRINSICS_AVAIL_INT_T #define EPICS_ATOMIC_INCR_INTT EPICS_ATOMIC_INLINE int epicsAtomicIncrIntT ( int * pTarget ) @@ -136,10 +99,9 @@ EPICS_ATOMIC_INLINE int epicsAtomicCmpAndSwapIntT ( int * pTarget, return __sync_val_compare_and_swap ( pTarget, oldVal, newVal); } -#endif /* if GCC_ATOMIC_INTRINSICS_AVAIL_INT_T */ +#endif -#if GCC_ATOMIC_INTRINSICS_AVAIL_SIZE_T \ - || GCC_ATOMIC_INTRINSICS_AVAIL_EARLIER +#if GCC_ATOMIC_INTRINSICS_AVAIL_SIZE_T #define EPICS_ATOMIC_INCR_SIZET EPICS_ATOMIC_INLINE size_t epicsAtomicIncrSizeT ( size_t * pTarget ) @@ -180,7 +142,7 @@ EPICS_ATOMIC_INLINE EpicsAtomicPtrT epicsAtomicCmpAndSwapPtrT ( return __sync_val_compare_and_swap ( pTarget, oldVal, newVal); } -#endif /* if GCC_ATOMIC_INTRINSICS_AVAIL_SIZE_T */ +#endif #ifdef __cplusplus } /* end of extern "C" */ diff --git a/modules/libcom/test/epicsAtomicTest.cpp b/modules/libcom/test/epicsAtomicTest.cpp index 77321af0e..32bae191e 100644 --- a/modules/libcom/test/epicsAtomicTest.cpp +++ b/modules/libcom/test/epicsAtomicTest.cpp @@ -285,15 +285,15 @@ static void testClassify() #endif #ifdef __GNUC__ -#if GCC_ATOMIC_INTRINSICS_GCC4_OR_BETTER +#if GCC_ATOMIC_INTRINSICS_AVAIL_SYNC testDiag("GCC using atomic builtin memory barrier"); #else testDiag("GCC using asm memory barrier"); #endif -#if GCC_ATOMIC_INTRINSICS_AVAIL_INT_T || GCC_ATOMIC_INTRINSICS_AVAIL_EARLIER +#if GCC_ATOMIC_INTRINSICS_AVAIL_INT_T testDiag("GCC use builtin for int"); #endif -#if GCC_ATOMIC_INTRINSICS_AVAIL_SIZE_T || GCC_ATOMIC_INTRINSICS_AVAIL_EARLIER +#if GCC_ATOMIC_INTRINSICS_AVAIL_SIZE_T testDiag("GCC use builtin for size_t"); #endif From a667cc7aa4e741f480f77711b941b2e507c60da7 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Mon, 2 Aug 2021 18:26:02 -0500 Subject: [PATCH 044/169] Move GCC+Clang common headers to a new file --- modules/libcom/src/osi/Makefile | 4 + .../src/osi/compiler/clang/epicsAtomicCD.h | 10 ++ .../src/osi/compiler/gcc/epicsAtomicCD.h | 125 +------------- modules/libcom/src/osi/epicsAtomicGCC.h | 156 ++++++++++++++++++ 4 files changed, 171 insertions(+), 124 deletions(-) create mode 100644 modules/libcom/src/osi/epicsAtomicGCC.h diff --git a/modules/libcom/src/osi/Makefile b/modules/libcom/src/osi/Makefile index ce9dcc692..a0835d57b 100644 --- a/modules/libcom/src/osi/Makefile +++ b/modules/libcom/src/osi/Makefile @@ -65,6 +65,10 @@ INC += osdVME.h INC += epicsMMIO.h INC += epicsMMIODef.h +INC_clang += epicsAtomicGCC.h +INC_gcc += epicsAtomicGCC.h +INC += $(INC_$(CMPLR_CLASS)) + Com_SRCS += epicsThread.cpp Com_SRCS += epicsMutex.cpp Com_SRCS += epicsEvent.cpp diff --git a/modules/libcom/src/osi/compiler/clang/epicsAtomicCD.h b/modules/libcom/src/osi/compiler/clang/epicsAtomicCD.h index 019ce244c..081828ad5 100644 --- a/modules/libcom/src/osi/compiler/clang/epicsAtomicCD.h +++ b/modules/libcom/src/osi/compiler/clang/epicsAtomicCD.h @@ -17,8 +17,18 @@ #ifndef epicsAtomicCD_h #define epicsAtomicCD_h +#ifndef __clang__ +# error this header is only for use with the Clang compiler +#endif + #define EPICS_ATOMIC_CMPLR_NAME "CLANG" +#include + +/* + * if currently unavailable as intrinsics we + * will try for an os specific inline solution + */ #include "epicsAtomicOSD.h" #endif /* epicsAtomicCD_h */ diff --git a/modules/libcom/src/osi/compiler/gcc/epicsAtomicCD.h b/modules/libcom/src/osi/compiler/gcc/epicsAtomicCD.h index a7bcd8f3c..c770b363e 100644 --- a/modules/libcom/src/osi/compiler/gcc/epicsAtomicCD.h +++ b/modules/libcom/src/osi/compiler/gcc/epicsAtomicCD.h @@ -23,130 +23,7 @@ #define EPICS_ATOMIC_CMPLR_NAME "GCC" -/* expands __GCC_HAVE_SYNC_COMPARE_AND_SWAP_ concatentating the numeric value __SIZEOF_*__ */ -#define GCC_ATOMIC_CONCAT( A, B ) GCC_ATOMIC_CONCATR(A,B) -#define GCC_ATOMIC_CONCATR( A, B ) ( A ## B ) - -/* - * As of GCC 8, the __sync_synchronize() is inlined for all - * known targets (aarch64, arm, i386, powerpc, and x86_64) - * except for arm <=6. - * Note that i386 inlines __sync_synchronize() but does not - * define __GCC_HAVE_SYNC_COMPARE_AND_SWAP_* - */ -#define GCC_ATOMIC_INTRINSICS_AVAIL_SYNC \ - defined(GCC_ATOMIC_INTRINSICS_AVAIL_INT_T) || defined(GCC_ATOMIC_INTRINSICS_AVAIL_SIZE_T) || defined(__i386) - -#define GCC_ATOMIC_INTRINSICS_AVAIL_INT_T \ - GCC_ATOMIC_CONCAT ( \ - __GCC_HAVE_SYNC_COMPARE_AND_SWAP_, \ - __SIZEOF_INT__ ) - -/* we assume __SIZEOF_POINTER__ == __SIZEOF_SIZE_T__ */ -#define GCC_ATOMIC_INTRINSICS_AVAIL_SIZE_T \ - GCC_ATOMIC_CONCAT ( \ - __GCC_HAVE_SYNC_COMPARE_AND_SWAP_, \ - __SIZEOF_SIZE_T__ ) - -#ifdef __cplusplus -extern "C" { -#endif - -#if GCC_ATOMIC_INTRINSICS_AVAIL_SYNC - -#ifndef EPICS_ATOMIC_READ_MEMORY_BARRIER -#define EPICS_ATOMIC_READ_MEMORY_BARRIER -EPICS_ATOMIC_INLINE void epicsAtomicReadMemoryBarrier (void) -{ - __sync_synchronize (); -} -#endif - -#ifndef EPICS_ATOMIC_WRITE_MEMORY_BARRIER -#define EPICS_ATOMIC_WRITE_MEMORY_BARRIER -EPICS_ATOMIC_INLINE void epicsAtomicWriteMemoryBarrier (void) -{ - __sync_synchronize (); -} -#endif - -#endif - -#if GCC_ATOMIC_INTRINSICS_AVAIL_INT_T - -#define EPICS_ATOMIC_INCR_INTT -EPICS_ATOMIC_INLINE int epicsAtomicIncrIntT ( int * pTarget ) -{ - return __sync_add_and_fetch ( pTarget, 1 ); -} - -#define EPICS_ATOMIC_DECR_INTT -EPICS_ATOMIC_INLINE int epicsAtomicDecrIntT ( int * pTarget ) -{ - return __sync_sub_and_fetch ( pTarget, 1 ); -} - -#define EPICS_ATOMIC_ADD_INTT -EPICS_ATOMIC_INLINE int epicsAtomicAddIntT ( int * pTarget, int delta ) -{ - return __sync_add_and_fetch ( pTarget, delta ); -} - -#define EPICS_ATOMIC_CAS_INTT -EPICS_ATOMIC_INLINE int epicsAtomicCmpAndSwapIntT ( int * pTarget, - int oldVal, int newVal ) -{ - return __sync_val_compare_and_swap ( pTarget, oldVal, newVal); -} - -#endif - -#if GCC_ATOMIC_INTRINSICS_AVAIL_SIZE_T - -#define EPICS_ATOMIC_INCR_SIZET -EPICS_ATOMIC_INLINE size_t epicsAtomicIncrSizeT ( size_t * pTarget ) -{ - return __sync_add_and_fetch ( pTarget, 1u ); -} - -#define EPICS_ATOMIC_DECR_SIZET -EPICS_ATOMIC_INLINE size_t epicsAtomicDecrSizeT ( size_t * pTarget ) -{ - return __sync_sub_and_fetch ( pTarget, 1u ); -} - -#define EPICS_ATOMIC_ADD_SIZET -EPICS_ATOMIC_INLINE size_t epicsAtomicAddSizeT ( size_t * pTarget, size_t delta ) -{ - return __sync_add_and_fetch ( pTarget, delta ); -} - -#define EPICS_ATOMIC_SUB_SIZET -EPICS_ATOMIC_INLINE size_t epicsAtomicSubSizeT ( size_t * pTarget, size_t delta ) -{ - return __sync_sub_and_fetch ( pTarget, delta ); -} - -#define EPICS_ATOMIC_CAS_SIZET -EPICS_ATOMIC_INLINE size_t epicsAtomicCmpAndSwapSizeT ( size_t * pTarget, - size_t oldVal, size_t newVal ) -{ - return __sync_val_compare_and_swap ( pTarget, oldVal, newVal); -} - -#define EPICS_ATOMIC_CAS_PTRT -EPICS_ATOMIC_INLINE EpicsAtomicPtrT epicsAtomicCmpAndSwapPtrT ( - EpicsAtomicPtrT * pTarget, - EpicsAtomicPtrT oldVal, EpicsAtomicPtrT newVal ) -{ - return __sync_val_compare_and_swap ( pTarget, oldVal, newVal); -} - -#endif - -#ifdef __cplusplus -} /* end of extern "C" */ -#endif +#include /* * if currently unavailable as gcc intrinsics we diff --git a/modules/libcom/src/osi/epicsAtomicGCC.h b/modules/libcom/src/osi/epicsAtomicGCC.h new file mode 100644 index 000000000..006d6a27e --- /dev/null +++ b/modules/libcom/src/osi/epicsAtomicGCC.h @@ -0,0 +1,156 @@ +/*************************************************************************\ +* Copyright (c) 2011 LANS LLC, as Operator of +* Los Alamos National Laboratory. +* Copyright (c) 2021 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* SPDX-License-Identifier: EPICS +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author Jeffrey O. Hill + * johill@lanl.gov + */ + +/* + * These implementations are the same for both GCC and Clang + */ + +#ifndef INC_epicsAtomicGCC_H +#define INC_epicsAtomicGCC_H + +/* expands __GCC_HAVE_SYNC_COMPARE_AND_SWAP_ concatentating + * the numeric value __SIZEOF_*__ + */ +#define GCC_ATOMIC_CONCAT( A, B ) GCC_ATOMIC_CONCATR(A,B) +#define GCC_ATOMIC_CONCATR( A, B ) ( A ## B ) + +#define GCC_ATOMIC_INTRINSICS_AVAIL_INT_T \ + GCC_ATOMIC_CONCAT ( \ + __GCC_HAVE_SYNC_COMPARE_AND_SWAP_, \ + __SIZEOF_INT__ ) + +/* we assume __SIZEOF_POINTER__ == __SIZEOF_SIZE_T__ */ +#define GCC_ATOMIC_INTRINSICS_AVAIL_SIZE_T \ + GCC_ATOMIC_CONCAT ( \ + __GCC_HAVE_SYNC_COMPARE_AND_SWAP_, \ + __SIZEOF_SIZE_T__ ) + +/* + * As of GCC 8, the __sync_synchronize() is inlined for all + * known targets (aarch64, arm, i386, powerpc, and x86_64) + * except for arm <=6. + * Note that i386 inlines __sync_synchronize() but does not + * define __GCC_HAVE_SYNC_COMPARE_AND_SWAP_* + */ +#if GCC_ATOMIC_INTRINSICS_AVAIL_INT_T || \ + GCC_ATOMIC_INTRINSICS_AVAIL_SIZE_T || \ + defined(__i386) +#define GCC_ATOMIC_INTRINSICS_AVAIL_SYNC 1 +#else +#define GCC_ATOMIC_INTRINSICS_AVAIL_SYNC 0 +#endif +/* The above macro is also used in epicsAtomicTest.cpp */ + +#ifdef __cplusplus +extern "C" { +#endif + +#if GCC_ATOMIC_INTRINSICS_AVAIL_SYNC + +#ifndef EPICS_ATOMIC_READ_MEMORY_BARRIER +#define EPICS_ATOMIC_READ_MEMORY_BARRIER +EPICS_ATOMIC_INLINE void epicsAtomicReadMemoryBarrier (void) +{ + __sync_synchronize (); +} +#endif + +#ifndef EPICS_ATOMIC_WRITE_MEMORY_BARRIER +#define EPICS_ATOMIC_WRITE_MEMORY_BARRIER +EPICS_ATOMIC_INLINE void epicsAtomicWriteMemoryBarrier (void) +{ + __sync_synchronize (); +} +#endif + +#endif + +#if GCC_ATOMIC_INTRINSICS_AVAIL_INT_T + +#define EPICS_ATOMIC_INCR_INTT +EPICS_ATOMIC_INLINE int epicsAtomicIncrIntT ( int * pTarget ) +{ + return __sync_add_and_fetch ( pTarget, 1 ); +} + +#define EPICS_ATOMIC_DECR_INTT +EPICS_ATOMIC_INLINE int epicsAtomicDecrIntT ( int * pTarget ) +{ + return __sync_sub_and_fetch ( pTarget, 1 ); +} + +#define EPICS_ATOMIC_ADD_INTT +EPICS_ATOMIC_INLINE int epicsAtomicAddIntT ( int * pTarget, int delta ) +{ + return __sync_add_and_fetch ( pTarget, delta ); +} + +#define EPICS_ATOMIC_CAS_INTT +EPICS_ATOMIC_INLINE int epicsAtomicCmpAndSwapIntT ( int * pTarget, + int oldVal, int newVal ) +{ + return __sync_val_compare_and_swap ( pTarget, oldVal, newVal); +} + +#endif + +#if GCC_ATOMIC_INTRINSICS_AVAIL_SIZE_T + +#define EPICS_ATOMIC_INCR_SIZET +EPICS_ATOMIC_INLINE size_t epicsAtomicIncrSizeT ( size_t * pTarget ) +{ + return __sync_add_and_fetch ( pTarget, 1u ); +} + +#define EPICS_ATOMIC_DECR_SIZET +EPICS_ATOMIC_INLINE size_t epicsAtomicDecrSizeT ( size_t * pTarget ) +{ + return __sync_sub_and_fetch ( pTarget, 1u ); +} + +#define EPICS_ATOMIC_ADD_SIZET +EPICS_ATOMIC_INLINE size_t epicsAtomicAddSizeT ( size_t * pTarget, size_t delta ) +{ + return __sync_add_and_fetch ( pTarget, delta ); +} + +#define EPICS_ATOMIC_SUB_SIZET +EPICS_ATOMIC_INLINE size_t epicsAtomicSubSizeT ( size_t * pTarget, size_t delta ) +{ + return __sync_sub_and_fetch ( pTarget, delta ); +} + +#define EPICS_ATOMIC_CAS_SIZET +EPICS_ATOMIC_INLINE size_t epicsAtomicCmpAndSwapSizeT ( size_t * pTarget, + size_t oldVal, size_t newVal ) +{ + return __sync_val_compare_and_swap ( pTarget, oldVal, newVal); +} + +#define EPICS_ATOMIC_CAS_PTRT +EPICS_ATOMIC_INLINE EpicsAtomicPtrT epicsAtomicCmpAndSwapPtrT ( + EpicsAtomicPtrT * pTarget, + EpicsAtomicPtrT oldVal, EpicsAtomicPtrT newVal ) +{ + return __sync_val_compare_and_swap ( pTarget, oldVal, newVal); +} + +#endif + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* INC_epicsAtomicGCC_H */ From 540a5c87d91d3be833bfea1ec1951a8563a5ac44 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Mon, 2 Aug 2021 18:26:44 -0500 Subject: [PATCH 045/169] Adjust wording of classification descriptions --- modules/libcom/test/epicsAtomicTest.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/modules/libcom/test/epicsAtomicTest.cpp b/modules/libcom/test/epicsAtomicTest.cpp index 32bae191e..a7bd31000 100644 --- a/modules/libcom/test/epicsAtomicTest.cpp +++ b/modules/libcom/test/epicsAtomicTest.cpp @@ -285,16 +285,17 @@ static void testClassify() #endif #ifdef __GNUC__ + /* Also applies to CLANG */ #if GCC_ATOMIC_INTRINSICS_AVAIL_SYNC - testDiag("GCC using atomic builtin memory barrier"); + testDiag("Use " EPICS_ATOMIC_CMPLR_NAME " atomic builtin memory barrier"); #else - testDiag("GCC using asm memory barrier"); + testDiag("Use default memory barrier"); #endif #if GCC_ATOMIC_INTRINSICS_AVAIL_INT_T - testDiag("GCC use builtin for int"); + testDiag("Use " EPICS_ATOMIC_CMPLR_NAME " builtins for int"); #endif #if GCC_ATOMIC_INTRINSICS_AVAIL_SIZE_T - testDiag("GCC use builtin for size_t"); + testDiag("Use " EPICS_ATOMIC_CMPLR_NAME " builtins for size_t"); #endif #ifndef EPICS_ATOMIC_INCR_INTT From 32d76623f26223b51be5f8759127f28525b53aa1 Mon Sep 17 00:00:00 2001 From: JJL772 Date: Wed, 21 Jul 2021 17:02:12 -0700 Subject: [PATCH 046/169] Fix potential memory leak on error In osdThread.c for POSIX if pthread_create_key fails In iocLogServer.c if fdmgr_init returns NULL In dbBkpt.c if semaphore creation fails while adding a bp to a lockset In devSiSoftCallback.c if linked record is not found --- modules/database/src/ioc/db/dbBkpt.c | 1 + modules/database/src/std/dev/devSiSoftCallback.c | 1 + modules/libcom/src/log/iocLogServer.c | 1 + modules/libcom/src/osi/os/posix/osdThread.c | 4 +++- 4 files changed, 6 insertions(+), 1 deletion(-) diff --git a/modules/database/src/ioc/db/dbBkpt.c b/modules/database/src/ioc/db/dbBkpt.c index 18a357ebe..a32ebab4e 100644 --- a/modules/database/src/ioc/db/dbBkpt.c +++ b/modules/database/src/ioc/db/dbBkpt.c @@ -331,6 +331,7 @@ long dbb(const char *record_name) if (pnode->ex_sem == NULL) { printf(" BKPT> Out of memory\n"); dbScanUnlock(precord); + free(pnode); epicsMutexUnlock(bkpt_stack_sem); return(1); } diff --git a/modules/database/src/std/dev/devSiSoftCallback.c b/modules/database/src/std/dev/devSiSoftCallback.c index 85ec4fcad..f52d595c1 100644 --- a/modules/database/src/std/dev/devSiSoftCallback.c +++ b/modules/database/src/std/dev/devSiSoftCallback.c @@ -108,6 +108,7 @@ static long add_record(dbCommon *pcommon) recGblRecordError(status, (void *)prec, "devSiSoftCallback (add_record) linked record not found"); + free(pdevPvt); return status; } diff --git a/modules/libcom/src/log/iocLogServer.c b/modules/libcom/src/log/iocLogServer.c index 732316196..08f9eca5f 100644 --- a/modules/libcom/src/log/iocLogServer.c +++ b/modules/libcom/src/log/iocLogServer.c @@ -113,6 +113,7 @@ int main(void) pserver->pfdctx = (void *) fdmgr_init(); if (!pserver->pfdctx) { + free(pserver); fprintf(stderr, "iocLogServer: %s\n", strerror(errno)); return IOCLS_ERROR; } diff --git a/modules/libcom/src/osi/os/posix/osdThread.c b/modules/libcom/src/osi/os/posix/osdThread.c index 61a67f62c..80c53e3e6 100644 --- a/modules/libcom/src/osi/os/posix/osdThread.c +++ b/modules/libcom/src/osi/os/posix/osdThread.c @@ -956,8 +956,10 @@ LIBCOM_API epicsThreadPrivateId epicsStdCall epicsThreadPrivateCreate(void) return NULL; status = pthread_key_create(key,0); checkStatus(status,"pthread_key_create epicsThreadPrivateCreate"); - if(status) + if(status) { + free(key); return NULL; + } return((epicsThreadPrivateId)key); } From 7c991f3f2a804e98df7ab89d83577694c8503a48 Mon Sep 17 00:00:00 2001 From: JJL772 Date: Wed, 21 Jul 2021 17:26:39 -0700 Subject: [PATCH 047/169] Fix segfault in dbtpn when value parameter is nullptr Running 'dbtpn Record' in iocsh would result in a segfault. --- modules/database/src/ioc/db/dbNotify.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/database/src/ioc/db/dbNotify.c b/modules/database/src/ioc/db/dbNotify.c index e596285c2..7613f3672 100644 --- a/modules/database/src/ioc/db/dbNotify.c +++ b/modules/database/src/ioc/db/dbNotify.c @@ -613,8 +613,10 @@ long dbtpn(char *pname, char *pvalue) ptpnInfo = dbCalloc(1, sizeof(tpnInfo)); ptpnInfo->ppn = ppn; ptpnInfo->callbackDone = epicsEventCreate(epicsEventEmpty); - strncpy(ptpnInfo->buffer, pvalue, 80); - ptpnInfo->buffer[79] = 0; + if (pvalue) { + strncpy(ptpnInfo->buffer, pvalue, sizeof(ptpnInfo->buffer)); + ptpnInfo->buffer[sizeof(ptpnInfo->buffer)-1] = 0; + } ppn->usrPvt = ptpnInfo; epicsThreadCreate("dbtpn", epicsThreadPriorityHigh, From cb8c7998b62701a849a6fa9c299cc1613f66a627 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 18 Sep 2020 18:37:25 -0700 Subject: [PATCH 048/169] epicsTime: rework Re-implement around epicsTimeStamp (C API) with class epicsTime becoming a wrapper. Prefer epicsInt64 arithmetic. Remove opaque struct l_fp (NTP time conversion) --- modules/libcom/src/osi/epicsTime.cpp | 1082 ++++--------------- modules/libcom/src/osi/epicsTime.h | 699 ++++++------ modules/libcom/src/osi/os/WIN32/osdTime.cpp | 12 +- modules/libcom/test/epicsTimeTest.cpp | 139 ++- 4 files changed, 676 insertions(+), 1256 deletions(-) diff --git a/modules/libcom/src/osi/epicsTime.cpp b/modules/libcom/src/osi/epicsTime.cpp index 044d58cfa..90d4f9810 100644 --- a/modules/libcom/src/osi/epicsTime.cpp +++ b/modules/libcom/src/osi/epicsTime.cpp @@ -12,13 +12,6 @@ /* epicsTime.cpp */ /* Author Jeffrey O. Hill */ -// Notes: -// 1) The epicsTime::nSec field is not public and so it could be -// changed to work more like the fractional seconds field in the NTP time -// stamp. That would significantly improve the precision of epicsTime on -// 64 bit architectures. -// - #include #include @@ -29,480 +22,76 @@ #include #include // vxWorks 6.0 requires this include -#include "locationException.h" +#include "errSymTbl.h" #include "epicsAssert.h" #include "epicsVersion.h" +#include "epicsStdlib.h" +#include "epicsMath.h" #include "envDefs.h" #include "epicsTime.h" #include "osiSock.h" /* pull in struct timeval */ #include "epicsStdio.h" -static const char pEpicsTimeVersion[] = - "@(#) " EPICS_VERSION_STRING ", Common Utilities Library"; - -// -// useful public constants -// -static const unsigned mSecPerSec = 1000u; -static const unsigned uSecPerMSec = 1000u; -static const unsigned uSecPerSec = uSecPerMSec * mSecPerSec; -static const unsigned nSecPerUSec = 1000u; -static const unsigned nSecPerSec = nSecPerUSec * uSecPerSec; +static const epicsUInt32 nSecPerSec = 1000000000u; static const unsigned nSecFracDigits = 9u; -// Timescale conversion data - -static const unsigned long NTP_TIME_AT_POSIX_EPOCH = 2208988800ul; -static const unsigned long NTP_TIME_AT_EPICS_EPOCH = - NTP_TIME_AT_POSIX_EPOCH + POSIX_TIME_AT_EPICS_EPOCH; - -// -// epicsTime (const unsigned long secIn, const unsigned long nSecIn) -// -inline epicsTime::epicsTime (const unsigned long secIn, const unsigned long nSecIn) : - secPastEpoch ( secIn ), nSec ( nSecIn ) +void epicsTime::throwError(int code) { - if (nSecIn >= nSecPerSec) { - this->secPastEpoch += nSecIn / nSecPerSec; - this->nSec = nSecIn % nSecPerSec; - } -} - -// -// epicsTimeLoadTimeInit -// -class epicsTimeLoadTimeInit { -public: - epicsTimeLoadTimeInit (); - double epicsEpochOffset; // seconds - double time_tSecPerTick; // seconds (both NTP and EPICS use int sec) - unsigned long epicsEpochOffsetAsAnUnsignedLong; - bool useDiffTimeOptimization; -}; - -// -// epicsTimeLoadTimeInit () -// -epicsTimeLoadTimeInit::epicsTimeLoadTimeInit () -{ - // All we know about time_t is that it is an arithmetic type. - time_t t_zero = static_cast (0); - time_t t_one = static_cast (1); - this->time_tSecPerTick = difftime (t_one, t_zero); - - /* The EPICS epoch (1/1/1990 00:00:00UTC) was 631152000 seconds after - * the ANSI epoch (1/1/1970 00:00:00UTC) - * Convert this offset into time_t units, however this must not be - * calculated using local time (i.e. using mktime() or similar), since - * in the UK the ANSI Epoch had daylight saving time in effect, and - * the value calculated would be 3600 seconds wrong.*/ - this->epicsEpochOffset = - (double) POSIX_TIME_AT_EPICS_EPOCH / this->time_tSecPerTick; - - if (this->time_tSecPerTick == 1.0 && - this->epicsEpochOffset <= ULONG_MAX && - this->epicsEpochOffset >= 0) { - // We can use simpler code on Posix-compliant systems - this->useDiffTimeOptimization = true; - this->epicsEpochOffsetAsAnUnsignedLong = - static_cast(this->epicsEpochOffset); - } else { - // Forced to use the slower but correct code - this->useDiffTimeOptimization = false; - this->epicsEpochOffsetAsAnUnsignedLong = 0; - } -} - -// -// private epicsTime::addNanoSec () -// -// Most formats keep the nSec value as an unsigned long, so are +ve. -// struct timeval's tv_usec may be -1, but I think that means error, -// so this private method never needs to handle -ve offsets. -// -void epicsTime :: addNanoSec ( long nSecAdj ) -{ - if (nSecAdj <= 0) + if(code==epicsTimeOK) return; - - if (static_cast(nSecAdj) >= nSecPerSec) { - this->secPastEpoch += nSecAdj / nSecPerSec; - nSecAdj %= nSecPerSec; - } - - this->nSec += nSecAdj; // Can't overflow - if (this->nSec >= nSecPerSec) { - this->secPastEpoch++; - this->nSec -= nSecPerSec; - } + throw std::logic_error(errSymMsg(code)); } -// -// epicsTime (const time_t_wrapper &tv) -// -epicsTime::epicsTime ( const time_t_wrapper & ansiTimeTicks ) -{ - // avoid c++ static initialization order issues - static epicsTimeLoadTimeInit & lti = * new epicsTimeLoadTimeInit (); - // - // try to directly map time_t into an unsigned long integer because this is - // faster on systems w/o hardware floating point and a simple integer type time_t. - // - if ( lti.useDiffTimeOptimization ) { - // LONG_MAX is used here and not ULONG_MAX because some systems (linux) - // still store time_t as a long. - if ( ansiTimeTicks.ts > 0 && ansiTimeTicks.ts <= LONG_MAX ) { - unsigned long ticks = static_cast < unsigned long > ( ansiTimeTicks.ts ); - if ( ticks >= lti.epicsEpochOffsetAsAnUnsignedLong ) { - this->secPastEpoch = ticks - lti.epicsEpochOffsetAsAnUnsignedLong; - } - else { - this->secPastEpoch = ( ULONG_MAX - lti.epicsEpochOffsetAsAnUnsignedLong ) + ticks; - } - this->nSec = 0; - return; - } - } - - // - // otherwise map time_t, which ANSI C and POSIX define as any arithmetic type, - // into type double - // - double sec = ansiTimeTicks.ts * lti.time_tSecPerTick - lti.epicsEpochOffset; - - // - // map into the the EPICS time stamp range (which allows rollover) - // - static double uLongMax = static_cast (ULONG_MAX); - if ( sec < 0.0 ) { - if ( sec < -uLongMax ) { - sec = sec + static_cast ( -sec / uLongMax ) * uLongMax; - } - sec += uLongMax; - } - else if ( sec > uLongMax ) { - sec = sec - static_cast ( sec / uLongMax ) * uLongMax; - } - - this->secPastEpoch = static_cast ( sec ); - this->nSec = static_cast ( ( sec-this->secPastEpoch ) * nSecPerSec ); +epicsTime::epicsTime ( const epicsTimeStamp & replace ) { + ts = replace; + if(ts.nsec >= nSecPerSec) + throw std::logic_error("epicsTimeStamp has overflow in nano-seconds field"); } -epicsTime::epicsTime (const epicsTimeStamp &ts) -{ - if ( ts.nsec < nSecPerSec ) { - this->secPastEpoch = ts.secPastEpoch; - this->nSec = ts.nsec; - } - else { - throw std::logic_error ( - "epicsTimeStamp has overflow in nano-seconds field" ); - } -} - -epicsTime::epicsTime () : - secPastEpoch(0u), nSec(0u) {} - epicsTime epicsTime::getCurrent () { epicsTimeStamp current; int status = epicsTimeGetCurrent (¤t); if (status) { - throwWithLocation ( unableToFetchCurrentTime () ); + throw unableToFetchCurrentTime ("Unable to fetch Current Time"); } return epicsTime ( current ); } -epicsTime epicsTime::getMonotonic() -{ - epicsTimeStamp current; - epicsTimeGetMonotonic (¤t); // can't fail - return epicsTime ( current ); -} - epicsTime epicsTime::getEvent (const epicsTimeEvent &event) { epicsTimeStamp current; int status = epicsTimeGetEvent (¤t, event); if (status) { - throwWithLocation ( unableToFetchCurrentTime () ); + throw unableToFetchCurrentTime ("Unable to fetch Event Time"); } return epicsTime ( current ); } -// -// operator time_t_wrapper () -// -epicsTime::operator time_t_wrapper () const -{ - // avoid c++ static initialization order issues - static epicsTimeLoadTimeInit & lti = * new epicsTimeLoadTimeInit (); - time_t_wrapper wrap; - - if ( lti.useDiffTimeOptimization ) { - if ( this->secPastEpoch < ULONG_MAX - lti.epicsEpochOffsetAsAnUnsignedLong ) { - wrap.ts = static_cast ( this->secPastEpoch + lti.epicsEpochOffsetAsAnUnsignedLong ); - return wrap; - } - } - - // - // map type double into time_t which ansi C defines as some arithmetic type - // - double tmp = (this->secPastEpoch + lti.epicsEpochOffset) / lti.time_tSecPerTick; - tmp += (this->nSec / lti.time_tSecPerTick) / nSecPerSec; - - wrap.ts = static_cast ( tmp ); - - return wrap; +epicsTime::operator struct timeval () const { + timeval ret; + epicsTimeToTimeval(&ret, &ts); + return ret; } -// -// convert to ANSI C struct tm (with nano seconds) adjusted for the local time zone -// -epicsTime::operator local_tm_nano_sec () const -{ - time_t_wrapper ansiTimeTicks = *this; - - local_tm_nano_sec tm; - - int status = epicsTime_localtime ( &ansiTimeTicks.ts, &tm.ansi_tm ); - if ( status ) { - throw std::logic_error ( "epicsTime_localtime failed" ); - } - - tm.nSec = this->nSec; - - return tm; +epicsTime::epicsTime ( const struct timeval & replace) { + throwError(epicsTimeFromTimeval(&ts, &replace)); } -// -// convert to ANSI C struct tm (with nano seconds) adjusted for UTC -// -epicsTime::operator gm_tm_nano_sec () const -{ - time_t_wrapper ansiTimeTicks = *this; - - gm_tm_nano_sec tm; - - int status = epicsTime_gmtime ( &ansiTimeTicks.ts, &tm.ansi_tm ); - if ( status ) { - throw std::logic_error ( "epicsTime_gmtime failed" ); - } - - tm.nSec = this->nSec; - - return tm; +epicsTime & epicsTime::operator = ( const struct timeval & replace) { + throwError(epicsTimeFromTimeval(&ts, &replace)); + return *this; } -// -// epicsTime (const local_tm_nano_sec &tm) -// -epicsTime::epicsTime (const local_tm_nano_sec &tm) +std::ostream& operator<<(std::ostream& strm, const epicsTime& ts) { - struct tm tmp = tm.ansi_tm; - time_t_wrapper ansiTimeTicks = { mktime (&tmp) }; + char temp[64]; - static const time_t mktimeError = static_cast (-1); - if (ansiTimeTicks.ts == mktimeError) { - throwWithLocation ( formatProblemWithStructTM () ); - } - - *this = epicsTime(ansiTimeTicks); - this->addNanoSec(tm.nSec); -} - -// -// epicsTime (const gm_tm_nano_sec &tm) -// - -// do conversion avoiding the timezone mechanism -static inline int is_leap(int year) -{ - if (year % 400 == 0) - return 1; - if (year % 100 == 0) - return 0; - if (year % 4 == 0) - return 1; - return 0; -} - -static inline int days_from_0(int year) -{ - year--; - return 365 * year + (year / 400) - (year / 100) + (year / 4); -} - -static inline int days_from_1970(int year) -{ - static const int days_from_0_to_1970 = days_from_0(1970); - return days_from_0(year) - days_from_0_to_1970; -} - -static inline int days_from_1jan(int year, int month, int day) -{ - static const int days[2][12] = - { - { 0,31,59,90,120,151,181,212,243,273,304,334}, - { 0,31,60,91,121,152,182,213,244,274,305,335} - }; - return days[is_leap(year)][month-1] + day - 1; -} - -epicsTime::epicsTime (const gm_tm_nano_sec &tm) -{ - int year = tm.ansi_tm.tm_year + 1900; - int month = tm.ansi_tm.tm_mon; - if (month > 11) { - year += month / 12; - month %= 12; - } else if (month < 0) { - int years_diff = (-month + 11) / 12; - year -= years_diff; - month += 12 * years_diff; - } - month++; - - int day = tm.ansi_tm.tm_mday; - int day_of_year = days_from_1jan(year, month, day); - int days_since_epoch = days_from_1970(year) + day_of_year; - - time_t_wrapper ansiTimeTicks; - ansiTimeTicks.ts = ((days_since_epoch - * 24 + tm.ansi_tm.tm_hour) - * 60 + tm.ansi_tm.tm_min) - * 60 + tm.ansi_tm.tm_sec; - - *this = epicsTime(ansiTimeTicks); - this->addNanoSec(tm.nSec); -} - -// -// operator struct timespec () -// -epicsTime::operator struct timespec () const -{ - struct timespec ts; - time_t_wrapper ansiTimeTicks; - - ansiTimeTicks = *this; - ts.tv_sec = ansiTimeTicks.ts; - ts.tv_nsec = static_cast (this->nSec); - return ts; -} - -// -// epicsTime (const struct timespec &ts) -// -epicsTime::epicsTime (const struct timespec &ts) -{ - time_t_wrapper ansiTimeTicks; - - ansiTimeTicks.ts = ts.tv_sec; - *this = epicsTime (ansiTimeTicks); - this->addNanoSec (ts.tv_nsec); -} - -// -// operator struct timeval () -// -epicsTime::operator struct timeval () const -{ - struct timeval ts; - time_t_wrapper ansiTimeTicks; - - ansiTimeTicks = *this; - // On Posix systems timeval :: tv_sec is a time_t so this can be - // a direct assignment. On other systems I dont know that we can - // guarantee that time_t and timeval :: tv_sec will have the - // same epoch or have the same scaling factor to discrete seconds. - // For example, on windows time_t changed recently to a 64 bit - // quantity but timeval is still a long. That can cause problems - // on 32 bit systems. So technically, we should have an os - // dependent conversion between time_t and timeval :: tv_sec? - ts.tv_sec = ansiTimeTicks.ts; - ts.tv_usec = static_cast < long > ( this->nSec / nSecPerUSec ); - return ts; -} - -// -// epicsTime (const struct timeval &ts) -// -epicsTime::epicsTime (const struct timeval &ts) -{ - time_t_wrapper ansiTimeTicks; - // On Posix systems timeval :: tv_sec is a time_t so this can be - // a direct assignment. On other systems I dont know that we can - // guarantee that time_t and timeval :: tv_sec will have the - // same epoch or have the same scaling factor to discrete seconds. - // For example, on windows time_t changed recently to a 64 bit - // quantity but timeval is still a long. That can cause problems - // on 32 bit systems. So technically, we should have an os - // dependent conversion between time_t and timeval :: tv_sec? - ansiTimeTicks.ts = ts.tv_sec; - *this = epicsTime (ansiTimeTicks); - this->addNanoSec (ts.tv_usec * nSecPerUSec); -} - - -static const double NTP_FRACTION_DENOMINATOR = 1.0 + 0xffffffff; - -struct l_fp { /* NTP time stamp */ - epicsUInt32 l_ui; /* sec past NTP epoch */ - epicsUInt32 l_uf; /* fractional seconds */ -}; - -// -// epicsTime::l_fp () -// -epicsTime::operator l_fp () const -{ - l_fp ts; - ts.l_ui = this->secPastEpoch + NTP_TIME_AT_EPICS_EPOCH; - ts.l_uf = static_cast < unsigned long > - ( ( this->nSec * NTP_FRACTION_DENOMINATOR ) / nSecPerSec ); - return ts; -} - -// -// epicsTime::epicsTime ( const l_fp & ts ) -// -epicsTime::epicsTime ( const l_fp & ts ) -{ - this->secPastEpoch = ts.l_ui - NTP_TIME_AT_EPICS_EPOCH; - this->nSec = static_cast < unsigned long > - ( ( ts.l_uf / NTP_FRACTION_DENOMINATOR ) * nSecPerSec ); -} - -epicsTime::operator epicsTimeStamp () const -{ - if ( this->nSec >= nSecPerSec ) { - throw std::logic_error ( - "epicsTimeStamp has overflow in nano-seconds field?" ); - } - epicsTimeStamp ts; - // - // truncation by design - // ------------------- - // epicsTime::secPastEpoch is based on ulong and has much greater range - // on 64 bit hosts than the original epicsTimeStamp::secPastEpoch. The - // epicsTimeStamp::secPastEpoch is based on epicsUInt32 so that it will - // match the original network protocol. Of course one can anticipate - // that eventually, a epicsUInt64 based network time stamp will be - // introduced when 64 bit architectures are more ubiquitous. - // - // Truncation usually works fine here because the routines in this code - // that compute time stamp differences and compare time stamps produce - // good results when the operands are on either side of a time stamp - // rollover as long as the difference between the operands does not exceed - // 1/2 of full range. - // - ts.secPastEpoch = static_cast < epicsUInt32 > ( this->secPastEpoch ); - ts.nsec = static_cast < epicsUInt32 > ( this->nSec ); - return ts; + (void)ts.strftime(temp, sizeof(temp), "%Y-%m-%d %H:%M:%S.%09f"); + temp[sizeof(temp)-1u] = '\0'; + return strm<%0f" @@ -522,7 +111,7 @@ static const char * fracFormatFind ( unsigned long & fracFmtWidth ) { assert ( prefixBufLen > 1 ); - unsigned long width = ULONG_MAX; + unsigned long width = 0xffffffff; bool fracFound = false; const char * pAfter = pFormat; const char * pFmt = pFormat; @@ -576,15 +165,14 @@ static const char * fracFormatFind ( // // size_t epicsTime::strftime () // -size_t epicsTime::strftime ( - char * pBuff, size_t bufLength, const char * pFormat ) const +size_t epicsStdCall epicsTimeToStrftime (char *pBuff, size_t bufLength, const char *pFormat, const epicsTimeStamp *pTS) { if ( bufLength == 0u ) { return 0u; } // presume that EPOCH date is an uninitialized time stamp - if ( this->secPastEpoch == 0 && this->nSec == 0u ) { + if ( pTS->secPastEpoch == 0 && pTS->nsec == 0u ) { strncpy ( pBuff, "", bufLength ); pBuff[bufLength-1] = '\0'; return strlen ( pBuff ); @@ -609,9 +197,10 @@ size_t epicsTime::strftime ( } // all but fractional seconds use strftime formatting if ( strftimePrefixBuf[0] != '\0' ) { - local_tm_nano_sec tmns = *this; + tm tm; + (void)epicsTimeToTM(&tm, 0, pTS); size_t strftimeNumChar = :: strftime ( - pBufCur, bufLenLeft, strftimePrefixBuf, & tmns.ansi_tm ); + pBufCur, bufLenLeft, strftimePrefixBuf, &tm ); pBufCur [ strftimeNumChar ] = '\0'; pBufCur += strftimeNumChar; bufLenLeft -= strftimeNumChar; @@ -625,8 +214,9 @@ size_t epicsTime::strftime ( // verify that there are enough chars left for the fractional seconds if ( fracWid < bufLenLeft ) { - local_tm_nano_sec tmns = *this; - if ( tmns.nSec < nSecPerSec ) { + tm tm; + (void)epicsTimeToTM(&tm, 0, pTS); + if ( pTS->nsec < nSecPerSec ) { // divisors for fraction (see below) static const unsigned long div[] = { static_cast < unsigned long > ( 1e9 ), @@ -641,7 +231,7 @@ size_t epicsTime::strftime ( static_cast < unsigned long > ( 1e0 ) }; // round without overflowing into whole seconds - unsigned long frac = tmns.nSec + div[fracWid] / 2; + unsigned long frac = pTS->nsec + div[fracWid] / 2; if (frac >= nSecPerSec) frac = nSecPerSec - 1; // convert nanosecs to integer of correct range @@ -691,449 +281,199 @@ size_t epicsTime::strftime ( // // epicsTime::show (unsigned) // -void epicsTime::show ( unsigned level ) const +void epicsStdCall epicsTimeShow (const epicsTimeStamp *pTS, unsigned level) { char bigBuffer[256]; - size_t numChar = this->strftime ( bigBuffer, sizeof ( bigBuffer ), - "%a %b %d %Y %H:%M:%S.%09f" ); + size_t numChar = epicsTimeToStrftime( bigBuffer, sizeof ( bigBuffer ), + "%a %b %d %Y %H:%M:%S.%09f", pTS ); if ( numChar > 0 ) { printf ( "epicsTime: %s\n", bigBuffer ); } - - if ( level > 1 ) { - // this also suppresses the "defined, but not used" - // warning message - printf ( "epicsTime: revision \"%s\"\n", - pEpicsTimeVersion ); - } - } -// -// epicsTime::operator + (const double &rhs) -// -// rhs has units seconds -// -epicsTime epicsTime::operator + (const double &rhs) const +int epicsStdCall epicsTimeToTime_t (time_t *pDest, const epicsTimeStamp *pSrc) { - unsigned long newSec, newNSec, secOffset, nSecOffset; - double fnsec; + STATIC_ASSERT(sizeof(*pDest) >= sizeof(pSrc->secPastEpoch)); - if (rhs >= 0) { - secOffset = static_cast (rhs); - fnsec = rhs - secOffset; - nSecOffset = static_cast ( (fnsec * nSecPerSec) + 0.5 ); - - newSec = this->secPastEpoch + secOffset; // overflow expected - newNSec = this->nSec + nSecOffset; - if (newNSec >= nSecPerSec) { - newSec++; // overflow expected - newNSec -= nSecPerSec; - } - } - else { - secOffset = static_cast (-rhs); - fnsec = rhs + secOffset; - nSecOffset = static_cast ( (-fnsec * nSecPerSec) + 0.5 ); - - newSec = this->secPastEpoch - secOffset; // underflow expected - if (this->nSec>=nSecOffset) { - newNSec = this->nSec - nSecOffset; - } - else { - // borrow - newSec--; // underflow expected - newNSec = this->nSec + (nSecPerSec - nSecOffset); - } - } - return epicsTime (newSec, newNSec); + // widen to 64-bit to (eventually) accomidate 64-bit time_t + *pDest = epicsUInt64(pSrc->secPastEpoch) + POSIX_TIME_AT_EPICS_EPOCH; + return epicsTimeOK; } -// -// operator - -// -// To make this code robust during timestamp rollover events -// time stamp differences greater than one half full scale are -// interpreted as rollover situations: -// -// when RHS is greater than THIS: -// RHS-THIS > one half full scale => return THIS + (ULONG_MAX-RHS) -// RHS-THIS <= one half full scale => return -(RHS-THIS) -// -// when THIS is greater than or equal to RHS -// THIS-RHS > one half full scale => return -(RHS + (ULONG_MAX-THIS)) -// THIS-RHS <= one half full scale => return THIS-RHS -// -double epicsTime::operator - (const epicsTime &rhs) const +int epicsStdCall epicsTimeFromTime_t (epicsTimeStamp *pDest, time_t src) { - double nSecRes, secRes; - - // - // first compute the difference between the nano-seconds members - // - // nano sec member is not allowed to be greater that 1/2 full scale - // so the unsigned to signed conversion is ok - // - if (this->nSec>=rhs.nSec) { - nSecRes = this->nSec - rhs.nSec; - } - else { - nSecRes = rhs.nSec - this->nSec; - nSecRes = -nSecRes; - } - - // - // next compute the difference between the seconds members - // and invert the sign of the nano seconds result if there - // is a range violation - // - if (this->secPastEpochsecPastEpoch; - if (secRes > ULONG_MAX/2) { - // - // In this situation where the difference is more than - // 68 years assume that the seconds counter has rolled - // over and compute the "wrap around" difference - // - secRes = 1 + (ULONG_MAX-secRes); - nSecRes = -nSecRes; - } - else { - secRes = -secRes; - } - } - else { - secRes = this->secPastEpoch - rhs.secPastEpoch; - if (secRes > ULONG_MAX/2) { - // - // In this situation where the difference is more than - // 68 years assume that the seconds counter has rolled - // over and compute the "wrap around" difference - // - secRes = 1 + (ULONG_MAX-secRes); - secRes = -secRes; - nSecRes = -nSecRes; - } - } - - return secRes + nSecRes/nSecPerSec; + pDest->secPastEpoch = epicsInt64(src) - POSIX_TIME_AT_EPICS_EPOCH; + pDest->nsec = 0; + return epicsTimeOK; } -// -// operator <= -// -bool epicsTime::operator <= (const epicsTime &rhs) const +int epicsStdCall epicsTimeToTM (struct tm *pDest, unsigned long *pNSecDest, const epicsTimeStamp *pSrc) { - bool rc; - - if (this->secPastEpochsecPastEpoch < ULONG_MAX/2) { - // - // In this situation where the difference is less than - // 69 years compute the expected result - // - rc = true; - } - else { - // - // In this situation where the difference is more than - // 69 years assume that the seconds counter has rolled - // over and compute the "wrap around" result - // - rc = false; - } - } - else if (this->secPastEpoch>rhs.secPastEpoch) { - if (this->secPastEpoch-rhs.secPastEpoch < ULONG_MAX/2) { - // - // In this situation where the difference is less than - // 69 years compute the expected result - // - rc = false; - } - else { - // - // In this situation where the difference is more than - // 69 years assume that the seconds counter has rolled - // over and compute the "wrap around" result - // - rc = true; - } - } - else { - if (this->nSec<=rhs.nSec) { - rc = true; - } - else { - rc = false; - } - } - return rc; + time_t temp; + int err; + err = epicsTimeToTime_t(&temp, pSrc); + if(!err) + err = epicsTime_localtime(&temp, pDest); + if(!err && pNSecDest) + *pNSecDest = pSrc->nsec; + return err; } -// -// operator < -// -bool epicsTime::operator < (const epicsTime &rhs) const +int epicsStdCall epicsTimeToGMTM (struct tm *pDest, unsigned long *pNSecDest, const epicsTimeStamp *pSrc) { - bool rc; - - if (this->secPastEpochsecPastEpoch < ULONG_MAX/2) { - // - // In this situation where the difference is less than - // 69 years compute the expected result - // - rc = true; - } - else { - // - // In this situation where the difference is more than - // 69 years assume that the seconds counter has rolled - // over and compute the "wrap around" result - // - rc = false; - } - } - else if (this->secPastEpoch>rhs.secPastEpoch) { - if (this->secPastEpoch-rhs.secPastEpoch < ULONG_MAX/2) { - // - // In this situation where the difference is less than - // 69 years compute the expected result - // - rc = false; - } - else { - // - // In this situation where the difference is more than - // 69 years assume that the seconds counter has rolled - // over and compute the "wrap around" result - // - rc = true; - } - } - else { - if (this->nSecnsec; + return err; } -extern "C" { - // - // ANSI C interface - // - // its too bad that these cant be implemented with inline functions - // at least when running the GNU compiler - // - LIBCOM_API int epicsStdCall epicsTimeToTime_t (time_t *pDest, const epicsTimeStamp *pSrc) - { - try { - time_t_wrapper dst = epicsTime (*pSrc); - *pDest = dst.ts; - } - catch (...) { - return S_time_conversion; - } - return epicsTimeOK; - } - LIBCOM_API int epicsStdCall epicsTimeFromTime_t (epicsTimeStamp *pDest, time_t src) - { - try { - time_t_wrapper dst; - dst.ts = src; - *pDest = epicsTime ( dst ); - } - catch (...) { - return S_time_conversion; - } - return epicsTimeOK; - } - LIBCOM_API int epicsStdCall epicsTimeToTM (struct tm *pDest, unsigned long *pNSecDest, const epicsTimeStamp *pSrc) - { - try { - local_tm_nano_sec tmns = epicsTime (*pSrc); - *pDest = tmns.ansi_tm; - if (pNSecDest) - *pNSecDest = tmns.nSec; - } - catch (...) { - return S_time_conversion; - } - return epicsTimeOK; - } - LIBCOM_API int epicsStdCall epicsTimeToGMTM (struct tm *pDest, unsigned long *pNSecDest, const epicsTimeStamp *pSrc) - { - try { - gm_tm_nano_sec gmtmns = epicsTime (*pSrc); - *pDest = gmtmns.ansi_tm; - if (pNSecDest) - *pNSecDest = gmtmns.nSec; - } - catch (...) { - return S_time_conversion; - } - return epicsTimeOK; - } - LIBCOM_API int epicsStdCall epicsTimeFromTM (epicsTimeStamp *pDest, const struct tm *pSrc, unsigned long nSecSrc) - { - try { - local_tm_nano_sec tmns; - tmns.ansi_tm = *pSrc; - tmns.nSec = nSecSrc; - *pDest = epicsTime (tmns); - } - catch (...) { - return S_time_conversion; - } - return epicsTimeOK; - } - LIBCOM_API int epicsStdCall epicsTimeFromGMTM (epicsTimeStamp *pDest, const struct tm *pSrc, unsigned long nSecSrc) - { - try { - gm_tm_nano_sec tmns; - tmns.ansi_tm = *pSrc; - tmns.nSec = nSecSrc; - *pDest = epicsTime (tmns); - } - catch (...) { - return S_time_conversion; - } - return epicsTimeOK; - } - LIBCOM_API int epicsStdCall epicsTimeToTimespec (struct timespec *pDest, const epicsTimeStamp *pSrc) - { - try { - *pDest = epicsTime (*pSrc); - } - catch (...) { - return S_time_conversion; - } - return epicsTimeOK; - } - LIBCOM_API int epicsStdCall epicsTimeFromTimespec (epicsTimeStamp *pDest, const struct timespec *pSrc) - { - try { - *pDest = epicsTime (*pSrc); - } - catch (...) { - return S_time_conversion; - } - return epicsTimeOK; - } - LIBCOM_API int epicsStdCall epicsTimeToTimeval (struct timeval *pDest, const epicsTimeStamp *pSrc) - { - try { - *pDest = epicsTime (*pSrc); - } - catch (...) { - return S_time_conversion; - } - return epicsTimeOK; - } - LIBCOM_API int epicsStdCall epicsTimeFromTimeval (epicsTimeStamp *pDest, const struct timeval *pSrc) - { - try { - *pDest = epicsTime (*pSrc); - } - catch (...) { - return S_time_conversion; - } - return epicsTimeOK; - } - LIBCOM_API double epicsStdCall epicsTimeDiffInSeconds (const epicsTimeStamp *pLeft, const epicsTimeStamp *pRight) - { - try { - return epicsTime (*pLeft) - epicsTime (*pRight); - } - catch (...) { - return - DBL_MAX; - } - } - LIBCOM_API void epicsStdCall epicsTimeAddSeconds (epicsTimeStamp *pDest, double seconds) - { - try { - *pDest = epicsTime (*pDest) + seconds; - } - catch ( ... ) { - *pDest = epicsTime (); - } - } - LIBCOM_API int epicsStdCall epicsTimeEqual (const epicsTimeStamp *pLeft, const epicsTimeStamp *pRight) - { - try { - return epicsTime (*pLeft) == epicsTime (*pRight); - } - catch ( ... ) { - return 0; - } - } - LIBCOM_API int epicsStdCall epicsTimeNotEqual (const epicsTimeStamp *pLeft, const epicsTimeStamp *pRight) - { - try { - return epicsTime (*pLeft) != epicsTime (*pRight); - } - catch ( ... ) { - return 1; - } - } - LIBCOM_API int epicsStdCall epicsTimeLessThan (const epicsTimeStamp *pLeft, const epicsTimeStamp *pRight) - { - try { - return epicsTime (*pLeft) < epicsTime (*pRight); - } - catch ( ... ) { - return 0; - } - } - LIBCOM_API int epicsStdCall epicsTimeLessThanEqual (const epicsTimeStamp *pLeft, const epicsTimeStamp *pRight) - { - try { - return epicsTime (*pLeft) <= epicsTime (*pRight); - } - catch ( ... ) { - return 0; - } - } - LIBCOM_API int epicsStdCall epicsTimeGreaterThan (const epicsTimeStamp *pLeft, const epicsTimeStamp *pRight) - { - try { - return epicsTime (*pLeft) > epicsTime (*pRight); - } - catch ( ... ) { - return 0; - } - } - LIBCOM_API int epicsStdCall epicsTimeGreaterThanEqual (const epicsTimeStamp *pLeft, const epicsTimeStamp *pRight) - { - try { - return epicsTime (*pLeft) >= epicsTime (*pRight); - } - catch ( ... ) { - return 0; - } - } - LIBCOM_API size_t epicsStdCall epicsTimeToStrftime (char *pBuff, size_t bufLength, const char *pFormat, const epicsTimeStamp *pTS) - { - try { - return epicsTime(*pTS).strftime (pBuff, bufLength, pFormat); - } - catch ( ... ) { - return 0; - } - } - LIBCOM_API void epicsStdCall epicsTimeShow (const epicsTimeStamp *pTS, unsigned interestLevel) - { - try { - epicsTime(*pTS).show (interestLevel); - } - catch ( ... ) { - printf ( "Invalid epicsTimeStamp\n" ); - } - } +int epicsStdCall epicsTimeFromTM (epicsTimeStamp *pDest, const struct tm *pSrc, unsigned long nSecSrc) +{ + tm temp = *pSrc; // mktime() modifies (at least) tm_wday and tm_yday + time_t tsrc = mktime(&temp); + int err = epicsTimeFromTime_t(pDest, tsrc); + if(!err) + pDest->nsec = nSecSrc; + return err; +} + +#ifdef _WIN32 +# define timegm _mkgmtime + +#elif defined(__rtems__) || defined(vxWorks) + +static +time_t timegm(tm* gtm) +{ + // ugly hack for targets without timegm(tm*), but which have mktime(tm*). + // probably has issues near start/end of DST + + // translate to seconds as if a local time. off by TZ offset + time_t fakelocal = mktime(gtm); + // now use gmtime() which applies the TZ offset again, but with the wrong sign + tm wrongtm; + epicsTime_gmtime(&fakelocal, &wrongtm); + // translate this to seconds + time_t fakex2 = mktime(&wrongtm); + + // tzoffset = fakelocal - fakex2; + + return epicsInt64(fakelocal)*2 - fakex2; +} + +#endif + +int epicsStdCall epicsTimeFromGMTM (epicsTimeStamp *pDest, const struct tm *pSrc, unsigned long nSecSrc) +{ + tm temp = *pSrc; // timegm() may modify (at least) tm_wday and tm_yday + time_t tsrc = timegm(&temp); + int err = epicsTimeFromTime_t(pDest, tsrc); + if(!err) + pDest->nsec = nSecSrc; + return err; +} + +int epicsStdCall epicsTimeToTimespec (struct timespec *pDest, const epicsTimeStamp *pSrc) +{ + int err = epicsTimeToTime_t(&pDest->tv_sec, pSrc); + if(!err) + pDest->tv_nsec = pSrc->nsec; + return err; +} + +int epicsStdCall epicsTimeFromTimespec (epicsTimeStamp *pDest, const struct timespec *pSrc) +{ + int err = epicsTimeFromTime_t(pDest, pSrc->tv_sec); + if(!err) + pDest->nsec = pSrc->tv_nsec; + return err; +} + +int epicsStdCall epicsTimeToTimeval (struct timeval *pDest, const epicsTimeStamp *pSrc) +{ + time_t temp; + int err = epicsTimeToTime_t(&temp, pSrc); + if(!err) { + pDest->tv_sec = temp; // tv_sec is not time_t on windows + pDest->tv_usec = pSrc->nsec/1000u; + } + return err; +} + +int epicsStdCall epicsTimeFromTimeval (epicsTimeStamp *pDest, const struct timeval *pSrc) +{ + int err = epicsTimeFromTime_t(pDest, pSrc->tv_sec); + if(!err) + pDest->nsec = pSrc->tv_usec*1000u; + return err; +} + +double epicsStdCall epicsTimeDiffInSeconds (const epicsTimeStamp *pLeft, const epicsTimeStamp *pRight) +{ + /* double(*pLeft - *pRight) + * + * 0xffffffff*1000000000 < 2**62 + * 0x200000000*1000000002 < 2**63 + * so there is (just barely) space to add 2 TSs as signed 64-bit integers without overflow + */ + + // handle over/underflow as u32 when subtracting + epicsInt64 nsec = epicsInt32(pLeft->secPastEpoch - pRight->secPastEpoch); + nsec *= nSecPerSec; + nsec += epicsInt32(pLeft->nsec) - epicsInt32(pRight->nsec); + + return double(nsec)*1e-9; +} + +void epicsStdCall epicsTimeAddSeconds (epicsTimeStamp *pDest, double seconds) +{ + epicsInt64 nsec = pDest->secPastEpoch; + nsec *= nSecPerSec; + nsec += epicsInt64(pDest->nsec); + nsec += epicsInt64(seconds*1e9 + (seconds>=0.0 ? 0.5 : -0.5)); + pDest->secPastEpoch = nsec/nSecPerSec; + pDest->nsec = (nsec>=0 ? nsec : -nsec)%nSecPerSec; +} + +int epicsStdCall epicsTimeEqual (const epicsTimeStamp *pLeft, const epicsTimeStamp *pRight) +{ + return pLeft->secPastEpoch == pRight->secPastEpoch && pLeft->nsec == pRight->nsec; +} + +int epicsStdCall epicsTimeNotEqual (const epicsTimeStamp *pLeft, const epicsTimeStamp *pRight) +{ + return !epicsTimeEqual(pLeft, pRight); +} + +epicsInt64 epicsStdCall epicsTimeDiffInNS (const epicsTimeStamp *pLeft, const epicsTimeStamp *pRight) +{ + epicsInt64 delta = epicsInt64(pLeft->secPastEpoch) - pRight->secPastEpoch; + delta *= nSecPerSec; + delta += epicsInt64(pLeft->nsec) - pRight->nsec; + return delta; +} + +int epicsStdCall epicsTimeLessThan (const epicsTimeStamp *pLeft, const epicsTimeStamp *pRight) +{ + return epicsTimeDiffInNS(pLeft, pRight) < 0; +} + +int epicsStdCall epicsTimeLessThanEqual (const epicsTimeStamp *pLeft, const epicsTimeStamp *pRight) +{ + return epicsTimeDiffInNS(pLeft, pRight) <= 0; +} + +int epicsStdCall epicsTimeGreaterThan (const epicsTimeStamp *pLeft, const epicsTimeStamp *pRight) +{ + return epicsTimeDiffInNS(pLeft, pRight) > 0; +} + +int epicsStdCall epicsTimeGreaterThanEqual (const epicsTimeStamp *pLeft, const epicsTimeStamp *pRight) +{ + return epicsTimeDiffInNS(pLeft, pRight) >= 0; } diff --git a/modules/libcom/src/osi/epicsTime.h b/modules/libcom/src/osi/epicsTime.h index 090fb5c48..8996c3733 100644 --- a/modules/libcom/src/osi/epicsTime.h +++ b/modules/libcom/src/osi/epicsTime.h @@ -26,6 +26,14 @@ /** \brief The EPICS Epoch is 00:00:00 Jan 1, 1990 UTC */ #define POSIX_TIME_AT_EPICS_EPOCH 631152000u +#ifdef __cplusplus + +#include +#include + +extern "C" { +#endif + /** \brief EPICS time stamp, for use from C code. * * Because it uses an unsigned 32-bit integer to hold the seconds count, an @@ -65,277 +73,6 @@ struct timespec; /* POSIX real time */ */ struct timeval; /* BSD */ -/** \struct l_fp - * \brief Network Time Protocol timestamp - * - * Network Time Protocol timestamp. The fields are: - * \li \c lui - Number of seconds since 1900 (The NTP epoch) - * \li \c luf - Fraction of a second. For example 0x800000000 represents 1/2 second. - */ -struct l_fp; /* NTP timestamp */ - -#ifdef __cplusplus - -/** \brief C++ only ANSI C struct tm with nanoseconds, local timezone - * - * Extend ANSI C "struct tm" to include nano seconds within a second - * and a struct tm that is adjusted for the local timezone. - */ -struct local_tm_nano_sec { - struct tm ansi_tm; /**< \brief ANSI C time details */ - unsigned long nSec; /**< \brief nanoseconds extension */ -}; - -/** \brief C++ only ANSI C sruct tm with nanoseconds, UTC - * - * Extend ANSI C "struct tm" to include nanoseconds within a second - * and a struct tm that is adjusted for GMT (UTC). - */ -struct gm_tm_nano_sec { - struct tm ansi_tm; /**< \brief ANSI C time details */ - unsigned long nSec; /**< \brief nanoseconds extension */ -}; - -/** \brief C++ only ANSI C time_t - * - * This is for converting to/from the ANSI C \c time_t. Since \c time_t - * is usually an elementary type providing a conversion operator from - * \c time_t to/from epicsTime could cause undesirable implicit - * conversions. Providing a conversion operator to/from the - * \c time_t_wrapper instead prevents implicit conversions. - */ -struct time_t_wrapper { - time_t ts; -}; - -/** \brief C++ Event number wrapper class - * - * Stores an event number for use by the epicsTime::getEvent() static - * class method. - */ -class LIBCOM_API epicsTimeEvent -{ -public: - epicsTimeEvent (const int &number); /**< \brief Constructor */ - operator int () const; /**< \brief Extractor */ -private: - int eventNumber; -}; - -/** \brief C++ time stamp object - * - * Holds an EPICS time stamp, and provides conversion functions for both - * input and output from/to other types. - * - * \note Time conversions: The epicsTime implementation will properly - * convert between the various formats from the beginning of the EPICS - * epoch until at least 2038. Unless the underlying architecture support - * has defective POSIX, BSD/SRV5, or standard C time support the EPICS - * implementation should be valid until 2106. - */ -class LIBCOM_API epicsTime -{ -public: - /// \brief Exception: Time provider problem - class unableToFetchCurrentTime {}; - /// \brief Exception: Bad field(s) in struct tm - class formatProblemWithStructTM {}; - - /** \brief The default constructor sets the time to the EPICS epoch. */ - epicsTime (); - - /** \brief Get time of event system event. - * - * Returns an epicsTime indicating when the associated event system - * event last occurred. - */ - static epicsTime getEvent ( const epicsTimeEvent & ); - /** \brief Get current clock time - * - * Returns an epicsTime containing the current time. For example: - * \code{.cpp} - * epicsTime now = epicsTime::getCurrent(); - * \endcode - */ - static epicsTime getCurrent (); - /** \brief Get current monotonic time - * - * Returns an epicsTime containing the current monotonic time, an - * OS clock which never going backwards or jumping forwards. - * This time is has an undefined epoch, and is only useful for - * measuring time differences. - */ - static epicsTime getMonotonic (); - - /** \name epicsTimeStamp conversions - * Convert to and from EPICS epicsTimeStamp format - * @{ */ - /** \brief Convert to epicsTimeStamp */ - operator epicsTimeStamp () const; - /** \brief Construct from epicsTimeStamp */ - epicsTime ( const epicsTimeStamp & ts ); - /** \brief Assign from epicsTimeStamp */ - epicsTime & operator = ( const epicsTimeStamp & ); - /** @} */ - - /** \name ANSI C time_t conversions - * Convert to and from ANSI C \c time_t wrapper . - * @{ */ - /** \brief Convert to ANSI C \c time_t */ - operator time_t_wrapper () const; - /** \brief Construct from ANSI C \c time_t */ - epicsTime ( const time_t_wrapper & ); - /** \brief Assign from ANSI C \c time_t */ - epicsTime & operator = ( const time_t_wrapper & ); - /** @} */ - - /** \name ANSI C struct tm local-time conversions - * Convert to and from ANSI Cs struct tm (with nano seconds), - * adjusted for the local time zone. - * @{ */ - /** \brief Convert to struct tm in local time zone */ - operator local_tm_nano_sec () const; - /** \brief Construct from struct tm in local time zone */ - epicsTime ( const local_tm_nano_sec & ); - /** \brief Assign from struct tm in local time zone */ - epicsTime & operator = ( const local_tm_nano_sec & ); - /** @} */ - - /** \name ANSI C struct tm UTC conversions - * Convert to and from ANSI Cs struct tm (with nano seconds), - * adjusted for Greenwich Mean Time (UTC). - * @{ */ - /** \brief Convert to struct tm in UTC/GMT */ - operator gm_tm_nano_sec () const; - /** \brief Construct from struct tm in UTC/GMT */ - epicsTime ( const gm_tm_nano_sec & ); - /** \brief Assign from struct tm in UTC */ - epicsTime & operator = ( const gm_tm_nano_sec & ); - /** @} */ - - /** \name POSIX RT struct timespec conversions - * Convert to and from the POSIX RealTime struct timespec - * format. - * @{ */ - /** \brief Convert to struct timespec */ - operator struct timespec () const; - /** \brief Construct from struct timespec */ - epicsTime ( const struct timespec & ); - /** \brief Assign from struct timespec */ - epicsTime & operator = ( const struct timespec & ); - /** @} */ - - /** \name BSD's struct timeval conversions - * Convert to and from the BSD struct timeval format. - * @{ */ - /** \brief Convert to struct timeval */ - operator struct timeval () const; - /** \brief Construct from struct timeval */ - epicsTime ( const struct timeval & ); - /** \brief Assign from struct timeval */ - epicsTime & operator = ( const struct timeval & ); - /** @} */ - - /** \name NTP timestamp conversions - * Convert to and from the NTP timestamp structure \c l_fp - * @{ */ - /** \brief Convert to NTP format */ - operator l_fp () const; - /** \brief Construct from NTP format */ - epicsTime ( const l_fp & ); - /** \brief Assign from NTP format */ - epicsTime & operator = ( const l_fp & ); - /** @} */ - - /** \name WIN32 FILETIME conversions - * Convert to and from WIN32s _FILETIME - * \note These are only implemented on Windows targets. - * @{ */ - /** \brief Convert to Windows struct _FILETIME */ - operator struct _FILETIME () const; - /** \brief Constuct from Windows struct _FILETIME */ - epicsTime ( const struct _FILETIME & ); - /** \brief Assign from Windows struct _FILETIME */ - epicsTime & operator = ( const struct _FILETIME & ); - /** @} */ - - /** \name Arithmetic operators - * Standard operators involving epicsTime objects and time differences - * which are always expressed as a \c double in seconds. - * @{ */ - /// \brief \p lhs minus \p rhs, in seconds - double operator- ( const epicsTime & ) const; - /// \brief \p lhs plus rhs seconds - epicsTime operator+ ( const double & ) const; - /// \brief \p lhs minus rhs seconds - epicsTime operator- ( const double & ) const; - /// \brief add rhs seconds to \p lhs - epicsTime operator+= ( const double & ); - /// \brief subtract rhs seconds from \p lhs - epicsTime operator-= ( const double & ); - /** @} */ - - /** \name Comparison operators - * Standard comparisons between epicsTime objects. - * @{ */ - /// \brief \p lhs equals \p rhs - bool operator == ( const epicsTime & ) const; - /// \brief \p lhs not equal to \p rhs - bool operator != ( const epicsTime & ) const; - /// \brief \p rhs no later than \p lhs - bool operator <= ( const epicsTime & ) const; - /// \brief \p lhs was before \p rhs - bool operator < ( const epicsTime & ) const; - /// \brief \p rhs not before \p lhs - bool operator >= ( const epicsTime & ) const; - /// \brief \p lhs was after \p rhs - bool operator > ( const epicsTime & ) const; - /** @} */ - - /** \brief Convert to string in user-specified format - * - * This method extends the standard C library routine strftime(). - * See your OS documentation for details about the standard routine. - * The epicsTime method adds support for printing the fractional - * portion of the time. It searches the format string for the - * sequence %0nf where \a n is the desired precision, - * and uses this format to convert the fractional seconds with the - * requested precision. For example: - * \code{.cpp} - * epicsTime time = epicsTime::getCurrent(); - * char buf[30]; - * time.strftime(buf, 30, "%Y-%m-%d %H:%M:%S.%06f"); - * printf("%s\n", buf); - * \endcode - * This will print the current time in the format: - * \code - * 2001-01-26 20:50:29.813505 - * \endcode - */ - size_t strftime ( char * pBuff, size_t bufLength, const char * pFormat ) const; - - /** \brief Dump current state to standard out */ - void show ( unsigned interestLevel ) const; - -private: - /* - * private because: - * a) application does not break when EPICS epoch is changed - * b) no assumptions about internal storage or internal precision - * in the application - * c) it would be easy to forget which argument is nanoseconds - * and which argument is seconds (no help from compiler) - */ - epicsTime ( const unsigned long secPastEpoch, const unsigned long nSec ); - void addNanoSec ( long nanoSecAdjust ); - - unsigned long secPastEpoch; /* seconds since O000 Jan 1, 1990 */ - unsigned long nSec; /* nanoseconds within second */ -}; - -extern "C" { -#endif /* __cplusplus */ - /** \name Return status values * epicsTime routines return \c S_time_ error status values: * @{ @@ -457,6 +194,12 @@ LIBCOM_API void epicsStdCall epicsTimeAddSeconds ( epicsTimeStamp * pDest, double secondsToAdd ); /* adds seconds to *pDest */ /** @} */ +/** \brief Return difference LHS-RHS as signed integer nanoseconds. + * @since UNRELEASED + */ +LIBCOM_API +epicsInt64 epicsStdCall epicsTimeDiffInNS (const epicsTimeStamp *pLeft, const epicsTimeStamp *pRight); + /** \name Comparison operators * Comparisons between epicsTimeStamp objects, returning 0=false, 1=true. * @{ */ @@ -515,98 +258,330 @@ LIBCOM_API void osdMonotonicInit(void); #endif #ifdef __cplusplus -} +} // extern "C" + +/** \brief C++ only ANSI C struct tm with nanoseconds, local timezone + * + * Extend ANSI C "struct tm" to include nano seconds within a second + * and a struct tm that is adjusted for the local timezone. + */ +struct local_tm_nano_sec { + struct tm ansi_tm; /**< \brief ANSI C time details */ + unsigned long nSec; /**< \brief nanoseconds extension */ +}; + +/** \brief C++ only ANSI C sruct tm with nanoseconds, UTC + * + * Extend ANSI C "struct tm" to include nanoseconds within a second + * and a struct tm that is adjusted for GMT (UTC). + */ +struct gm_tm_nano_sec { + struct tm ansi_tm; /**< \brief ANSI C time details */ + unsigned long nSec; /**< \brief nanoseconds extension */ +}; + +/** \brief C++ only ANSI C time_t + * + * This is for converting to/from the ANSI C \c time_t. Since \c time_t + * is usually an elementary type providing a conversion operator from + * \c time_t to/from epicsTime could cause undesirable implicit + * conversions. Providing a conversion operator to/from the + * \c time_t_wrapper instead prevents implicit conversions. + */ +struct time_t_wrapper { + time_t ts; +}; + +/** \brief C++ Event number wrapper class + * + * Stores an event number for use by the epicsTime::getEvent() static + * class method. + */ +class LIBCOM_API epicsTimeEvent +{ +public: + epicsTimeEvent (const int &number) :eventNumber(number) {} + operator int () const { return eventNumber; } +private: + int eventNumber; +}; + +/** \brief C++ time stamp object + * + * Holds an EPICS time stamp, and provides conversion functions for both + * input and output from/to other types. + * + * \note Time conversions: The epicsTime implementation will properly + * convert between the various formats from the beginning of the EPICS + * epoch until at least 2038. Unless the underlying architecture support + * has defective POSIX, BSD/SRV5, or standard C time support the EPICS + * implementation should be valid until 2106. + */ +class LIBCOM_API epicsTime +{ + // translate S_time_* code to exception + static void throwError(int code); +public: + /// \brief Exception: Time provider problem + typedef std::runtime_error unableToFetchCurrentTime; + /// \brief Exception: Bad field(s) in struct tm + typedef std::logic_error formatProblemWithStructTM; + + /** \brief The default constructor sets the time to the EPICS epoch. */ +#if __cplusplus>=201103L + constexpr epicsTime() :ts{} {} +#else + epicsTime () { + ts.secPastEpoch = ts.nsec = 0u; + } +#endif + + /** \brief Get time of event system event. + * + * Returns an epicsTime indicating when the associated event system + * event last occurred. + */ + static inline epicsTime getEvent ( const epicsTimeEvent & evt) ; + /** \brief Get current clock time + * + * Returns an epicsTime containing the current time. For example: + * \code{.cpp} + * epicsTime now = epicsTime::getCurrent(); + * \endcode + */ + static epicsTime getCurrent (); + /** \brief Get current monotonic time + * + * Returns an epicsTime containing the current monotonic time, an + * OS clock which never going backwards or jumping forwards. + * This time is has an undefined epoch, and is only useful for + * measuring time differences. + */ + static epicsTime getMonotonic () { + epicsTime ret; + epicsTimeGetMonotonic(&ret.ts); // can't fail + return ret; + } + + /** \name epicsTimeStamp conversions + * Convert to and from EPICS epicsTimeStamp format + * @{ */ + /** \brief Convert to epicsTimeStamp */ + operator const epicsTimeStamp& () const { return ts; } + /** \brief Construct from epicsTimeStamp */ + epicsTime ( const epicsTimeStamp & replace ); + /** \brief Assign from epicsTimeStamp */ + epicsTime & operator = ( const epicsTimeStamp & replace) { + ts = replace; + return *this; + } + /** @} */ + + /** \name ANSI C time_t conversions + * Convert to and from ANSI C \c time_t wrapper . + * @{ */ + /** \brief Convert to ANSI C \c time_t */ + operator time_t_wrapper () const { + time_t_wrapper ret; + throwError(epicsTimeToTime_t(&ret.ts, &ts)); + return ret; + } + /** \brief Construct from ANSI C \c time_t */ + epicsTime ( const time_t_wrapper & replace ) { + throwError(epicsTimeFromTime_t(&ts, replace.ts)); + } + /** \brief Assign from ANSI C \c time_t */ + epicsTime & operator = ( const time_t_wrapper & replace) { + throwError(epicsTimeFromTime_t(&ts, replace.ts)); + return *this; + } + /** @} */ + + /** \name ANSI C struct tm local-time conversions + * Convert to and from ANSI Cs struct tm (with nano seconds), + * adjusted for the local time zone. + * @{ */ + /** \brief Convert to struct tm in local time zone */ + operator local_tm_nano_sec () const { + local_tm_nano_sec ret; + throwError(epicsTimeToTM(&ret.ansi_tm, 0, &ts)); + ret.nSec = ts.nsec; + return ret; + } + /** \brief Construct from struct tm in local time zone */ + epicsTime ( const local_tm_nano_sec & replace) { + throwError(epicsTimeFromTM(&ts, &replace.ansi_tm, replace.nSec)); + } + /** \brief Assign from struct tm in local time zone */ + epicsTime & operator = ( const local_tm_nano_sec & replace) { + throwError(epicsTimeFromTM(&ts, &replace.ansi_tm, replace.nSec)); + return *this; + } + /** @} */ + + /** \name ANSI C struct tm UTC conversions + * Convert to and from ANSI Cs struct tm (with nano seconds), + * adjusted for Greenwich Mean Time (UTC). + * @{ */ + /** \brief Convert to struct tm in UTC/GMT */ + operator gm_tm_nano_sec () const { + gm_tm_nano_sec ret; + throwError(epicsTimeToGMTM(&ret.ansi_tm, 0, &ts)); + ret.nSec = ts.nsec; + return ret; + } + /** \brief Construct from struct tm in UTC/GMT */ + epicsTime ( const gm_tm_nano_sec & replace) { + throwError(epicsTimeFromGMTM(&ts, &replace.ansi_tm, replace.nSec)); + } + /** \brief Assign from struct tm in UTC */ + epicsTime & operator = ( const gm_tm_nano_sec & replace) { + throwError(epicsTimeFromGMTM(&ts, &replace.ansi_tm, replace.nSec)); + return *this; + } + /** @} */ + + /** \name POSIX RT struct timespec conversions + * Convert to and from the POSIX RealTime struct timespec + * format. + * @{ */ + /** \brief Convert to struct timespec */ + operator struct timespec () const { + timespec ret; + epicsTimeToTimespec(&ret, &ts); + return ret; + } + /** \brief Construct from struct timespec */ + epicsTime ( const struct timespec & replace) { + throwError(epicsTimeFromTimespec(&ts, &replace)); + } + /** \brief Assign from struct timespec */ + epicsTime & operator = ( const struct timespec & replace ) { + throwError(epicsTimeFromTimespec(&ts, &replace)); + return *this; + } + /** @} */ + + /** \name BSD's struct timeval conversions + * Convert to and from the BSD struct timeval format. + * @{ */ + /** \brief Convert to struct timeval */ + operator struct timeval () const ; + /** \brief Construct from struct timeval */ + epicsTime ( const struct timeval & replace); + /** \brief Assign from struct timeval */ + epicsTime & operator = ( const struct timeval & replace); + /** @} */ + +#ifdef _WIN32 + /** \name WIN32 FILETIME conversions + * Convert to and from WIN32s _FILETIME + * \note These are only implemented on Windows targets. + * @{ */ + /** \brief Convert to Windows struct _FILETIME */ + operator struct _FILETIME () const; + /** \brief Constuct from Windows struct _FILETIME */ + epicsTime ( const struct _FILETIME & ); + /** \brief Assign from Windows struct _FILETIME */ + epicsTime & operator = ( const struct _FILETIME & ); + /** @} */ +#endif /* _WIN32 */ + + /** \name Arithmetic operators + * Standard operators involving epicsTime objects and time differences + * which are always expressed as a \c double in seconds. + * @{ */ + /// \brief \p lhs minus \p rhs, in seconds + double operator- ( const epicsTime & other) const { + return epicsTimeDiffInSeconds(&ts, &other.ts); + } + /// \brief \p lhs plus rhs seconds + epicsTime operator+ (double delta) const { + epicsTime ret(*this); + epicsTimeAddSeconds(&ret.ts, delta); + return ret; + } + /// \brief \p lhs minus rhs seconds + epicsTime operator- (double delta ) const { + return (*this)+(-delta); + } + /// \brief add rhs seconds to \p lhs + epicsTime operator+= (double delta) { + epicsTimeAddSeconds(&ts, delta); + return *this; + } + /// \brief subtract rhs seconds from \p lhs + epicsTime operator-= ( double delta ) { + return (*this) += (-delta); + } + /** @} */ + + /** \name Comparison operators + * Standard comparisons between epicsTime objects. + * @{ */ + /// \brief \p lhs equals \p rhs + bool operator == ( const epicsTime & other) const { + return epicsTimeEqual(&ts, &other.ts); + } + /// \brief \p lhs not equal to \p rhs + bool operator != ( const epicsTime & other) const { + return epicsTimeNotEqual(&ts, &other.ts); + } + /// \brief \p rhs no later than \p lhs + bool operator <= ( const epicsTime & other) const { + return epicsTimeLessThanEqual(&ts, &other.ts); + } + /// \brief \p lhs was before \p rhs + bool operator < ( const epicsTime & other) const { + return epicsTimeLessThan(&ts, &other.ts); + } + /// \brief \p rhs not before \p lhs + bool operator >= ( const epicsTime & other) const { + return epicsTimeGreaterThanEqual(&ts, &other.ts); + } + /// \brief \p lhs was after \p rhs + bool operator > ( const epicsTime & other) const { + return epicsTimeGreaterThan(&ts, &other.ts); + } + /** @} */ + + /** \brief Convert to string in user-specified format + * + * This method extends the standard C library routine strftime(). + * See your OS documentation for details about the standard routine. + * The epicsTime method adds support for printing the fractional + * portion of the time. It searches the format string for the + * sequence %0nf where \a n is the desired precision, + * and uses this format to convert the fractional seconds with the + * requested precision. For example: + * \code{.cpp} + * epicsTime time = epicsTime::getCurrent(); + * char buf[30]; + * time.strftime(buf, 30, "%Y-%m-%d %H:%M:%S.%06f"); + * printf("%s\n", buf); + * \endcode + * This will print the current time in the format: + * \code + * 2001-01-26 20:50:29.813505 + * \endcode + */ + size_t strftime ( char * pBuff, size_t bufLength, const char * pFormat ) const { + return epicsTimeToStrftime(pBuff, bufLength, pFormat, &ts); + } + + /** \brief Dump current state to standard out */ + void show ( unsigned interestLevel ) const { + epicsTimeShow(&ts, interestLevel); + } + +private: + epicsTimeStamp ts; +}; + +LIBCOM_API +std::ostream& operator<<(std::ostream& strm, const epicsTime& ts); + #endif /* __cplusplus */ -/* inline member functions ,*/ -#ifdef __cplusplus - -/* epicsTimeEvent */ - -inline epicsTimeEvent::epicsTimeEvent (const int &number) : - eventNumber(number) {} - -inline epicsTimeEvent::operator int () const -{ - return this->eventNumber; -} - - -/* epicsTime */ - -inline epicsTime epicsTime::operator - ( const double & rhs ) const -{ - return epicsTime::operator + ( -rhs ); -} - -inline epicsTime epicsTime::operator += ( const double & rhs ) -{ - *this = epicsTime::operator + ( rhs ); - return *this; -} - -inline epicsTime epicsTime::operator -= ( const double & rhs ) -{ - *this = epicsTime::operator + ( -rhs ); - return *this; -} - -inline bool epicsTime::operator == ( const epicsTime & rhs ) const -{ - if ( this->secPastEpoch == rhs.secPastEpoch && this->nSec == rhs.nSec ) { - return true; - } - return false; -} - -inline bool epicsTime::operator != ( const epicsTime & rhs ) const -{ - return !epicsTime::operator == ( rhs ); -} - -inline bool epicsTime::operator >= ( const epicsTime & rhs ) const -{ - return ! ( *this < rhs ); -} - -inline bool epicsTime::operator > ( const epicsTime & rhs ) const -{ - return ! ( *this <= rhs ); -} - -inline epicsTime & epicsTime::operator = ( const local_tm_nano_sec & rhs ) -{ - return *this = epicsTime ( rhs ); -} - -inline epicsTime & epicsTime::operator = ( const gm_tm_nano_sec & rhs ) -{ - return *this = epicsTime ( rhs ); -} - -inline epicsTime & epicsTime::operator = ( const struct timespec & rhs ) -{ - *this = epicsTime ( rhs ); - return *this; -} - -inline epicsTime & epicsTime::operator = ( const epicsTimeStamp & rhs ) -{ - *this = epicsTime ( rhs ); - return *this; -} - -inline epicsTime & epicsTime::operator = ( const l_fp & rhs ) -{ - *this = epicsTime ( rhs ); - return *this; -} - -inline epicsTime & epicsTime::operator = ( const time_t_wrapper & rhs ) -{ - *this = epicsTime ( rhs ); - return *this; -} -#endif /* __cplusplus */ #endif /* epicsTimehInclude */ diff --git a/modules/libcom/src/osi/os/WIN32/osdTime.cpp b/modules/libcom/src/osi/os/WIN32/osdTime.cpp index fb2cfb6c0..e2c3efbfe 100644 --- a/modules/libcom/src/osi/os/WIN32/osdTime.cpp +++ b/modules/libcom/src/osi/os/WIN32/osdTime.cpp @@ -510,8 +510,8 @@ static unsigned __stdcall _pllThreadEntry ( void * pCurrentTimeIn ) epicsTime::operator FILETIME () const { LARGE_INTEGER ftTicks; - ftTicks.QuadPart = ( this->secPastEpoch * FILE_TIME_TICKS_PER_SEC ) + - ( this->nSec / ET_TICKS_PER_FT_TICK ); + ftTicks.QuadPart = ( this->ts.secPastEpoch * FILE_TIME_TICKS_PER_SEC ) + + ( this->ts.nsec / ET_TICKS_PER_FT_TICK ); ftTicks.QuadPart += epicsEpochInFileTime; FILETIME ts; ts.dwLowDateTime = ftTicks.LowPart; @@ -527,15 +527,15 @@ epicsTime::epicsTime ( const FILETIME & ts ) if ( lift.QuadPart > epicsEpochInFileTime ) { LONGLONG fileTimeTicksSinceEpochEPICS = lift.QuadPart - epicsEpochInFileTime; - this->secPastEpoch = static_cast < epicsUInt32 > + this->ts.secPastEpoch = static_cast < epicsUInt32 > ( fileTimeTicksSinceEpochEPICS / FILE_TIME_TICKS_PER_SEC ); - this->nSec = static_cast < epicsUInt32 > + this->ts.nsec = static_cast < epicsUInt32 > ( ( fileTimeTicksSinceEpochEPICS % FILE_TIME_TICKS_PER_SEC ) * ET_TICKS_PER_FT_TICK ); } else { - this->secPastEpoch = 0; - this->nSec = 0; + this->ts.secPastEpoch = 0; + this->ts.nsec = 0; } } diff --git a/modules/libcom/test/epicsTimeTest.cpp b/modules/libcom/test/epicsTimeTest.cpp index 3e10e6808..66d13e91d 100644 --- a/modules/libcom/test/epicsTimeTest.cpp +++ b/modules/libcom/test/epicsTimeTest.cpp @@ -10,6 +10,9 @@ /* * Authors: Jeff Hill, Marty Kraimer and Andrew Johnson */ +#include + +#include #include #include #include @@ -17,6 +20,7 @@ #include #include +#include "envDefs.h" #include "epicsTime.h" #include "epicsThread.h" #include "errlog.h" @@ -29,16 +33,45 @@ using namespace std; * routines is incorporated into epicsTimeTest () below. */ -struct l_fp { /* NTP time stamp */ - epicsUInt32 l_ui; /* sec past NTP epoch */ - epicsUInt32 l_uf; /* fractional seconds */ -}; - static const unsigned mSecPerSec = 1000u; static const unsigned uSecPerSec = 1000u * mSecPerSec; static const unsigned nSecPerSec = 1000u * uSecPerSec; static const double precisionEPICS = 1.0 / nSecPerSec; +static void testAdd(epicsUInt32 lhsSec, epicsUInt32 lhsNS, + double rhs, + epicsUInt32 expectSec, epicsUInt32 expectNS) +{ + epicsTimeStamp lhs = {lhsSec, lhsNS}; + epicsTimeStamp expect = {expectSec, expectNS}; + epicsTimeStamp actual = lhs; + + + epicsTimeAddSeconds(&actual, rhs); + testOk(epicsTimeEqual(&actual, &expect), + "testAdd(%u:%u + %.9f -> %u:%u == %u:%u)", + unsigned(lhs.secPastEpoch), unsigned(lhs.nsec), + rhs, + unsigned(actual.secPastEpoch), unsigned(actual.nsec), + unsigned(expect.secPastEpoch), unsigned(expect.nsec)); +} + +static void testDiff(epicsUInt32 lhsSec, epicsUInt32 lhsNS, + epicsUInt32 rhsSec, epicsUInt32 rhsNS, + double expect) +{ + epicsTimeStamp lhs = {lhsSec, lhsNS}; + epicsTimeStamp rhs = {rhsSec, rhsNS}; + double actual = epicsTimeDiffInSeconds(&lhs, &rhs); + double diff = actual - expect; + + testOk(fabs(diff) %.9f ~= %.9f (%g)", + unsigned(lhs.secPastEpoch), unsigned(lhs.nsec), + unsigned(rhs.secPastEpoch), unsigned(rhs.nsec), + actual, expect, diff); +} + static void crossCheck(double delay) { double mindelta = 2*epicsMonotonicResolution()*1e-9, @@ -80,12 +113,91 @@ static void testMonotonic() testDiag("Small Delta %u ns", (unsigned)(B-A)); } +static void testTMGames() +{ + testDiag("testTMGames()"); + + epicsTimeStamp now; + testOk1(!epicsTimeGetCurrent(&now)); + now.nsec = 0; // not relevant + + tm gtm, ltm; + epicsTimeToTM(<m, 0, &now); + epicsTimeToGMTM(>m, 0, &now); + + // we can't do any tests on the decomposed time without knowing the current TZ + testDiag("LTM mday=%u hour=%u min=%u sec=%u", ltm.tm_mday, ltm.tm_hour, ltm.tm_min, ltm.tm_sec); + testDiag("GTM mday=%u hour=%u min=%u sec=%u", gtm.tm_mday, gtm.tm_hour, gtm.tm_min, gtm.tm_sec); + + epicsTimeStamp gtime, ltime; + epicsTimeFromTM(<ime, <m, 0); + epicsTimeFromGMTM(>ime, >m, 0); + + testOk(now.secPastEpoch==ltime.secPastEpoch, "localtime %u == %u", + unsigned(now.secPastEpoch), unsigned(ltime.secPastEpoch)); + + testOk(now.secPastEpoch==gtime.secPastEpoch, "gmtime %u == %u", + unsigned(now.secPastEpoch), unsigned(ltime.secPastEpoch)); +} + MAIN(epicsTimeTest) { const int wasteTime = 100000; const int nTimes = 10; - testPlan(22 + nTimes * 19); + testPlan(52 + nTimes * 19); + + testDiag("$TZ = \"%s\"", getenv("TZ")); + testDiag("EPICS_TZ = \"%s\"", envGetConfigParamPtr(&EPICS_TZ)); + +#if !defined(_WIN32) && !defined(vxWorks) + { + // at least glibc doesn't initialize tzname[2] until some time.h function needs the time zone + time_t junk = 0; + (void)localtime(&junk); + testDiag("Local TZ names \"%s\", \"%s\"", tzname[0], tzname[1]); + } +#endif + + // sec:ns + double == sec:ns + testAdd(0,0, 0.0, 0,0); + testAdd(1,1, 0.0, 1,1); + testAdd(1,999999999, 0.000000001, 2,0); + testAdd(1,1, 2.000000002, 3,3); + testAdd(1,0, -1.0, 0,0); + testAdd(0,1, -0.000000001, 0,0); + testAdd(1,1, -1.000000001, 0,0); + testAdd(0xffffffff,0, -1.0, 0xfffffffe,0); + testAdd(0x7fffffff,0, 1.0, 0x80000000,0); + testAdd(0x7fffffff,999999999, 0.000000001, 0x80000000,0); + + // sec:ns - sec:ns == double + testDiff(0,0, 0,0, 0.0); + + testDiff(0,1, 0,1, 0.0); + testDiff(1,0, 1,0, 0.0); + testDiff(1,1, 1,1, 0.0); + + testDiff(2,0, 1,999999999, 0.000000001); + testDiff(1,999999999, 2,0, -0.000000001); + + testDiff(1,0, 0xffffffff,0, 2.0); + testDiff(0xffffffff,0, 1,0, -2.0); + + testDiff(1,999999999, 0xffffffff,999999999, 2.0); + testDiff(0xffffffff,999999999, 1,999999999, -2.0); + + testDiff(0,999999999, 0xffffffff,0, 1.999999999); // 0.99999.. - -1.0 + testDiff(0xffffffff,0, 0,999999999, -1.999999999); // -1.0 - 0.999.. + + testDiff(0x80000000,0, 0x7fffffff,0, 1.0); + testDiff(0x7fffffff,0, 0x80000000,0, -1.0); + + testDiff(0x80000000,0, 0x7fffffff,999999999, 0.000000001); + testDiff(0x7fffffff,999999999, 0x80000000,0, -0.000000001); + + testDiff(0x80000000,999999999, 0x7fffffff,0, 1.999999999); + testDiff(0x7fffffff,0, 0x80000000,999999999, -1.999999999); try { const epicsTimeStamp epochTS = {0, 0}; @@ -113,7 +225,7 @@ MAIN(epicsTimeTest) ts.strftime(buf, sizeof(buf), pFormat); testFail("nanosecond overflow returned \"%s\"", buf); } - catch ( ... ) { + catch ( std::exception& ) { testPass("nanosecond overflow throws"); } } @@ -178,20 +290,11 @@ MAIN(epicsTimeTest) now = epicsTime::getCurrent(); testPass("default time provider"); } - catch ( ... ) { + catch ( std::exception& ) { testFail("epicsTime::getCurrent() throws"); testAbort("Can't continue, check your time provider"); } - { - l_fp ntp = now; - epicsTime tsf = ntp; - const double diff = fabs(tsf - now); - // the difference in the precision of the two time formats - static const double precisionNTP = 1.0 / (1.0 + 0xffffffff); - testOk1(diff <= precisionEPICS + precisionNTP); - } - testDiag("Running %d loops", nTimes); const epicsTime begin = epicsTime::getCurrent(); @@ -225,6 +328,7 @@ MAIN(epicsTimeTest) "now - begin ~= diff"); testOk1(begin + 0 == begin); + std::cout<<"# begin + diff ("<<(begin + diff)<<") == now ("< Date: Wed, 4 Aug 2021 17:46:37 -0700 Subject: [PATCH 049/169] Com: clear IP_MULTICAST_ALL on Linux The default, non-compliant, behavior will pass all multicast packets to any socket bound to 0.0.0.0 or the mcast address, regardless of which groups, on which interfaces, that socket has joined. --- modules/libcom/src/osi/os/posix/osdSock.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/modules/libcom/src/osi/os/posix/osdSock.c b/modules/libcom/src/osi/os/posix/osdSock.c index 4ebd0db73..f773b3357 100644 --- a/modules/libcom/src/osi/os/posix/osdSock.c +++ b/modules/libcom/src/osi/os/posix/osdSock.c @@ -99,6 +99,22 @@ LIBCOM_API SOCKET epicsStdCall epicsSocketCreate ( close ( sock ); sock = INVALID_SOCKET; } + +#ifdef __linux__ +# ifndef IP_MULTICAST_ALL +# define IP_MULTICAST_ALL 49 +# endif + /* Enable compliant filtering of multicasts on Linux. cf. 'man 7 ip' */ + if(domain==AF_INET && type==SOCK_DGRAM){ + static int logged; + int val = 0; + if(setsockopt(sock, IPPROTO_IP, IP_MULTICAST_ALL, (char*)&val, sizeof(val)) && !logged) { + logged = 1; + errlogPrintf("Warning: Unable to clear IP_MULTICAST_ALL (err=%d). This may cause problems on multi-homed hosts.\n", + SOCKERRNO); + } + } +#endif } return sock; } From 8beb7bd2c8e3630fda3c49a1124c4495a4012080 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Tue, 10 Aug 2021 20:29:32 -0500 Subject: [PATCH 050/169] Restore the -p flag to MKDIR which RTEMS host.cfg removes --- configure/os/CONFIG.Common.RTEMS | 3 +++ 1 file changed, 3 insertions(+) diff --git a/configure/os/CONFIG.Common.RTEMS b/configure/os/CONFIG.Common.RTEMS index b7a42d137..c8729f933 100644 --- a/configure/os/CONFIG.Common.RTEMS +++ b/configure/os/CONFIG.Common.RTEMS @@ -79,6 +79,9 @@ CPPFLAGS += $($(BUILD_CLASS)_CPPFLAGS) $(POSIX_CPPFLAGS) $(OPT_CPPFLAGS)\ ECHO = @$(if $(filter -s,$(MFLAGS)),$(NOP),echo) +# Originally set in os/CONFIG.UnixCommon.Common +MKDIR = mkdir -p + #-------------------------------------------------- # Although RTEMS uses gcc, it wants to use gcc its own way CROSS_CPPFLAGS = From 72626cd5dd9e75f0d685d9cf609eea9647a61cb2 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Tue, 10 Aug 2021 20:31:16 -0500 Subject: [PATCH 051/169] Add newlines to a couple iocsh usage strings --- modules/database/src/ioc/db/dbIocRegister.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/database/src/ioc/db/dbIocRegister.c b/modules/database/src/ioc/db/dbIocRegister.c index 4df5ae336..ef57c5d44 100644 --- a/modules/database/src/ioc/db/dbIocRegister.c +++ b/modules/database/src/ioc/db/dbIocRegister.c @@ -234,7 +234,7 @@ static const iocshArg dbtgfArg0 = { "record name",iocshArgString}; static const iocshArg * const dbtgfArgs[1] = {&dbtgfArg0}; static const iocshFuncDef dbtgfFuncDef = {"dbtgf",1,dbtgfArgs, "Database Test Get Field.\n" - "Get field with different DBR_* types"}; + "Get field with different DBR_* types\n"}; static void dbtgfCallFunc(const iocshArgBuf *args) { dbtgf(args[0].sval);} /* dbtpf */ @@ -283,7 +283,7 @@ static const iocshArg * const dbtpnArgs[2] = {&dbtpnArg0,&dbtpnArg1}; static const iocshFuncDef dbtpnFuncDef = {"dbtpn",2,dbtpnArgs, "Database Put Notify\n" "Without value, begin async. processing and get\n" - "With value, begin put, process, and get"}; + "With value, begin put, process, and get\n"}; static void dbtpnCallFunc(const iocshArgBuf *args) { dbtpn(args[0].sval,args[1].sval);} From 8175cc8e647f215ec93db968680b7b465239e346 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Tue, 10 Aug 2021 20:35:19 -0500 Subject: [PATCH 052/169] POD text updates to dbCommon and various record types --- modules/database/src/ioc/db/dbCommon.dbd.pod | 153 ++++++++++-------- modules/database/src/std/rec/aiRecord.dbd.pod | 14 +- modules/database/src/std/rec/biRecord.dbd.pod | 7 +- .../database/src/std/rec/calcRecord.dbd.pod | 9 +- .../src/std/rec/calcoutRecord.dbd.pod | 16 +- .../src/std/rec/dfanoutRecord.dbd.pod | 45 ++++-- .../src/std/rec/longoutRecord.dbd.pod | 16 +- .../database/src/std/rec/mbbiRecord.dbd.pod | 9 +- .../src/std/rec/mbboDirectRecord.dbd.pod | 18 ++- .../database/src/std/rec/seqRecord.dbd.pod | 46 +++--- 10 files changed, 197 insertions(+), 136 deletions(-) diff --git a/modules/database/src/ioc/db/dbCommon.dbd.pod b/modules/database/src/ioc/db/dbCommon.dbd.pod index 5ad4627c5..5033a362f 100644 --- a/modules/database/src/ioc/db/dbCommon.dbd.pod +++ b/modules/database/src/ioc/db/dbCommon.dbd.pod @@ -77,12 +77,12 @@ A set of periodic scan intervals =back Additional periodic scan rates may be defined for individual IOCs by making a -local copy of menuScan.dbd and adding more choices as required. Scan rates -should normally be defined in order, with the fastest rates appearing first. -Scan periods may now be specified in seconds, minutes, hours or Hertz/Hz, and -plural time units will also be accepted (seconds are used if no unit is -mentioned in the choice string). For example the rates given below are all -valid: +local copy of menuScan.dbd and adding more choices as required. Periodic scan +rates should normally be defined in order following the other scan types, with +the longest periods appearing first. Scan periods can be specified with a unit +string of C/C, C/C, C/C or +C/C. Seconds are used if no unit is included in the choice string. +For example these rates are all valid: 1 hour 0.5 hours @@ -97,7 +97,7 @@ initialization (before the normal scan tasks are started). The B field orders the records within a specific SCAN group. This is not meaningful for passive records. All records of a specified phase are processed -before those with higher phase number. Whenever possible it is better to use +before those with higher phase number. It is generally better practice to use linked passive records to enforce the order of processing rather than a phase number. @@ -109,23 +109,23 @@ The call to post_event is: post_event(short event_number). The B field specifies the scheduling priority for processing records with SCAN=C and asynchronous record completion tasks. -The B field specifies a "disable value". Record processing is -immediately terminated if the value of this field is equal to the value of the -DISA field, i.e. the record is disabled. Note that field values of a record -can be changed by database put or Channel Access, even if a record is +The B field specifies a "disable value". Record processing cannot +begin when the value of this field is equal to the value of the DISA +field, meaning the record is disabled. Note that field values of a record +can be changed by database or Channel Access puts, even if the record is disabled. -The B field contains the value that is compared with DISV to determine -if the record is disabled. The value of the DISA field is obtained via SDIS if -SDIS is a database or channel access link. If SDIS is not a database or -channel access link, then DISA can be set via dbPutField or dbPutLink. - -If the B field of a record is written to, the record is processed. +The B field contains the value that is compared with DISV to determine if +the record is disabled. A value is obtained for the DISA field from the B +link field before the IOC tries to process the record. If SDIS is not set, DISA +may be set by some other method to enable and disable the record. The B field defines the record's "disable severity". If this field is not NO_ALARM and the record is disabled, the record will be put into alarm with this severity and a status of DISABLE_ALARM. +If the B field of a record is written to, the record is processed. + The B field contains the lock set to which this record belongs. All records linked in any way via input, output, or forward database links belong to the same lock set. Lock sets are determined at IOC initialization time, and @@ -135,15 +135,18 @@ The B field counts the number of times dbProcess finds the record active during successive scans, i.e. PACT is TRUE. If dbProcess finds the record active MAX_LOCK times (currently set to 10) it raises a SCAN_ALARM. -The B field is TRUE while the record is being processed. For +The B field is TRUE while the record is active (being processed). For asynchronous records PACT can be TRUE from the time record processing is started until the asynchronous completion occurs. As long as PACT is TRUE, dbProcess will not call the record processing routine. See Application Developers Guide for details on usage of PACT. -The B field is a database link to another record (the "target" record). -Processing a record with a specified FLNK field will force processing of the -target record, provided the target record's SCAN field is set to C. +The B field is a link pointing to another record (the "target" record). +Processing a record with the FLNK field set will trigger processing of the +target record towards the end of processing the first record (but before PACT is +cleared), provided the target record's SCAN field is set to C. If the +FLNK field is a Channel Access link it must point to the PROC field of the +target record. The B field is for internal use by the scanning system. @@ -236,35 +239,46 @@ The B field is for internal use by the scanning system. =head3 Alarm Fields -These fields indicate the status and severity of alarms, or else determine the +Alarm fields indicate the status and severity of record alarms, or determine how and when alarms are triggered. Of course, many records have alarm-related -fields not common to all records. These fields are listed and explained in the +fields not common to all records. Those fields are listed and explained in the appropriate section on each record. The B field contains the current alarm status. The B field contains the current alarm severity. -These two fields are seen outside database access. The B and B -fields are used by the database access, record support, and device support -routines to set new alarm status and severity values. Whenever any software -component discovers an alarm condition, it uses the following macro function: -recGblSetSevr(precord,new_status,new_severity) This ensures that the current -alarm severity is set equal to the highest outstanding alarm. The file alarm.h -defines all allowed alarm status and severity values. +The B string field may contain more detailed information about the alarm. + +The STAT, SEVR and AMSG fields hold alarm information as seen outside of the +database. The B, B and B fields are used during record +processing by the database access, record support, and device support routines +to set new alarm status and severity values and message text. Whenever any +software component discovers an alarm condition, it calls one of these routines +to register the alarm: + + recGblSetSevr(precord, new_status, new_severity); + recGblSetSevrMsg(precord, new_status, new_severity, "Message", ...); + +These check the current alarm severity and update the NSTA, NSEV and NAMSG +fields if appropriate so they always relate to the highest severity alarm seen +so far during record processing. The file alarm.h defines the allowed alarm +status and severity values. Towards the end of record processing these fields +are copied into the STAT, SEVR and AMSG fields and alarm monitors triggered. The B field contains the highest unacknowledged alarm severity. -The B field specifies if it is necessary to acknowledge transient +The B field specifies whether it is necessary to acknowledge transient alarms. -The B indicates if the record's value is BnBeBined. Typically -this is caused by a failure in device support, the fact that the record has -never been processed, or that the VAL field currently contains a NaN (not a -number). UDF is initialized to TRUE at IOC initialization. Record and device -support routines which write to the VAL field are responsible for setting UDF. +The B indicates if the record's value is BnBeBined. Typically this +is caused by a failure in device support, the fact that the record has never +been processed, or that the VAL field currently contains a NaN (not a number) or +Inf (Infinite) value. UDF defaults to TRUE but can be set in a database file. +Record and device support routines which write to the VAL field are generally +responsible for setting and clearing UDF. -=fields STAT, SEVR, NSTA, NSEV, ACKS, ACKT, UDF +=fields STAT, SEVR, AMSG, NSTA, NSEV, NAMSG, ACKS, ACKT, UDF =cut @@ -422,9 +436,11 @@ The B field is is for private use of the device support modules. =head3 Debugging Fields -The B field is used for trace processing. If this field is non-zero a -message is printed whenever this record is processed, and when any other -record in the same lock-set is processed by a database link from this record. +The B field can be used to trace record processing. When this field is +non-zero and the record is processed, a trace message will be be printed for +this record and any other record in the same lock-set that is triggered by a +database link from this record. The trace message includes the name of the +thread doing the processing, and the name of the record being processed. The B field indicates if there is a breakpoint set at this record. This supports setting a debug breakpoint in the record processing. STEP through @@ -435,32 +451,26 @@ database processing can be supported using this. =head3 Miscellaneous Fields -The B field contains a character string value defining the access -security group for this record. If left empty, the record is placed in group -DEFAULT. +The B string field sets the name of the access security group used for this +record. If left empty, the record is placed in group C. -The B field is a field for private use of the access security system. +The B field is private for use by the access security system. -The B field controls dbPutFields to this record which are normally -issued by channel access. If the field is set to TRUE all dbPutFields -directed to this record are ignored except to the field DISP itself. +The B field controls whether puts from outside the IOC are allowed to +modify the fields of this record at all. If the field is set to TRUE all puts +directed to this record are ignored, except for puts to the field DISP itself. -The B field specifies the device type for the record. Each record type -has its own set of device support routines which are specified in -devSup.ASCII. If a record type does not have any associated device support, -DTYP and DSET are meaningless. +The B field specifies the device type for the record. Most record types +have their own set of device types which are specified in the IOC's database +definition file. If a record type does not call any device support routines, +the DTYP and DSET fields are not used. -The B field contains the monitor lock. The lock used by the monitor -routines when the monitor list is being used. The list is locked whenever -monitors are being scheduled, invoked, or when monitors are being added to or -removed from the list. This field is accessed only by the dbEvent routines. +The B field contains a mutex which is locked by the monitor routines in +dbEvent.c whenever the monitor list for this record is accessed. -The B field is the head of the list of monitors connected to this +The B field holds a linked list of client monitors connected to this record. Each record support module is responsible for triggering monitors for -any fields that change as a result of record processing. Monitors are present -if mlis count is greater than zero. The call to trigger monitors is: -db_post_event(precord,&data,mask), where "mask" is some combination of -DBE_ALARM, DBE_VALUE, and DBE_LOG. +any fields that change as a result of record processing. The B field contains the address of a putNotify callback. @@ -474,23 +484,24 @@ The B field contains the address of dbRecordType The B field specifies a reprocessing of the record when current processing completes. -The B