From ca800fa57dcfa76d03666571ee6cdaa75ec55e19 Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Wed, 4 Apr 2018 11:01:45 -0700
Subject: [PATCH 001/281] libCom: add epicsThreadCreateOpt()
Variant of epicsThreadCreate() which accepts optional
arguments via a structure to allow for compatible
addition of new arguments.
---
modules/libcom/src/osi/epicsThread.cpp | 11 +++++++
modules/libcom/src/osi/epicsThread.h | 16 +++++++++
modules/libcom/src/osi/os/RTEMS/osdThread.c | 19 ++++++++---
modules/libcom/src/osi/os/WIN32/osdThread.c | 21 +++++++++---
modules/libcom/src/osi/os/posix/osdThread.c | 33 ++++++++++++++-----
modules/libcom/src/osi/os/vxWorks/osdThread.c | 24 ++++++++++----
6 files changed, 100 insertions(+), 24 deletions(-)
diff --git a/modules/libcom/src/osi/epicsThread.cpp b/modules/libcom/src/osi/epicsThread.cpp
index 892d73de0..372806edb 100644
--- a/modules/libcom/src/osi/epicsThread.cpp
+++ b/modules/libcom/src/osi/epicsThread.cpp
@@ -31,6 +31,17 @@
using namespace std;
+epicsThreadId epicsShareAPI epicsThreadCreate (
+ const char * name, unsigned int priority, unsigned int stackSize,
+ EPICSTHREADFUNC funptr,void * parm )
+{
+ epicsThreadOpts opts;
+ opts.priority = priority;
+ opts.stackSize = stackSize;
+
+ return epicsThreadCreateOpt(name, funptr, parm, &opts);
+}
+
epicsThreadRunable::~epicsThreadRunable () {}
void epicsThreadRunable::run () {}
void epicsThreadRunable::show ( unsigned int ) const {}
diff --git a/modules/libcom/src/osi/epicsThread.h b/modules/libcom/src/osi/epicsThread.h
index 84b2c4788..d0879a8eb 100644
--- a/modules/libcom/src/osi/epicsThread.h
+++ b/modules/libcom/src/osi/epicsThread.h
@@ -65,6 +65,22 @@ epicsShareFunc void epicsThreadRealtimeLock(void);
epicsShareFunc void epicsShareAPI epicsThreadExitMain(void);
+typedef struct epicsThreadOpts {
+ /** Thread priority in OSI range (cf. epicsThreadPriority*) */
+ unsigned int priority;
+ /** Thread stack size, as returned by epicsThreadGetStackSize().
+ *
+ * @warning Do not pass enum epicsThreadStackSizeClass directly!
+ */
+ unsigned int stackSize;
+} epicsThreadOpts;
+
+epicsShareFunc void epicsThreadOptsDefaults(epicsThreadOpts *opts);
+
+epicsShareFunc epicsThreadId epicsThreadCreateOpt (
+ const char * name,
+ EPICSTHREADFUNC funptr, void * parm,
+ const epicsThreadOpts *opts );
epicsShareFunc epicsThreadId epicsShareAPI epicsThreadCreate (
const char * name, unsigned int priority, unsigned int stackSize,
EPICSTHREADFUNC funptr,void * parm );
diff --git a/modules/libcom/src/osi/os/RTEMS/osdThread.c b/modules/libcom/src/osi/os/RTEMS/osdThread.c
index 769e95820..7b478df65 100644
--- a/modules/libcom/src/osi/os/RTEMS/osdThread.c
+++ b/modules/libcom/src/osi/os/RTEMS/osdThread.c
@@ -148,6 +148,13 @@ epicsThreadGetStackSize (epicsThreadStackSizeClass size)
return stackSize;
}
+static const epicsThreadOpts opts_default = {epicsThreadPriorityLow, 5000};
+
+void epicsThreadOptsDefaults(epicsThreadOpts *opts)
+{
+ *opts = opts_default;
+}
+
/*
* Ensure integrity of task variable list
*/
@@ -263,13 +270,17 @@ void epicsThreadRealtimeLock(void)
* Create and start a new thread
*/
epicsThreadId
-epicsThreadCreate (const char *name,
- unsigned int priority, unsigned int stackSize,
- EPICSTHREADFUNC funptr,void *parm)
+epicsThreadCreateOpt (
+ const char * name,
+ EPICSTHREADFUNC funptr, void * parm, const epicsThreadOpts *opts )
{
rtems_id tid;
rtems_status_code sc;
char c[4];
+ unsigned stackSize;
+
+ if(!opts) opts = &opts_default;
+ stackSize = opts->stackSize;
if (!initialized) epicsThreadInit();
if (stackSize < RTEMS_MINIMUM_STACK_SIZE) {
@@ -279,7 +290,7 @@ epicsThreadCreate (const char *name,
}
strncpy (c, name, sizeof c);
sc = rtems_task_create (rtems_build_name (c[0], c[1], c[2], c[3]),
- epicsThreadGetOssPriorityValue (priority),
+ epicsThreadGetOssPriorityValue (opts->priority),
stackSize,
RTEMS_PREEMPT|RTEMS_NO_TIMESLICE|RTEMS_NO_ASR|RTEMS_INTERRUPT_LEVEL(0),
RTEMS_FLOATING_POINT|RTEMS_LOCAL,
diff --git a/modules/libcom/src/osi/os/WIN32/osdThread.c b/modules/libcom/src/osi/os/WIN32/osdThread.c
index 8cdb4a3f4..f78c7c0f4 100644
--- a/modules/libcom/src/osi/os/WIN32/osdThread.c
+++ b/modules/libcom/src/osi/os/WIN32/osdThread.c
@@ -464,6 +464,13 @@ epicsShareFunc unsigned int epicsShareAPI
return stackSizeTable[stackSizeClass];
}
+static const epicsThreadOpts opts_default = {epicsThreadPriorityLow, STACK_SIZE(1)};
+
+void epicsThreadOptsDefaults(epicsThreadOpts *opts)
+{
+ *opts = opts_default;
+}
+
void epicsThreadCleanupWIN32 ()
{
win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal ();
@@ -579,8 +586,10 @@ static win32ThreadParam * epicsThreadImplicitCreate ( void )
/*
* epicsThreadCreate ()
*/
-epicsShareFunc epicsThreadId epicsShareAPI epicsThreadCreate (const char *pName,
- unsigned int priority, unsigned int stackSize, EPICSTHREADFUNC pFunc,void *pParm)
+epicsThreadId epicsThreadCreateOpt (
+ const char * pName,
+ EPICSTHREADFUNC pFunc, void * pParm,
+ const epicsThreadOpts *opts )
{
win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal ();
win32ThreadParam * pParmWIN32;
@@ -592,18 +601,20 @@ epicsShareFunc epicsThreadId epicsShareAPI epicsThreadCreate (const char *pName,
return NULL;
}
+ if(!opts) opts = &opts_default;
+
pParmWIN32 = epicsThreadParmCreate ( pName );
if ( pParmWIN32 == 0 ) {
return ( epicsThreadId ) pParmWIN32;
}
pParmWIN32->funptr = pFunc;
pParmWIN32->parm = pParm;
- pParmWIN32->epicsPriority = priority;
+ pParmWIN32->epicsPriority = opts->priority;
{
unsigned threadId;
pParmWIN32->handle = (HANDLE) _beginthreadex (
- 0, stackSize, epicsWin32ThreadEntry,
+ 0, opts->stackSize, epicsWin32ThreadEntry,
pParmWIN32,
CREATE_SUSPENDED | STACK_SIZE_PARAM_IS_A_RESERVATION,
& threadId );
@@ -615,7 +626,7 @@ epicsShareFunc epicsThreadId epicsShareAPI epicsThreadCreate (const char *pName,
pParmWIN32->id = ( DWORD ) threadId ;
}
- osdPriority = epicsThreadGetOsdPriorityValue (priority);
+ osdPriority = epicsThreadGetOsdPriorityValue (opts->priority);
bstat = SetThreadPriority ( pParmWIN32->handle, osdPriority );
if (!bstat) {
CloseHandle ( pParmWIN32->handle );
diff --git a/modules/libcom/src/osi/os/posix/osdThread.c b/modules/libcom/src/osi/os/posix/osdThread.c
index 755390eed..65e1929ec 100644
--- a/modules/libcom/src/osi/os/posix/osdThread.c
+++ b/modules/libcom/src/osi/os/posix/osdThread.c
@@ -430,15 +430,22 @@ void epicsThreadRealtimeLock(void)
#endif
}
-epicsShareFunc unsigned int epicsShareAPI epicsThreadGetStackSize (epicsThreadStackSizeClass stackSizeClass)
-{
#if defined (OSITHREAD_USE_DEFAULT_STACK)
- return 0;
+#define STACK_SIZE(f) (0)
#elif defined(_POSIX_THREAD_ATTR_STACKSIZE) && _POSIX_THREAD_ATTR_STACKSIZE > 0
#define STACK_SIZE(f) (f * 0x10000 * sizeof(void *))
static const unsigned stackSizeTable[epicsThreadStackBig+1] = {
STACK_SIZE(1), STACK_SIZE(2), STACK_SIZE(4)
};
+#else
+#define STACK_SIZE(f) (0)
+#endif /*_POSIX_THREAD_ATTR_STACKSIZE*/
+
+epicsShareFunc unsigned int epicsShareAPI epicsThreadGetStackSize (epicsThreadStackSizeClass stackSizeClass)
+{
+#if defined (OSITHREAD_USE_DEFAULT_STACK)
+ return 0;
+#elif defined(_POSIX_THREAD_ATTR_STACKSIZE) && _POSIX_THREAD_ATTR_STACKSIZE > 0
if (stackSizeClasspriority,opts->stackSize,funptr,parm);
if(pthreadInfo==0) return 0;
pthreadInfo->isEpicsThread = 1;
setSchedulingPolicy(pthreadInfo,SCHED_FIFO);
@@ -518,7 +535,7 @@ epicsShareFunc epicsThreadId epicsShareAPI epicsThreadCreate(const char *name,
if(status==EPERM){
/* Try again without SCHED_FIFO*/
free_threadInfo(pthreadInfo);
- pthreadInfo = init_threadInfo(name,priority,stackSize,funptr,parm);
+ pthreadInfo = init_threadInfo(name,opts->priority,opts->stackSize,funptr,parm);
if(pthreadInfo==0) return 0;
pthreadInfo->isEpicsThread = 1;
status = pthread_create(&pthreadInfo->tid,&pthreadInfo->attr,
diff --git a/modules/libcom/src/osi/os/vxWorks/osdThread.c b/modules/libcom/src/osi/os/vxWorks/osdThread.c
index ce01ea609..6a71301a2 100644
--- a/modules/libcom/src/osi/os/vxWorks/osdThread.c
+++ b/modules/libcom/src/osi/os/vxWorks/osdThread.c
@@ -133,6 +133,13 @@ unsigned int epicsThreadGetStackSize (epicsThreadStackSizeClass stackSizeClass)
return stackSizeTable[stackSizeClass];
}
+static const epicsThreadOpts opts_default = {epicsThreadPriorityLow, 4000*ARCH_STACK_FACTOR};
+
+void epicsThreadOptsDefaults(epicsThreadOpts *opts)
+{
+ *opts = opts_default;
+}
+
struct epicsThreadOSD {};
/* Strictly speaking this should be a WIND_TCB, but we only need it to
* be able to create an epicsThreadId that is guaranteed never to be
@@ -190,19 +197,22 @@ static void createFunction(EPICSTHREADFUNC func, void *parm)
#else
#define TASK_FLAGS (VX_FP_TASK)
#endif
-epicsThreadId epicsThreadCreate(const char *name,
- unsigned int priority, unsigned int stackSize,
- EPICSTHREADFUNC funptr,void *parm)
+epicsThreadId
+epicsThreadCreateOpt (
+ const char * name,
+ EPICSTHREADFUNC funptr, void * parm, const epicsThreadOpts *opts )
{
int tid;
+ if(!opts) opts = &opts_default;
+
epicsThreadInit();
- if(stackSize<100) {
- errlogPrintf("epicsThreadCreate %s illegal stackSize %d\n",name,stackSize);
+ if(opts->stackSize<100) {
+ errlogPrintf("epicsThreadCreate %s illegal stackSize %d\n",name,opts->stackSize);
return(0);
}
- tid = taskSpawn((char *)name,getOssPriorityValue(priority),
- TASK_FLAGS, stackSize,
+ tid = taskSpawn((char *)name,getOssPriorityValue(opts->priority),
+ TASK_FLAGS, opts->stackSize,
(FUNCPTR)createFunction,(int)funptr,(int)parm,
0,0,0,0,0,0,0,0);
if(tid==ERROR) {
From d989c8fade61a2fbff0823276f3a52449022a96c Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Wed, 4 Apr 2018 11:23:53 -0700
Subject: [PATCH 002/281] libCom: joinable threads (posix only)
Add epicsThreadJoin() and epicsThreadOpts::joinable.
For compatibility, default threads aren't joinable
by default.
Currently only POSIX can join.
---
modules/libcom/src/osi/epicsThread.cpp | 1 +
modules/libcom/src/osi/epicsThread.h | 5 +-
modules/libcom/src/osi/os/Linux/osdThread.h | 5 ++
modules/libcom/src/osi/os/RTEMS/osdThread.c | 4 +-
modules/libcom/src/osi/os/RTEMS/osdThread.h | 3 +
modules/libcom/src/osi/os/WIN32/osdThread.c | 4 +-
modules/libcom/src/osi/os/WIN32/osdThread.h | 4 +-
modules/libcom/src/osi/os/posix/osdThread.c | 56 ++++++++++++++++---
modules/libcom/src/osi/os/posix/osdThread.h | 5 ++
modules/libcom/src/osi/os/vxWorks/osdThread.c | 4 +-
modules/libcom/src/osi/os/vxWorks/osdThread.h | 3 +
11 files changed, 80 insertions(+), 14 deletions(-)
diff --git a/modules/libcom/src/osi/epicsThread.cpp b/modules/libcom/src/osi/epicsThread.cpp
index 372806edb..4f983f0e3 100644
--- a/modules/libcom/src/osi/epicsThread.cpp
+++ b/modules/libcom/src/osi/epicsThread.cpp
@@ -38,6 +38,7 @@ epicsThreadId epicsShareAPI epicsThreadCreate (
epicsThreadOpts opts;
opts.priority = priority;
opts.stackSize = stackSize;
+ opts.joinable = 0;
return epicsThreadCreateOpt(name, funptr, parm, &opts);
}
diff --git a/modules/libcom/src/osi/epicsThread.h b/modules/libcom/src/osi/epicsThread.h
index d0879a8eb..0482bea34 100644
--- a/modules/libcom/src/osi/epicsThread.h
+++ b/modules/libcom/src/osi/epicsThread.h
@@ -73,6 +73,8 @@ typedef struct epicsThreadOpts {
* @warning Do not pass enum epicsThreadStackSizeClass directly!
*/
unsigned int stackSize;
+ /** Should thread be joinable? (default (0) is not joinable). */
+ unsigned int joinable;
} epicsThreadOpts;
epicsShareFunc void epicsThreadOptsDefaults(epicsThreadOpts *opts);
@@ -86,7 +88,8 @@ epicsShareFunc epicsThreadId epicsShareAPI epicsThreadCreate (
EPICSTHREADFUNC funptr,void * parm );
epicsShareFunc epicsThreadId epicsShareAPI epicsThreadMustCreate (
const char * name, unsigned int priority, unsigned int stackSize,
- EPICSTHREADFUNC funptr,void * parm );
+ EPICSTHREADFUNC funptr,void * parm );
+epicsShareFunc void epicsThreadJoin(epicsThreadId id);
epicsShareFunc void epicsShareAPI epicsThreadSuspendSelf(void);
epicsShareFunc void epicsShareAPI epicsThreadResume(epicsThreadId id);
epicsShareFunc unsigned int epicsShareAPI epicsThreadGetPriority(
diff --git a/modules/libcom/src/osi/os/Linux/osdThread.h b/modules/libcom/src/osi/os/Linux/osdThread.h
index 7d2a4868d..40a837e9f 100644
--- a/modules/libcom/src/osi/os/Linux/osdThread.h
+++ b/modules/libcom/src/osi/os/Linux/osdThread.h
@@ -16,12 +16,16 @@
#include "ellLib.h"
#include "epicsEvent.h"
+/* This target supports joining threads */
+#define EPICS_THREAD_CAN_JOIN (1)
+
#ifdef __cplusplus
extern "C" {
#endif
typedef struct epicsThreadOSD {
ELLNODE node;
+ int refcnt;
pthread_t tid;
pid_t lwpId;
pthread_attr_t attr;
@@ -35,6 +39,7 @@ typedef struct epicsThreadOSD {
int isRealTimeScheduled;
int isOnThreadList;
unsigned int osiPriority;
+ int joinable;
char name[1]; /* actually larger */
} epicsThreadOSD;
diff --git a/modules/libcom/src/osi/os/RTEMS/osdThread.c b/modules/libcom/src/osi/os/RTEMS/osdThread.c
index 7b478df65..8df2415c0 100644
--- a/modules/libcom/src/osi/os/RTEMS/osdThread.c
+++ b/modules/libcom/src/osi/os/RTEMS/osdThread.c
@@ -148,7 +148,7 @@ epicsThreadGetStackSize (epicsThreadStackSizeClass size)
return stackSize;
}
-static const epicsThreadOpts opts_default = {epicsThreadPriorityLow, 5000};
+static const epicsThreadOpts opts_default = {epicsThreadPriorityLow, 5000, 0};
void epicsThreadOptsDefaults(epicsThreadOpts *opts)
{
@@ -316,6 +316,8 @@ threadMustCreate (const char *name,
return tid;
}
+void epicsThreadJoin(epicsThreadId id) {}
+
void
epicsThreadSuspendSelf (void)
{
diff --git a/modules/libcom/src/osi/os/RTEMS/osdThread.h b/modules/libcom/src/osi/os/RTEMS/osdThread.h
index 4451f845a..7307603eb 100644
--- a/modules/libcom/src/osi/os/RTEMS/osdThread.h
+++ b/modules/libcom/src/osi/os/RTEMS/osdThread.h
@@ -8,5 +8,8 @@
* in file LICENSE that is included with this distribution.
\*************************************************************************/
+/* This target does not support joining threads */
+#define EPICS_THREAD_CAN_JOIN (0)
+
int epicsThreadGetOssPriorityValue(unsigned int osiPriority);
diff --git a/modules/libcom/src/osi/os/WIN32/osdThread.c b/modules/libcom/src/osi/os/WIN32/osdThread.c
index f78c7c0f4..fb3f39f88 100644
--- a/modules/libcom/src/osi/os/WIN32/osdThread.c
+++ b/modules/libcom/src/osi/os/WIN32/osdThread.c
@@ -464,7 +464,7 @@ epicsShareFunc unsigned int epicsShareAPI
return stackSizeTable[stackSizeClass];
}
-static const epicsThreadOpts opts_default = {epicsThreadPriorityLow, STACK_SIZE(1)};
+static const epicsThreadOpts opts_default = {epicsThreadPriorityLow, STACK_SIZE(1), 0};
void epicsThreadOptsDefaults(epicsThreadOpts *opts)
{
@@ -651,6 +651,8 @@ epicsThreadId epicsThreadCreateOpt (
return ( epicsThreadId ) pParmWIN32;
}
+void epicsThreadJoin(epicsThreadId id) {}
+
/*
* epicsThreadSuspendSelf ()
*/
diff --git a/modules/libcom/src/osi/os/WIN32/osdThread.h b/modules/libcom/src/osi/os/WIN32/osdThread.h
index 136e96bf4..10025d2ad 100644
--- a/modules/libcom/src/osi/os/WIN32/osdThread.h
+++ b/modules/libcom/src/osi/os/WIN32/osdThread.h
@@ -11,5 +11,7 @@
#ifndef osdThreadh
#define osdThreadh
-
+/* This target does not support joining threads */
+#define EPICS_THREAD_CAN_JOIN (0)
+
#endif /* osdThreadh */
diff --git a/modules/libcom/src/osi/os/posix/osdThread.c b/modules/libcom/src/osi/os/posix/osdThread.c
index 65e1929ec..d34edf694 100644
--- a/modules/libcom/src/osi/os/posix/osdThread.c
+++ b/modules/libcom/src/osi/os/posix/osdThread.c
@@ -38,6 +38,7 @@
#include "errlog.h"
#include "epicsAssert.h"
#include "epicsExit.h"
+#include "epicsAtomic.h"
epicsShareFunc void epicsThreadShowInfo(epicsThreadOSD *pthreadInfo, unsigned int level);
epicsShareFunc void osdThreadHooksRun(epicsThreadId id);
@@ -167,12 +168,14 @@ static epicsThreadOSD * create_threadInfo(const char *name)
return NULL;
}
strcpy(pthreadInfo->name, name);
+ epicsAtomicIncrIntT(&pthreadInfo->refcnt); /* initial ref for the thread itself */
return pthreadInfo;
}
static epicsThreadOSD * init_threadInfo(const char *name,
unsigned int priority, unsigned int stackSize,
- EPICSTHREADFUNC funptr,void *parm)
+ EPICSTHREADFUNC funptr,void *parm,
+ unsigned joinable)
{
epicsThreadOSD *pthreadInfo;
int status;
@@ -182,12 +185,15 @@ static epicsThreadOSD * init_threadInfo(const char *name,
return NULL;
pthreadInfo->createFunc = funptr;
pthreadInfo->createArg = parm;
+ pthreadInfo->joinable = joinable;
status = pthread_attr_init(&pthreadInfo->attr);
checkStatusOnce(status,"pthread_attr_init");
if(status) return 0;
- status = pthread_attr_setdetachstate(
- &pthreadInfo->attr, PTHREAD_CREATE_DETACHED);
- checkStatusOnce(status,"pthread_attr_setdetachstate");
+ if(!joinable){
+ status = pthread_attr_setdetachstate(
+ &pthreadInfo->attr, PTHREAD_CREATE_DETACHED);
+ checkStatusOnce(status,"pthread_attr_setdetachstate");
+ }
#if defined (_POSIX_THREAD_ATTR_STACKSIZE)
#if ! defined (OSITHREAD_USE_DEFAULT_STACK)
status = pthread_attr_setstacksize( &pthreadInfo->attr,(size_t)stackSize);
@@ -204,6 +210,8 @@ static void free_threadInfo(epicsThreadOSD *pthreadInfo)
{
int status;
+ if(epicsAtomicDecrIntT(&pthreadInfo->refcnt) > 0) return;
+
status = mutexLock(&listLock);
checkStatusQuit(status,"pthread_mutex_lock","free_threadInfo");
if(pthreadInfo->isOnThreadList) ellDelete(&pthreadList,&pthreadInfo->node);
@@ -366,7 +374,7 @@ static void once(void)
if(errVerbose) fprintf(stderr,"task priorities are not implemented\n");
#endif /* _POSIX_THREAD_PRIORITY_SCHEDULING */
- pthreadInfo = init_threadInfo("_main_",0,epicsThreadGetStackSize(epicsThreadStackSmall),0,0);
+ pthreadInfo = init_threadInfo("_main_",0,epicsThreadGetStackSize(epicsThreadStackSmall),0,0,0);
assert(pthreadInfo!=NULL);
status = pthread_setspecific(getpthreadInfo,(void *)pthreadInfo);
checkStatusOnceQuit(status,"pthread_setspecific","epicsThreadInit");
@@ -462,7 +470,7 @@ epicsShareFunc unsigned int epicsShareAPI epicsThreadGetStackSize (epicsThreadSt
#endif /*_POSIX_THREAD_ATTR_STACKSIZE*/
}
-static const epicsThreadOpts opts_default = {epicsThreadPriorityLow, STACK_SIZE(1)};
+static const epicsThreadOpts opts_default = {epicsThreadPriorityLow, STACK_SIZE(1), 0};
void epicsThreadOptsDefaults(epicsThreadOpts *opts)
{
@@ -525,7 +533,7 @@ epicsThreadCreateOpt (
assert(pcommonAttr);
sigfillset(&blockAllSig);
pthread_sigmask(SIG_SETMASK,&blockAllSig,&oldSig);
- pthreadInfo = init_threadInfo(name,opts->priority,opts->stackSize,funptr,parm);
+ pthreadInfo = init_threadInfo(name,opts->priority,opts->stackSize,funptr,parm,opts->joinable);
if(pthreadInfo==0) return 0;
pthreadInfo->isEpicsThread = 1;
setSchedulingPolicy(pthreadInfo,SCHED_FIFO);
@@ -535,7 +543,7 @@ epicsThreadCreateOpt (
if(status==EPERM){
/* Try again without SCHED_FIFO*/
free_threadInfo(pthreadInfo);
- pthreadInfo = init_threadInfo(name,opts->priority,opts->stackSize,funptr,parm);
+ pthreadInfo = init_threadInfo(name,opts->priority,opts->stackSize,funptr,parm,opts->joinable);
if(pthreadInfo==0) return 0;
pthreadInfo->isEpicsThread = 1;
status = pthread_create(&pthreadInfo->tid,&pthreadInfo->attr,
@@ -548,6 +556,10 @@ epicsThreadCreateOpt (
}
status = pthread_sigmask(SIG_SETMASK,&oldSig,NULL);
checkStatusOnce(status,"pthread_sigmask");
+ if(pthreadInfo->joinable) {
+ /* extra ref for epicsThreadJoin() */
+ epicsAtomicIncrIntT(&pthreadInfo->refcnt);
+ }
return(pthreadInfo);
}
@@ -587,7 +599,33 @@ static epicsThreadOSD *createImplicit(void)
}
return pthreadInfo;
}
-
+
+void epicsThreadJoin(epicsThreadId id)
+{
+ void *ret = NULL;
+ int status;
+
+ if(!id) {
+ return;
+ } else if(!id->joinable) {
+ /* try to error nicely, however in all likelyhood de-ref of
+ * 'id' has already caused SIGSEGV as we are racing thread exit,
+ * which free's 'id'.
+ */
+ cantProceed("%s join not enabled for thread.\n", id->name);
+ }
+
+ status = pthread_join(id->tid, &ret);
+ if(status == EDEADLK) {
+ /* Thread can't join itself (directly or indirectly)
+ * so we detach instead.
+ */
+ status = pthread_detach(id->tid);
+ checkStatusOnce(status, "pthread_detach");
+ } else checkStatusOnce(status, "pthread_join");
+ free_threadInfo(id);
+}
+
epicsShareFunc void epicsShareAPI epicsThreadSuspendSelf(void)
{
epicsThreadOSD *pthreadInfo;
diff --git a/modules/libcom/src/osi/os/posix/osdThread.h b/modules/libcom/src/osi/os/posix/osdThread.h
index 3a80b537c..eee0c825d 100644
--- a/modules/libcom/src/osi/os/posix/osdThread.h
+++ b/modules/libcom/src/osi/os/posix/osdThread.h
@@ -16,12 +16,16 @@
#include "ellLib.h"
#include "epicsEvent.h"
+/* This target supports joining threads */
+#define EPICS_THREAD_CAN_JOIN (1)
+
#ifdef __cplusplus
extern "C" {
#endif
typedef struct epicsThreadOSD {
ELLNODE node;
+ int refcnt;
pthread_t tid;
pthread_attr_t attr;
struct sched_param schedParam;
@@ -34,6 +38,7 @@ typedef struct epicsThreadOSD {
int isRealTimeScheduled;
int isOnThreadList;
unsigned int osiPriority;
+ int joinable;
char name[1]; /* actually larger */
} epicsThreadOSD;
diff --git a/modules/libcom/src/osi/os/vxWorks/osdThread.c b/modules/libcom/src/osi/os/vxWorks/osdThread.c
index 6a71301a2..7a0fe7b9f 100644
--- a/modules/libcom/src/osi/os/vxWorks/osdThread.c
+++ b/modules/libcom/src/osi/os/vxWorks/osdThread.c
@@ -133,7 +133,7 @@ unsigned int epicsThreadGetStackSize (epicsThreadStackSizeClass stackSizeClass)
return stackSizeTable[stackSizeClass];
}
-static const epicsThreadOpts opts_default = {epicsThreadPriorityLow, 4000*ARCH_STACK_FACTOR};
+static const epicsThreadOpts opts_default = {epicsThreadPriorityLow, 4000*ARCH_STACK_FACTOR, 0};
void epicsThreadOptsDefaults(epicsThreadOpts *opts)
{
@@ -223,6 +223,8 @@ epicsThreadCreateOpt (
return((epicsThreadId)tid);
}
+void epicsThreadJoin(epicsThreadId id) {}
+
void epicsThreadSuspendSelf()
{
STATUS status;
diff --git a/modules/libcom/src/osi/os/vxWorks/osdThread.h b/modules/libcom/src/osi/os/vxWorks/osdThread.h
index 2ee9f2d46..09704b667 100644
--- a/modules/libcom/src/osi/os/vxWorks/osdThread.h
+++ b/modules/libcom/src/osi/os/vxWorks/osdThread.h
@@ -10,4 +10,7 @@
#ifndef osdThreadh
#define osdThreadh
+/* This target does not support joining threads */
+#define EPICS_THREAD_CAN_JOIN (0)
+
#endif /* osdThreadh */
From c9dcab95a6aa7376edde322d3b9473e9a89c2b38 Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Wed, 4 Apr 2018 11:28:08 -0700
Subject: [PATCH 003/281] class epicsThread is joinable
---
modules/libcom/src/osi/epicsThread.cpp | 22 +++++++++++++++++++---
modules/libcom/src/osi/epicsThread.h | 1 +
2 files changed, 20 insertions(+), 3 deletions(-)
diff --git a/modules/libcom/src/osi/epicsThread.cpp b/modules/libcom/src/osi/epicsThread.cpp
index 4f983f0e3..52819c8eb 100644
--- a/modules/libcom/src/osi/epicsThread.cpp
+++ b/modules/libcom/src/osi/epicsThread.cpp
@@ -153,6 +153,10 @@ bool epicsThread::exitWait ( const double delay ) throw ()
if ( this->pThreadDestroyed ) {
*this->pThreadDestroyed = true;
}
+ if(!joined) {
+ epicsThreadJoin(this->id);
+ joined = true;
+ }
return true;
}
epicsTime exitWaitBegin = epicsTime::getCurrent ();
@@ -166,6 +170,10 @@ bool epicsThread::exitWait ( const double delay ) throw ()
epicsTime current = epicsTime::getCurrent ();
exitWaitElapsed = current - exitWaitBegin;
}
+ if(!joined) {
+ epicsThreadJoin(this->id);
+ joined = true;
+ }
}
catch ( std :: exception & except ) {
errlogPrintf (
@@ -190,10 +198,18 @@ epicsThread::epicsThread (
unsigned stackSize, unsigned priority ) :
runable ( runableIn ), id ( 0 ), pThreadDestroyed ( 0 ),
begin ( false ), cancel ( false ), terminated ( false )
+ , joined(false)
{
- this->id = epicsThreadCreate (
- pName, priority, stackSize, epicsThreadCallEntryPoint,
- static_cast < void * > ( this ) );
+ epicsThreadOpts opts;
+ epicsThreadOptsDefaults(&opts);
+ opts.stackSize = stackSize;
+ opts.priority = priority;
+ opts.joinable = 1;
+
+ this->id = epicsThreadCreateOpt(
+ pName, epicsThreadCallEntryPoint,
+ static_cast < void * > ( this ),
+ &opts);
if ( ! this->id ) {
throw unableToCreateThread ();
}
diff --git a/modules/libcom/src/osi/epicsThread.h b/modules/libcom/src/osi/epicsThread.h
index 0482bea34..c43d7fb01 100644
--- a/modules/libcom/src/osi/epicsThread.h
+++ b/modules/libcom/src/osi/epicsThread.h
@@ -197,6 +197,7 @@ private:
bool begin;
bool cancel;
bool terminated;
+ bool joined;
bool beginWait () throw ();
epicsThread ( const epicsThread & );
From 460e58e3e5624501a4831eb6f45e9f9d8deb5a78 Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Thu, 5 Apr 2018 09:04:46 -0700
Subject: [PATCH 004/281] libCom "join" win32 threads
Wait for completion
---
modules/libcom/src/osi/os/WIN32/osdThread.c | 37 ++++++++++++++++++++-
modules/libcom/src/osi/os/WIN32/osdThread.h | 4 +--
2 files changed, 38 insertions(+), 3 deletions(-)
diff --git a/modules/libcom/src/osi/os/WIN32/osdThread.c b/modules/libcom/src/osi/os/WIN32/osdThread.c
index fb3f39f88..eba41645f 100644
--- a/modules/libcom/src/osi/os/WIN32/osdThread.c
+++ b/modules/libcom/src/osi/os/WIN32/osdThread.c
@@ -32,6 +32,7 @@
#include "epicsAssert.h"
#include "ellLib.h"
#include "epicsExit.h"
+#include "epicsAtomic.h"
epicsShareFunc void osdThreadHooksRun(epicsThreadId id);
@@ -46,6 +47,7 @@ typedef struct win32ThreadGlobal {
typedef struct epicsThreadOSD {
ELLNODE node;
+ int refcnt;
HANDLE handle;
EPICSTHREADFUNC funptr;
void * parm;
@@ -53,6 +55,7 @@ typedef struct epicsThreadOSD {
DWORD id;
unsigned epicsPriority;
char isSuspended;
+ char joinable;
} win32ThreadParam;
typedef struct epicsThreadPrivateOSD {
@@ -238,6 +241,8 @@ static void epicsParmCleanupWIN32 ( win32ThreadParam * pParm )
}
if ( pParm ) {
+ if(epicsAtomicDecrIntT(&pParm->refcnt) > 0) return;
+
/* fprintf ( stderr, "thread %s is exiting\n", pParm->pName ); */
EnterCriticalSection ( & pGbl->mutex );
ellDelete ( & pGbl->threadList, & pParm->node );
@@ -533,6 +538,7 @@ static win32ThreadParam * epicsThreadParmCreate ( const char *pName )
pParmWIN32->pName = (char *) ( pParmWIN32 + 1 );
strcpy ( pParmWIN32->pName, pName );
pParmWIN32->isSuspended = 0;
+ epicsAtomicIncrIntT(&pParmWIN32->refcnt);
}
return pParmWIN32;
}
@@ -648,10 +654,39 @@ epicsThreadId epicsThreadCreateOpt (
return NULL;
}
+ if(opts->joinable) {
+ pParmWIN32->joinable = 1;
+ epicsAtomicIncrIntT(&pParmWIN32->refcnt);
+ }
+
return ( epicsThreadId ) pParmWIN32;
}
-void epicsThreadJoin(epicsThreadId id) {}
+void epicsThreadJoin(epicsThreadId id)
+{
+ win32ThreadParam * pParmWIN32 = id;
+
+ if(!id) {
+ /* no-op */
+ } else if(!pParmWIN32->joinable) {
+ /* try to error nicely, however in all likelyhood de-ref of
+ * 'pParmWIN32' has already crashed us as we are racing thread exit,
+ * which free's 'pParmWIN32'.
+ */
+ cantProceed("%s join not enabled for thread.\n", pParmWIN32->pName);
+
+ } else if(epicsThreadGetIdSelf() != id) {
+ DWORD status = WaitForSingleObject(pParmWIN32->handle, INFINITE);
+ if(status != WAIT_OBJECT_0) {
+ /* TODO: signal error? */
+ }
+
+ epicsParmCleanupWIN32(pParmWIN32);
+ } else {
+ /* join self silently does nothing */
+ epicsParmCleanupWIN32(pParmWIN32);
+ }
+}
/*
* epicsThreadSuspendSelf ()
diff --git a/modules/libcom/src/osi/os/WIN32/osdThread.h b/modules/libcom/src/osi/os/WIN32/osdThread.h
index 10025d2ad..fe60564d1 100644
--- a/modules/libcom/src/osi/os/WIN32/osdThread.h
+++ b/modules/libcom/src/osi/os/WIN32/osdThread.h
@@ -11,7 +11,7 @@
#ifndef osdThreadh
#define osdThreadh
-/* This target does not support joining threads */
-#define EPICS_THREAD_CAN_JOIN (0)
+/* This target supports joining threads */
+#define EPICS_THREAD_CAN_JOIN (1)
#endif /* osdThreadh */
From 149ab1186ad6e8b8e7a8879bf40d56c905ccce3a Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Sat, 21 Apr 2018 10:15:35 -0700
Subject: [PATCH 005/281] epicsThread fix join
---
modules/libcom/src/osi/epicsThread.cpp | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/modules/libcom/src/osi/epicsThread.cpp b/modules/libcom/src/osi/epicsThread.cpp
index 52819c8eb..7092900b1 100644
--- a/modules/libcom/src/osi/epicsThread.cpp
+++ b/modules/libcom/src/osi/epicsThread.cpp
@@ -154,8 +154,11 @@ bool epicsThread::exitWait ( const double delay ) throw ()
*this->pThreadDestroyed = true;
}
if(!joined) {
+ {
+ epicsGuard < epicsMutex > guard ( this->mutex );
+ joined = true;
+ }
epicsThreadJoin(this->id);
- joined = true;
}
return true;
}
@@ -170,9 +173,11 @@ bool epicsThread::exitWait ( const double delay ) throw ()
epicsTime current = epicsTime::getCurrent ();
exitWaitElapsed = current - exitWaitBegin;
}
- if(!joined) {
- epicsThreadJoin(this->id);
+ if(this->terminated && !joined) {
joined = true;
+
+ epicsGuardRelease < epicsMutex > unguard ( guard );
+ epicsThreadJoin(this->id);
}
}
catch ( std :: exception & except ) {
From f9092783f89d3626fdbd431457f15a084b2fb884 Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Sat, 23 Jun 2018 12:36:07 -0700
Subject: [PATCH 006/281] RTEMS thread join
---
modules/libcom/RTEMS/rtems_config.c | 1 +
modules/libcom/src/osi/os/RTEMS/osdThread.c | 105 +++++++++++++++++---
modules/libcom/src/osi/os/RTEMS/osdThread.h | 4 +-
3 files changed, 96 insertions(+), 14 deletions(-)
diff --git a/modules/libcom/RTEMS/rtems_config.c b/modules/libcom/RTEMS/rtems_config.c
index 147c08b10..796b1049b 100644
--- a/modules/libcom/RTEMS/rtems_config.c
+++ b/modules/libcom/RTEMS/rtems_config.c
@@ -27,6 +27,7 @@
#endif
#define CONFIGURE_MAXIMUM_TASKS rtems_resource_unlimited(30)
+#define CONFIGURE_MAXIMUM_BARRIERS rtems_resource_unlimited(30)
#define CONFIGURE_MAXIMUM_SEMAPHORES rtems_resource_unlimited(500)
#define CONFIGURE_MAXIMUM_TIMERS rtems_resource_unlimited(20)
#define CONFIGURE_MAXIMUM_MESSAGE_QUEUES rtems_resource_unlimited(5)
diff --git a/modules/libcom/src/osi/os/RTEMS/osdThread.c b/modules/libcom/src/osi/os/RTEMS/osdThread.c
index 8df2415c0..a22e2f87f 100644
--- a/modules/libcom/src/osi/os/RTEMS/osdThread.c
+++ b/modules/libcom/src/osi/os/RTEMS/osdThread.c
@@ -35,6 +35,7 @@
#include "osiUnistd.h"
#include "osdInterrupt.h"
#include "epicsExit.h"
+#include "epicsAtomic.h"
epicsShareFunc void osdThreadHooksRun(epicsThreadId id);
epicsShareFunc void osdThreadHooksRunMain(epicsThreadId id);
@@ -47,6 +48,9 @@ struct taskVar {
struct taskVar *back;
char *name;
rtems_id id;
+ rtems_id join_barrier; /* only valid if joinable */
+ int refcnt;
+ int joinable;
EPICSTHREADFUNC funptr;
void *parm;
unsigned int threadVariableCapacity;
@@ -170,6 +174,22 @@ taskVarUnlock (void)
epicsMutexOsdUnlock (taskVarMutex);
}
+static
+void taskUnref(struct taskVar *v)
+{
+ int ref = epicsAtomicDecrIntT(&v->refcnt);
+ assert(ref>=0);
+ if(ref>0) return;
+
+
+ if (v->joinable) {
+ rtems_barrier_delete(v->join_barrier);
+ }
+ free (v->threadVariables);
+ free (v->name);
+ free (v);
+}
+
/*
* EPICS threads destroy themselves by returning from the thread entry function.
* This simple wrapper provides the same semantics on RTEMS.
@@ -190,9 +210,12 @@ threadWrapper (rtems_task_argument arg)
if (v->forw)
v->forw->back = v->back;
taskVarUnlock ();
- free (v->threadVariables);
- free (v->name);
- free (v);
+ if(v->joinable) {
+ rtems_status_code sc = rtems_barrier_wait(v->join_barrier, RTEMS_NO_TIMEOUT);
+ if(sc!=RTEMS_SUCCESSFUL)
+ cantProceed("oops %s\n", rtems_status_text(sc));
+ }
+ taskUnref(v);
rtems_task_delete (RTEMS_SELF);
}
@@ -203,21 +226,34 @@ void epicsThreadExitMain (void)
{
}
-static void
+static rtems_status_code
setThreadInfo(rtems_id tid, const char *name, EPICSTHREADFUNC funptr,
- void *parm)
+ void *parm, int joinable)
{
struct taskVar *v;
uint32_t note;
- rtems_status_code sc;
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
v = mallocMustSucceed (sizeof *v, "epicsThreadCreate_vars");
v->name = epicsStrDup(name);
v->id = tid;
v->funptr = funptr;
v->parm = parm;
+ v->joinable = joinable;
+ v->refcnt = joinable ? 2 : 1;
v->threadVariableCapacity = 0;
v->threadVariables = NULL;
+ if (joinable) {
+ char c[3];
+ strncpy(c, v->name, 3);
+ sc = rtems_barrier_create(rtems_build_name('~', c[0], c[1], c[2]),
+ RTEMS_BARRIER_AUTOMATIC_RELEASE | RTEMS_LOCAL,
+ 2, &v->join_barrier);
+ if (sc != RTEMS_SUCCESSFUL) {
+ free(v);
+ return sc;
+ }
+ }
note = (uint32_t)v;
rtems_task_set_note (tid, RTEMS_NOTEPAD_TASKVAR, note);
taskVarLock ();
@@ -229,10 +265,14 @@ setThreadInfo(rtems_id tid, const char *name, EPICSTHREADFUNC funptr,
taskVarUnlock ();
if (funptr) {
sc = rtems_task_start (tid, threadWrapper, (rtems_task_argument)v);
- if (sc != RTEMS_SUCCESSFUL)
- errlogPrintf ("setThreadInfo: Can't start %s: %s\n",
- name, rtems_status_text(sc));
}
+ if (sc != RTEMS_SUCCESSFUL) {
+ if (joinable) {
+ rtems_barrier_delete(v->join_barrier);
+ }
+ free(v);
+ }
+ return sc;
}
/*
@@ -254,7 +294,8 @@ epicsThreadInit (void)
if (!onceMutex || !taskVarMutex)
cantProceed("epicsThreadInit() can't create global mutexes\n");
rtems_task_ident (RTEMS_SELF, 0, &tid);
- setThreadInfo (tid, "_main_", NULL, NULL);
+ if(setThreadInfo (tid, "_main_", NULL, NULL, 0) != RTEMS_SUCCESSFUL)
+ cantProceed("epicsThreadInit() unable to setup _main_");
osdThreadHooksRunMain((epicsThreadId)tid);
initialized = 1;
epicsThreadCreate ("ImsgDaemon", 99,
@@ -300,7 +341,13 @@ epicsThreadCreateOpt (
name, rtems_status_text(sc));
return 0;
}
- setThreadInfo (tid, name, funptr,parm);
+ sc = setThreadInfo (tid, name, funptr, parm, opts->joinable);
+ if (sc != RTEMS_SUCCESSFUL) {
+ errlogPrintf ("epicsThreadCreate create failure during setup for %s: %s\n",
+ name, rtems_status_text(sc));
+ rtems_task_delete(tid);
+ return 0;
+ }
return (epicsThreadId)tid;
}
@@ -316,7 +363,41 @@ threadMustCreate (const char *name,
return tid;
}
-void epicsThreadJoin(epicsThreadId id) {}
+void epicsThreadJoin(epicsThreadId id)
+{
+ rtems_id target_tid = (rtems_id)id, self_tid;
+ struct taskVar *v;
+
+ rtems_task_ident (RTEMS_SELF, 0, &self_tid);
+
+ {
+ uint32_t note;
+ rtems_task_get_note (target_tid, RTEMS_NOTEPAD_TASKVAR, ¬e);
+ v = (void *)note;
+ }
+
+ if(!v->joinable) {
+ /* try to error nicely, however in all likelyhood rtems_task_get_note failed,
+ * or gave us the wrong thread as we are racing thread exit.
+ */
+ cantProceed("%s join not enabled for thread.\n", v->name);
+
+ } else if(target_tid!=self_tid) {
+ /* wait for target to complete */
+ rtems_status_code sc = rtems_barrier_wait(v->join_barrier, RTEMS_NO_TIMEOUT);
+ if(sc!=RTEMS_SUCCESSFUL)
+ cantProceed("oopsj %s\n", rtems_status_text(sc));
+
+ if(sc != RTEMS_SUCCESSFUL) {
+ errlogPrintf("epicsThreadJoin('%s') -> %s\n", v->name, rtems_status_text(sc));
+ }
+ }
+
+ taskUnref(v);
+ /* target task may be deleted.
+ * self task is not deleted, even for self join.
+ */
+}
void
epicsThreadSuspendSelf (void)
diff --git a/modules/libcom/src/osi/os/RTEMS/osdThread.h b/modules/libcom/src/osi/os/RTEMS/osdThread.h
index 7307603eb..84d579c0f 100644
--- a/modules/libcom/src/osi/os/RTEMS/osdThread.h
+++ b/modules/libcom/src/osi/os/RTEMS/osdThread.h
@@ -8,8 +8,8 @@
* in file LICENSE that is included with this distribution.
\*************************************************************************/
-/* This target does not support joining threads */
-#define EPICS_THREAD_CAN_JOIN (0)
+/* This target supports joining threads */
+#define EPICS_THREAD_CAN_JOIN (1)
int epicsThreadGetOssPriorityValue(unsigned int osiPriority);
From 530eba133fd1fc3af91457bad73220cfcfbbe2cc Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Sat, 16 Jun 2018 09:45:00 -0700
Subject: [PATCH 007/281] as,rsrv: use real client IP instead of untrusted host
name
---
modules/database/src/ioc/rsrv/camessage.c | 8 +++++
modules/database/src/ioc/rsrv/caservertask.c | 14 +++++++++
modules/database/src/ioc/rsrv/server.h | 2 +-
modules/libcom/src/as/asLib.h | 5 +++
modules/libcom/src/as/asLibRoutines.c | 32 +++++++++++++++++---
modules/libcom/src/iocsh/libComRegister.c | 6 ++++
6 files changed, 61 insertions(+), 6 deletions(-)
diff --git a/modules/database/src/ioc/rsrv/camessage.c b/modules/database/src/ioc/rsrv/camessage.c
index 72a4b17a1..40448d018 100644
--- a/modules/database/src/ioc/rsrv/camessage.c
+++ b/modules/database/src/ioc/rsrv/camessage.c
@@ -861,6 +861,14 @@ static int host_name_action ( caHdrLargeArray *mp, void *pPayload,
return RSRV_ERROR;
}
+ /* after all validation */
+ if(asUseIP) {
+
+ DLOG (2, ( "CAS: host_name_action for \"%s\" ignores clist provided host name\n",
+ client->pHostName ) );
+ return RSRV_OK;
+ }
+
/*
* user name will not change if there isnt enough memory
*/
diff --git a/modules/database/src/ioc/rsrv/caservertask.c b/modules/database/src/ioc/rsrv/caservertask.c
index f377d837f..048487b20 100644
--- a/modules/database/src/ioc/rsrv/caservertask.c
+++ b/modules/database/src/ioc/rsrv/caservertask.c
@@ -1421,6 +1421,20 @@ struct client *create_tcp_client (SOCKET sock , const osiSockAddr *peerAddr)
}
client->addr = peerAddr->ia;
+ if(asUseIP) {
+ epicsUInt32 ip = ntohl(client->addr.sin_addr.s_addr);
+ client->pHostName = malloc(24);
+ if(!client->pHostName) {
+ destroy_client ( client );
+ return NULL;
+ }
+ epicsSnprintf(client->pHostName, 24,
+ "%u.%u.%u.%u",
+ (ip>>24)&0xff,
+ (ip>>16)&0xff,
+ (ip>>8)&0xff,
+ (ip>>0)&0xff);
+ }
/*
* see TCP(4P) this seems to make unsolicited single events much
diff --git a/modules/database/src/ioc/rsrv/server.h b/modules/database/src/ioc/rsrv/server.h
index 4d502f77f..6392c692b 100644
--- a/modules/database/src/ioc/rsrv/server.h
+++ b/modules/database/src/ioc/rsrv/server.h
@@ -86,7 +86,7 @@ typedef struct client {
ELLLIST chanList;
ELLLIST chanPendingUpdateARList;
ELLLIST putNotifyQue;
- struct sockaddr_in addr;
+ struct sockaddr_in addr; /* peer address, TCP only */
epicsTimeStamp time_at_last_send;
epicsTimeStamp time_at_last_recv;
void *evuser;
diff --git a/modules/libcom/src/as/asLib.h b/modules/libcom/src/as/asLib.h
index 261e5ed7d..b4e5139ce 100644
--- a/modules/libcom/src/as/asLib.h
+++ b/modules/libcom/src/as/asLib.h
@@ -21,6 +21,11 @@
extern "C" {
#endif
+/* 0 - Use (unverified) client provided host name string.
+ * 1 - Use actual client IP address. HAG() are resolved to IPs at ACF load time.
+ */
+epicsShareExtern int asUseIP;
+
typedef struct asgMember *ASMEMBERPVT;
typedef struct asgClient *ASCLIENTPVT;
typedef int (*ASINPUTFUNCPTR)(char *buf,int max_size);
diff --git a/modules/libcom/src/as/asLibRoutines.c b/modules/libcom/src/as/asLibRoutines.c
index 3f5713efc..ceade030e 100644
--- a/modules/libcom/src/as/asLibRoutines.c
+++ b/modules/libcom/src/as/asLibRoutines.c
@@ -15,6 +15,8 @@
#include
#define epicsExportSharedSymbols
+#include "osiSock.h"
+#include "epicsTypes.h"
#include "epicsStdio.h"
#include "dbDefs.h"
#include "epicsThread.h"
@@ -27,6 +29,8 @@
#include "postfix.h"
#include "asLib.h"
+int asUseIP;
+
static epicsMutexId asLock;
#define LOCK epicsMutexMustLock(asLock)
#define UNLOCK epicsMutexUnlock(asLock)
@@ -1206,11 +1210,29 @@ static long asHagAddHost(HAG *phag,const char *host)
int len, i;
if (!phag) return 0;
- len = strlen(host);
- phagname = asCalloc(1, sizeof(HAGNAME) + len + 1);
- phagname->host = (char *)(phagname + 1);
- for (i = 0; i < len; i++) {
- phagname->host[i] = (char)tolower((int)host[i]);
+ if(!asUseIP) {
+ len = strlen(host);
+ phagname = asCalloc(1, sizeof(HAGNAME) + len + 1);
+ phagname->host = (char *)(phagname + 1);
+ for (i = 0; i < len; i++) {
+ phagname->host[i] = (char)tolower((int)host[i]);
+ }
+ } else {
+ struct sockaddr_in addr;
+ epicsUInt32 ip;
+ if(aToIPAddr(host, 0, &addr)) {
+ errlogPrintf("Unable to resolve host '%s'\n", host);
+ return S_asLib_noHag;
+ }
+ ip = ntohl(addr.sin_addr.s_addr);
+ phagname = asCalloc(1, sizeof(HAGNAME) + 24);
+ phagname->host = (char *)(phagname + 1);
+ epicsSnprintf(phagname->host, 24,
+ "%u.%u.%u.%u",
+ (ip>>24)&0xff,
+ (ip>>16)&0xff,
+ (ip>>8)&0xff,
+ (ip>>0)&0xff);
}
ellAdd(&phag->list, &phagname->node);
return 0;
diff --git a/modules/libcom/src/iocsh/libComRegister.c b/modules/libcom/src/iocsh/libComRegister.c
index 0d8c5678c..2bbb09f3e 100644
--- a/modules/libcom/src/iocsh/libComRegister.c
+++ b/modules/libcom/src/iocsh/libComRegister.c
@@ -12,6 +12,7 @@
#define epicsExportSharedSymbols
#include "iocsh.h"
+#include "asLib.h"
#include "epicsStdioRedirect.h"
#include "epicsString.h"
#include "epicsTime.h"
@@ -392,6 +393,8 @@ static void installLastResortEventProviderCallFunc(const iocshArgBuf *args)
installLastResortEventProvider();
}
+static iocshVarDef asUseIPDef = {"asUseIP", iocshArgInt, 0};
+
void epicsShareAPI libComRegister(void)
{
iocshRegister(&dateFuncDef, dateCallFunc);
@@ -424,4 +427,7 @@ void epicsShareAPI libComRegister(void)
iocshRegister(&generalTimeReportFuncDef,generalTimeReportCallFunc);
iocshRegister(&installLastResortEventProviderFuncDef, installLastResortEventProviderCallFunc);
+
+ asUseIPDef.pval = &asUseIP;
+ iocshRegisterVariable(&asUseIPDef);
}
From dced29c4752a224cafd85e9dc5ca65dea7b50960 Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Wed, 14 Nov 2018 14:41:45 -0800
Subject: [PATCH 008/281] asLib: test asUseIP
---
modules/libcom/test/aslibtest.c | 39 +++++++++++++++++++++++++++++++--
1 file changed, 37 insertions(+), 2 deletions(-)
diff --git a/modules/libcom/test/aslibtest.c b/modules/libcom/test/aslibtest.c
index 875aa56fd..367a12426 100644
--- a/modules/libcom/test/aslibtest.c
+++ b/modules/libcom/test/aslibtest.c
@@ -81,8 +81,8 @@ static const char hostname_config[] = ""
static void testHostNames(void)
{
-
testDiag("testHostNames()");
+ asUseIP = 0;
testOk1(asInitMem(hostname_config, NULL)==0);
@@ -109,11 +109,46 @@ static void testHostNames(void)
testAccess("ro", 0);
testAccess("rw", 0);
}
+
+static void testUseIP(void)
+{
+ testDiag("testUseIP()");
+ asUseIP = 1;
+
+ /* still host names in .acf */
+ testOk1(asInitMem(hostname_config, NULL)==0);
+ /* now resolved to IPs */
+
+ setUser("testing");
+ setHost("localhost"); /* will not match against resolved IP */
+ asAsl = 0;
+
+ testAccess("invalid", 0);
+ testAccess("DEFAULT", 0);
+ testAccess("ro", 0);
+ testAccess("rw", 0);
+
+ setHost("127.0.0.1");
+
+ testAccess("invalid", 0);
+ testAccess("DEFAULT", 0);
+ testAccess("ro", 1);
+ testAccess("rw", 3);
+
+ setHost("nosuchhost");
+
+ testAccess("invalid", 0);
+ testAccess("DEFAULT", 0);
+ testAccess("ro", 0);
+ testAccess("rw", 0);
+}
+
MAIN(aslibtest)
{
- testPlan(14);
+ testPlan(27);
testSyntaxErrors();
testHostNames();
+ testUseIP();
errlogFlush();
return testDone();
}
From 4ee3cbf382d0694dd06b9cb53bfcfd2752545ddf Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Wed, 14 Mar 2018 20:52:22 -0700
Subject: [PATCH 009/281] LINKER_USE_RPATH=ORIGIN
Use Linux specific linker trick to allow relocation
of built tree.
relative rpath
use $ORIGIN to reference libraries in other modules
by relative path.
fix rel. RPATH
---
configure/CONFIG_BASE | 2 ++
configure/CONFIG_COMMON | 2 ++
configure/CONFIG_SITE | 9 ++++++-
configure/RULES_BUILD | 7 ++++++
configure/os/CONFIG.Common.linuxCommon | 2 ++
src/tools/Makefile | 2 ++
src/tools/makeRPath.py | 33 ++++++++++++++++++++++++++
7 files changed, 56 insertions(+), 1 deletion(-)
create mode 100644 src/tools/makeRPath.py
diff --git a/configure/CONFIG_BASE b/configure/CONFIG_BASE
index 137ce0098..32349ebd5 100644
--- a/configure/CONFIG_BASE
+++ b/configure/CONFIG_BASE
@@ -44,6 +44,8 @@ FULLPATHNAME = $(PERL) $(TOOLS)/fullPathName.pl
TAPTOJUNIT = $(PERL) $(TOOLS)/tap-to-junit-xml.pl
GENVERSIONHEADER = $(PERL) $(TOOLS)/genVersionHeader.pl $(QUIET_FLAG) $(QUESTION_FLAG)
+MAKERPATH = $(PYTHON) $(TOOLS)/makeRPath.py
+
#---------------------------------------------------------------
# tools for installing libraries and products
INSTALL = $(PERL) $(TOOLS)/installEpics.pl $(QUIET_FLAG)
diff --git a/configure/CONFIG_COMMON b/configure/CONFIG_COMMON
index eef4d6745..d7766e50a 100644
--- a/configure/CONFIG_COMMON
+++ b/configure/CONFIG_COMMON
@@ -38,6 +38,8 @@ BUILD_ARCHS = $(EPICS_HOST_ARCH) $(CROSS1) $(CROSS2)
# otherwise override this in os/CONFIG_SITE..Common
PERL = perl -CSD
+PYTHON = python
+
#-------------------------------------------------------
# Check configure/RELEASE file for consistency
CHECK_RELEASE_YES = checkRelease
diff --git a/configure/CONFIG_SITE b/configure/CONFIG_SITE
index b657f5b65..49f82c7a8 100644
--- a/configure/CONFIG_SITE
+++ b/configure/CONFIG_SITE
@@ -169,10 +169,17 @@ EPICS_SITE_VERSION =
GCC_PIPE = NO
# Set RPATH when linking executables and libraries.
-# Must be either YES or NO. If you set this to NO you must also provide a
+# Must be either YES, NO, or ORIGIN. If you set this to NO you must also provide a
# way for Base executables to find their shared libraries when they are
# run at build-time, e.g. set the LD_LIBRARY_PATH environment variable.
+# ORIGIN is Linux specific.
LINKER_USE_RPATH = YES
+# Only used when LINKER_USE_RPATH=ORIGIN
+# The build time root of the relocatable tree.
+# Linking to libraries under this root directory will be relative.
+# Linking to libraries outside of this root will be absolute.
+LINKER_ORIGIN_ROOT = $(INSTALL_LOCATION)
+
# Overrides for the settings above may appear in a CONFIG_SITE.local file
-include $(CONFIG)/CONFIG_SITE.local
diff --git a/configure/RULES_BUILD b/configure/RULES_BUILD
index 21a838790..f2f4d8873 100644
--- a/configure/RULES_BUILD
+++ b/configure/RULES_BUILD
@@ -194,6 +194,13 @@ ifeq ($(EPICS_HOST_ARCH),$(T_A))
$(info Warning: RELEASE file consistency checks have been disabled)
endif
+# $(FINAL_DIR) signals eventual install locations to makeRPath script
+$(TESTPRODNAME): FINAL_DIR=.
+$(PRODNAME): FINAL_DIR=$(INSTALL_BIN)
+$(TESTSHRLIBNAME): FINAL_DIR=.
+$(SHRLIBNAME): FINAL_DIR=$(INSTALL_SHRLIB)
+$(LOADABLE_SHRLIBNAME): FINAL_DIR=$(INSTALL_SHRLIB)
+
#---------------------------------------------------------------
# The order of the following rules is
# VERY IMPORTANT !!!!
diff --git a/configure/os/CONFIG.Common.linuxCommon b/configure/os/CONFIG.Common.linuxCommon
index 965de09b8..e8e9ab3ca 100644
--- a/configure/os/CONFIG.Common.linuxCommon
+++ b/configure/os/CONFIG.Common.linuxCommon
@@ -25,11 +25,13 @@ STATIC_LDLIBS_YES= -Wl,-Bdynamic
# Set runtime path for shared libraries if USE_RPATH=YES and STATIC_BUILD=NO
SHRLIBDIR_RPATH_LDFLAGS_YES_NO = $(SHRLIB_DEPLIB_DIRS:%=-Wl,-rpath,%)
+SHRLIBDIR_RPATH_LDFLAGS_ORIGIN_NO = $(shell $(MAKERPATH) -O '\$$ORIGIN' -F $(FINAL_DIR) -R $(LINKER_ORIGIN_ROOT) $(SHRLIB_DEPLIB_DIRS))
SHRLIBDIR_LDFLAGS += \
$(SHRLIBDIR_RPATH_LDFLAGS_$(LINKER_USE_RPATH)_$(STATIC_BUILD))
# Set runtime path for products if USE_RPATH=YES and STATIC_BUILD=NO
PRODDIR_RPATH_LDFLAGS_YES_NO = $(PROD_DEPLIB_DIRS:%=-Wl,-rpath,%)
+PRODDIR_RPATH_LDFLAGS_ORIGIN_NO = $(shell $(MAKERPATH) -O '\$$ORIGIN' -F $(FINAL_DIR) -R $(LINKER_ORIGIN_ROOT) $(PROD_DEPLIB_DIRS))
PRODDIR_LDFLAGS += \
$(PRODDIR_RPATH_LDFLAGS_$(LINKER_USE_RPATH)_$(STATIC_BUILD))
diff --git a/src/tools/Makefile b/src/tools/Makefile
index 39497a417..8c4f25adc 100644
--- a/src/tools/Makefile
+++ b/src/tools/Makefile
@@ -39,6 +39,8 @@ PERL_SCRIPTS += tap-to-junit-xml.pl
PERL_SCRIPTS += useManifestTool.pl
PERL_SCRIPTS += genVersionHeader.pl
+PERL_SCRIPTS += makeRPath.py
+
HTMLS = style.css
HTMLS += EPICS/Getopts.html
HTMLS += EPICS/Path.html
diff --git a/src/tools/makeRPath.py b/src/tools/makeRPath.py
new file mode 100644
index 000000000..edad80085
--- /dev/null
+++ b/src/tools/makeRPath.py
@@ -0,0 +1,33 @@
+#!/usr/bin/env python
+
+from __future__ import print_function
+
+import sys
+import os
+
+from argparse import ArgumentParser
+
+if os.environ.get('EPICS_DEBUG_RPATH','')=='YES':
+ sys.stderr.write('%s'%sys.argv)
+
+P = ArgumentParser()
+P.add_argument('-F','--final',default=os.getcwd(), help='Final install location for ELF file')
+P.add_argument('-R','--root',default='/')
+P.add_argument('-O', '--origin', default='$ORIGIN')
+P.add_argument('path', nargs='*')
+args = P.parse_args()
+
+fdir = os.path.abspath(args.final)
+
+output = []
+for path in args.path:
+ path = os.path.abspath(path)
+
+ if args.root and os.path.relpath(path, args.root).startswith('../'):
+ pass # absolute rpath
+ else:
+ path = os.path.relpath(path, fdir)
+
+ output.append('-Wl,-rpath,'+os.path.join(args.origin, path))
+
+print(' '.join(output))
From 32340584b4fee7e3080e8d19332d959ef6de0e0a Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Wed, 25 Apr 2018 22:57:03 -0700
Subject: [PATCH 010/281] epicsGetExecDir() paths relative to executable
For linux, enable softIoc to find .dbd relative to the executable
location.
The same could be done for other targets
*bsd
may have symlink /proc/curproc/file
fallback to sysctl() with KERN_PROC_PATHNAME
solaris
getexecname()
mac
_NSGetExecutablePath()
WIN32
GetModuleFileName(NULL)
others
out of luck...
---
modules/database/src/std/softIoc/softMain.cpp | 41 ++++++++++++++-
modules/libcom/src/misc/unixFileName.h | 22 ++++++++
modules/libcom/src/osi/Makefile | 1 +
modules/libcom/src/osi/os/Linux/osdgetexec.c | 50 +++++++++++++++++++
modules/libcom/src/osi/os/WIN32/osiFileName.h | 22 ++++++++
.../libcom/src/osi/os/cygwin32/osiFileName.h | 22 ++++++++
.../libcom/src/osi/os/default/osdgetexec.c | 14 ++++++
7 files changed, 170 insertions(+), 2 deletions(-)
create mode 100644 modules/libcom/src/osi/os/Linux/osdgetexec.c
create mode 100644 modules/libcom/src/osi/os/default/osdgetexec.c
diff --git a/modules/database/src/std/softIoc/softMain.cpp b/modules/database/src/std/softIoc/softMain.cpp
index 8400a6554..01ef19b2f 100644
--- a/modules/database/src/std/softIoc/softMain.cpp
+++ b/modules/database/src/std/softIoc/softMain.cpp
@@ -62,23 +62,59 @@
#include "epicsThread.h"
#include "epicsExit.h"
#include "epicsStdio.h"
+#include "epicsString.h"
#include "dbStaticLib.h"
#include "subRecord.h"
#include "dbAccess.h"
#include "asDbLib.h"
#include "iocInit.h"
#include "iocsh.h"
+#include "osiFileName.h"
#include "epicsInstallDir.h"
extern "C" int softIoc_registerRecordDeviceDriver(struct dbBase *pdbbase);
-#define DBD_FILE EPICS_BASE "/dbd/softIoc.dbd"
-#define EXIT_FILE EPICS_BASE "/db/softIocExit.db"
+#define DBD_BASE "dbd/softIoc.dbd"
+#define EXIT_BASE "db/softIocExit.db"
+#define DBD_FILE_REL "../../" DBD_BASE
+#define EXIT_FILE_REL "../../" EXIT_BASE
+#define DBD_FILE EPICS_BASE "/" DBD_BASE
+#define EXIT_FILE EPICS_BASE "/" EXIT_BASE
const char *arg0;
const char *base_dbd = DBD_FILE;
const char *exit_db = EXIT_FILE;
+static void preparePath(void)
+{
+ FILE *fp;
+ char *prefix = epicsGetExecDir();
+ char *dbd, *exit;
+ if(!prefix) return;
+
+ dbd = (char*)malloc(strlen(prefix) + strlen(DBD_FILE_REL) + 1);
+ if(dbd) {
+ dbd[0] = '\0';
+ strcat(dbd, prefix);
+ strcat(dbd, DBD_FILE_REL);
+ printf("Testing '%s'\n", dbd);
+ if((fp = fopen(dbd, "rb"))!=NULL) {
+ fclose(fp);
+ base_dbd = dbd;
+ }
+ }
+
+ exit = (char*)malloc(strlen(prefix) + strlen(EXIT_FILE_REL) + 1);
+ if(exit) {
+ exit[0] = '\0';
+ strcat(exit, prefix);
+ strcat(exit, EXIT_FILE_REL);
+ if((fp = fopen(exit, "rb"))!=NULL) {
+ fclose(fp);
+ exit_db = exit;
+ }
+ }
+}
static void exitSubroutine(subRecord *precord) {
epicsExitLater((precord->a == 0.0) ? EXIT_SUCCESS : EXIT_FAILURE);
@@ -96,6 +132,7 @@ static void usage(int status) {
int main(int argc, char *argv[])
{
+ preparePath();
char *dbd_file = const_cast(base_dbd);
char *macros = NULL;
char xmacro[PVNAME_STRINGSZ + 4];
diff --git a/modules/libcom/src/misc/unixFileName.h b/modules/libcom/src/misc/unixFileName.h
index 36e818c8f..9d7af252c 100644
--- a/modules/libcom/src/misc/unixFileName.h
+++ b/modules/libcom/src/misc/unixFileName.h
@@ -14,7 +14,29 @@
#ifndef unixFileNameH
#define unixFileNameH
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#define OSI_PATH_LIST_SEPARATOR ":"
#define OSI_PATH_SEPARATOR "/"
+/** Return the absolute path of the current executable.
+ @returns NULL or the path. Caller must free()
+ */
+epicsShareFunc
+char *epicsGetExecName(void);
+
+/** Return the absolute path of the directory containing the current executable.
+ @returns NULL or the path. Caller must free()
+ */
+epicsShareFunc
+char *epicsGetExecDir(void);
+
+#ifdef __cplusplus
+}
+#endif
+
#endif /* unixFileNameH */
diff --git a/modules/libcom/src/osi/Makefile b/modules/libcom/src/osi/Makefile
index ecbf4c23b..0352e9ffe 100644
--- a/modules/libcom/src/osi/Makefile
+++ b/modules/libcom/src/osi/Makefile
@@ -123,6 +123,7 @@ Com_SRCS += osdMonotonic.c
Com_SRCS += osdProcess.c
Com_SRCS += osdNetIntf.c
Com_SRCS += osdMessageQueue.c
+Com_SRCS += osdgetexec.c
Com_SRCS += devLibVME.c
Com_SRCS += devLibVMEOSD.c
diff --git a/modules/libcom/src/osi/os/Linux/osdgetexec.c b/modules/libcom/src/osi/os/Linux/osdgetexec.c
new file mode 100644
index 000000000..2ea8ca4ae
--- /dev/null
+++ b/modules/libcom/src/osi/os/Linux/osdgetexec.c
@@ -0,0 +1,50 @@
+
+#include
+#include
+#include
+#include
+
+#define epicsExportSharedSymbols
+#include
+
+char *epicsGetExecName(void)
+{
+ size_t max = PATH_MAX;
+ char *ret = NULL;
+ ssize_t n;
+
+ while(1) {
+ char *temp = realloc(ret, max);
+ if(!temp) {
+ /* we treat alloc failure as terminal */
+ free(ret);
+ ret = NULL;
+ break;
+ }
+ ret = temp;
+
+ n = readlink("/proc/self/exe", ret, max);
+ if(n < max) {
+ /* readlink() never adds a nil */
+ ret[n] = '\0';
+ break;
+ }
+
+ max += 64;
+ }
+
+ return ret;
+}
+
+char *epicsGetExecDir(void)
+{
+ char *ret = epicsGetExecName();
+ if(ret) {
+ char *sep = strrchr(ret, '/');
+ if(sep) {
+ /* nil the charactor after the / */
+ sep[1] = '\0';
+ }
+ }
+ return ret;
+}
diff --git a/modules/libcom/src/osi/os/WIN32/osiFileName.h b/modules/libcom/src/osi/os/WIN32/osiFileName.h
index 6ff0308b2..ced745f71 100644
--- a/modules/libcom/src/osi/os/WIN32/osiFileName.h
+++ b/modules/libcom/src/osi/os/WIN32/osiFileName.h
@@ -15,7 +15,29 @@
#ifndef osiFileNameH
#define osiFileNameH
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#define OSI_PATH_LIST_SEPARATOR ";"
#define OSI_PATH_SEPARATOR "\\"
+/** Return the absolute path of the current executable.
+ @returns NULL or the path. Caller must free()
+ */
+epicsShareFunc
+char *epicsGetExecName(void);
+
+/** Return the absolute path of the directory containing the current executable.
+ @returns NULL or the path. Caller must free()
+ */
+epicsShareFunc
+char *epicsGetExecDir(void);
+
+#ifdef __cplusplus
+}
+#endif
+
#endif /* osiFileNameH */
diff --git a/modules/libcom/src/osi/os/cygwin32/osiFileName.h b/modules/libcom/src/osi/os/cygwin32/osiFileName.h
index 6d7fd6eb9..1e7799098 100644
--- a/modules/libcom/src/osi/os/cygwin32/osiFileName.h
+++ b/modules/libcom/src/osi/os/cygwin32/osiFileName.h
@@ -14,7 +14,29 @@
#ifndef osiFileNameH
#define osiFileNameH
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#define OSI_PATH_LIST_SEPARATOR ";"
#define OSI_PATH_SEPARATOR "\\"
+/** Return the absolute path of the current executable.
+ @returns NULL or the path. Caller must free()
+ */
+epicsShareFunc
+char *epicsGetExecName(void);
+
+/** Return the absolute path of the directory containing the current executable.
+ @returns NULL or the path. Caller must free()
+ */
+epicsShareFunc
+char *epicsGetExecDir(void);
+
+#ifdef __cplusplus
+}
+#endif
+
#endif /* osiFileNameH */
diff --git a/modules/libcom/src/osi/os/default/osdgetexec.c b/modules/libcom/src/osi/os/default/osdgetexec.c
new file mode 100644
index 000000000..0bec9ead9
--- /dev/null
+++ b/modules/libcom/src/osi/os/default/osdgetexec.c
@@ -0,0 +1,14 @@
+#include
+
+#define epicsExportSharedSymbols
+#include
+
+char *epicsGetExecName(void)
+{
+ return NULL;
+}
+
+char *epicsGetExecDir(void)
+{
+ return NULL;
+}
From 84831e13e7bfea253c5a545c1c9c9f059d4b02bb Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Thu, 26 Apr 2018 14:33:58 -0700
Subject: [PATCH 011/281] epicsGetExecName WIN32, Darwin, solaris, freebsd
---
modules/libcom/src/osi/os/Darwin/osdgetexec.c | 50 ++++++++++++++
modules/libcom/src/osi/os/Linux/osdgetexec.c | 6 +-
modules/libcom/src/osi/os/WIN32/osdgetexec.c | 52 ++++++++++++++
.../libcom/src/osi/os/freebsd/osdgetexec.c | 68 +++++++++++++++++++
.../libcom/src/osi/os/solaris/osdgetexec.c | 30 ++++++++
modules/libcom/test/Makefile | 5 ++
modules/libcom/test/testexecname.c | 24 +++++++
7 files changed, 234 insertions(+), 1 deletion(-)
create mode 100644 modules/libcom/src/osi/os/Darwin/osdgetexec.c
create mode 100644 modules/libcom/src/osi/os/WIN32/osdgetexec.c
create mode 100644 modules/libcom/src/osi/os/freebsd/osdgetexec.c
create mode 100644 modules/libcom/src/osi/os/solaris/osdgetexec.c
create mode 100644 modules/libcom/test/testexecname.c
diff --git a/modules/libcom/src/osi/os/Darwin/osdgetexec.c b/modules/libcom/src/osi/os/Darwin/osdgetexec.c
new file mode 100644
index 000000000..4e4961c59
--- /dev/null
+++ b/modules/libcom/src/osi/os/Darwin/osdgetexec.c
@@ -0,0 +1,50 @@
+
+#include
+#include
+
+#include
+
+#define epicsExportSharedSymbols
+#include
+
+char *epicsGetExecName(void)
+{
+ uint32_t max = 64u;
+ char *ret = NULL;
+
+ while(1) {
+ char *temp = realloc(ret, max);
+ if(!temp) {
+ /* we treat alloc failure as terminal */
+ free(ret);
+ ret = NULL;
+ break;
+ }
+ ret = temp;
+
+ /* cf. "man 3 dyld" */
+ if(_NSGetExecutablePath(ret, &max)==0) {
+ /* max left unchanged */
+ ret[max-1] = '\0';
+ break;
+ }
+ /* max has been updated with required size */
+ }
+
+ /* TODO: _NSGetExecutablePath() doesn't follow symlinks */
+
+ return ret;
+}
+
+char *epicsGetExecDir(void)
+{
+ char *ret = epicsGetExecName();
+ if(ret) {
+ char *sep = strrchr(ret, '/');
+ if(sep) {
+ /* nil the charactor after the / */
+ sep[1] = '\0';
+ }
+ }
+ return ret;
+}
diff --git a/modules/libcom/src/osi/os/Linux/osdgetexec.c b/modules/libcom/src/osi/os/Linux/osdgetexec.c
index 2ea8ca4ae..cba5d78a8 100644
--- a/modules/libcom/src/osi/os/Linux/osdgetexec.c
+++ b/modules/libcom/src/osi/os/Linux/osdgetexec.c
@@ -24,7 +24,11 @@ char *epicsGetExecName(void)
ret = temp;
n = readlink("/proc/self/exe", ret, max);
- if(n < max) {
+ if(n == -1) {
+ free(ret);
+ ret = NULL;
+ break;
+ } else if(n < max) {
/* readlink() never adds a nil */
ret[n] = '\0';
break;
diff --git a/modules/libcom/src/osi/os/WIN32/osdgetexec.c b/modules/libcom/src/osi/os/WIN32/osdgetexec.c
new file mode 100644
index 000000000..a46ce50cd
--- /dev/null
+++ b/modules/libcom/src/osi/os/WIN32/osdgetexec.c
@@ -0,0 +1,52 @@
+
+#include
+#include
+#include
+
+#define epicsExportSharedSymbols
+#include
+
+char *epicsGetExecName(void)
+{
+ size_t max = 128;
+ char *ret = NULL;
+ DWORD n;
+
+ while(1) {
+ char *temp = realloc(ret, max);
+ if(!temp) {
+ /* we treat alloc failure as terminal */
+ free(ret);
+ ret = NULL;
+ break;
+ }
+ ret = temp;
+
+ n = GetModuleFileName(NULL, ret, max);
+ if(n == 0) {
+ free(ret);
+ ret = NULL;
+ break;
+ } else if(n < max) {
+ ret[n] = '\0';
+ break;
+ }
+
+ max += 64;
+ }
+
+ return ret;
+}
+
+char *epicsGetExecDir(void)
+{
+ char *ret = epicsGetExecName();
+ if(ret) {
+ char *sep = strrchr(ret, '\\');
+ if(sep) {
+ /* nil the charactor after the / */
+ sep[1] = '\0';
+ }
+ }
+ return ret;
+}
diff --git a/modules/libcom/src/osi/os/freebsd/osdgetexec.c b/modules/libcom/src/osi/os/freebsd/osdgetexec.c
new file mode 100644
index 000000000..39c0a163d
--- /dev/null
+++ b/modules/libcom/src/osi/os/freebsd/osdgetexec.c
@@ -0,0 +1,68 @@
+
+#include
+#include
+#include
+#include
+
+#define epicsExportSharedSymbols
+#include
+
+char *epicsGetExecName(void)
+{
+ size_t max = PATH_MAX;
+ char *ret = NULL;
+ ssize_t n;
+
+ while(1) {
+ char *temp = realloc(ret, max);
+ if(!temp) {
+ /* we treat alloc failure as terminal */
+ free(ret);
+ ret = NULL;
+ break;
+ }
+ ret = temp;
+
+ n = readlink("/proc/curproc/file", ret, max);
+ if(n == -1) {
+ free(ret);
+ ret = NULL;
+ break;
+ } else if(n < max) {
+ /* readlink() never adds a nil */
+ ret[n] = '\0';
+ break;
+ }
+
+ max += 64;
+ }
+
+ if(!ret) {
+ int mib[4];
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROC;
+ mib[2] = KERN_PROC_PATHNAME;
+ mib[3] = -1;
+
+ ret = malloc(max);
+ if(ret) {
+ sysctl(mib, 4, ret, &cb, NULL, 0);
+ /* TODO: error check */
+ }
+ }
+
+ return ret;
+}
+
+char *epicsGetExecDir(void)
+{
+ char *ret = epicsGetExecName();
+ if(ret) {
+ char *sep = strrchr(ret, '/');
+ if(sep) {
+ /* nil the charactor after the / */
+ sep[1] = '\0';
+ }
+ }
+ return ret;
+}
diff --git a/modules/libcom/src/osi/os/solaris/osdgetexec.c b/modules/libcom/src/osi/os/solaris/osdgetexec.c
new file mode 100644
index 000000000..ff9739ca9
--- /dev/null
+++ b/modules/libcom/src/osi/os/solaris/osdgetexec.c
@@ -0,0 +1,30 @@
+
+#include
+#include
+
+#define epicsExportSharedSymbols
+#include
+
+char *epicsGetExecName(void)
+{
+ const char *raw = getexecname();
+ char *ret = NULL;
+ /* manpage says getexecname() might return a relative path. we treat this as an error */
+ if(raw[0]=='/') {
+ ret = strdup(raw);
+ }
+ return ret;
+}
+
+char *epicsGetExecDir(void)
+{
+ char *ret = epicsGetExecName();
+ if(ret) {
+ char *sep = strrchr(ret, '/');
+ if(sep) {
+ /* nil the charactor after the / */
+ sep[1] = '\0';
+ }
+ }
+ return ret;
+}
diff --git a/modules/libcom/test/Makefile b/modules/libcom/test/Makefile
index 1c26c44b7..7e1ff1abc 100755
--- a/modules/libcom/test/Makefile
+++ b/modules/libcom/test/Makefile
@@ -232,6 +232,11 @@ osiSockTest_SRCS += osiSockTest.c
testHarness_SRCS += osiSockTest.c
TESTS += osiSockTest
+TESTPROD_HOST += testexecname
+testexecname_SRCS += testexecname.c
+# no point in including in testHarness. Not implemented for RTEMS/vxWorks.
+TESTS += testexecname
+
ifeq ($(BUILD_CLASS),HOST)
ifneq ($(OS_CLASS),WIN32)
# This test can only be run on a build host, and is broken on Windows
diff --git a/modules/libcom/test/testexecname.c b/modules/libcom/test/testexecname.c
new file mode 100644
index 000000000..87be847a0
--- /dev/null
+++ b/modules/libcom/test/testexecname.c
@@ -0,0 +1,24 @@
+
+#include
+
+#include
+#include
+
+#include
+
+MAIN(testexecname)
+{
+ testPlan(1);
+
+ {
+ char *buf = epicsGetExecName();
+ if(!buf) {
+ testSkip(1, "epicsGetExecName() not available for this target");
+ } else {
+ char *loc = strstr(buf, "testexecname");
+ testOk(!!loc, "Find \"testexecname\" in \"%s\"", buf);
+ }
+ }
+
+ return testDone();
+}
From 7bdbded47d78232d7871f523ea0d713c76d4deef Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Tue, 20 Nov 2018 21:59:34 -0800
Subject: [PATCH 012/281] travis-ci test rpath $ORIGIN
---
.travis.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.travis.yml b/.travis.yml
index d6130c753..e7b1ed57a 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -15,7 +15,7 @@ addons:
script:
- .ci/travis-build.sh
env:
- - CMPLR=gcc
+ - CMPLR=gcc EXTRA=LINKER_USE_RPATH=ORIGIN
- CMPLR=clang
- CMPLR=gcc STATIC=YES
- CMPLR=clang STATIC=YES
From f5194b2274360cabab8ce9097ed31665f07e4ef4 Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Thu, 13 Dec 2018 11:22:07 -0800
Subject: [PATCH 013/281] older binutils compat
---
src/tools/makeRPath.py | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/src/tools/makeRPath.py b/src/tools/makeRPath.py
index edad80085..5d151a41a 100644
--- a/src/tools/makeRPath.py
+++ b/src/tools/makeRPath.py
@@ -26,6 +26,10 @@ for path in args.path:
if args.root and os.path.relpath(path, args.root).startswith('../'):
pass # absolute rpath
else:
+ # some older binutils don't seem to handle $ORIGIN correctly
+ # when locating dependencies of libraries. So also provide
+ # the absolute path for internal use by 'ld' only.
+ output.append('-Wl,-rpath-link,'+path)
path = os.path.relpath(path, fdir)
output.append('-Wl,-rpath,'+os.path.join(args.origin, path))
From 5087c4cb2f9897828829f5b15076ab29d8106ac3 Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Sun, 23 Dec 2018 20:44:57 -0800
Subject: [PATCH 014/281] monotonic adapt to regular time APIs
---
modules/libcom/src/osi/epicsGeneralTime.c | 8 ++++++++
modules/libcom/src/osi/epicsTime.cpp | 7 +++++++
modules/libcom/src/osi/epicsTime.h | 2 ++
3 files changed, 17 insertions(+)
diff --git a/modules/libcom/src/osi/epicsGeneralTime.c b/modules/libcom/src/osi/epicsGeneralTime.c
index d08e9c6a9..10854ae55 100644
--- a/modules/libcom/src/osi/epicsGeneralTime.c
+++ b/modules/libcom/src/osi/epicsGeneralTime.c
@@ -204,6 +204,14 @@ int epicsShareAPI epicsTimeGetCurrent(epicsTimeStamp *pDest)
return status;
}
+int epicsTimeGetMonotonic ( epicsTimeStamp * pDest )
+{
+ epicsUInt64 now = epicsMonotonicGet();
+ pDest->nsec = now%1000000000ul;
+ pDest->secPastEpoch = now/1000000000ul;
+ return 0;
+}
+
int epicsTimeGetCurrentInt(epicsTimeStamp *pDest)
{
gtProvider *ptp = gtPvt.lastTimeProvider;
diff --git a/modules/libcom/src/osi/epicsTime.cpp b/modules/libcom/src/osi/epicsTime.cpp
index 6dd1d3f97..ddced3a65 100644
--- a/modules/libcom/src/osi/epicsTime.cpp
+++ b/modules/libcom/src/osi/epicsTime.cpp
@@ -218,6 +218,13 @@ epicsTime epicsTime::getCurrent ()
return epicsTime ( current );
}
+epicsTime epicsTime::getMonotonic()
+{
+ epicsTimeStamp current;
+ epicsTimeGetMonotonic (¤t); // can't fail
+ return epicsTime ( current );
+}
+
epicsTime epicsTime::getEvent (const epicsTimeEvent &event)
{
epicsTimeStamp current;
diff --git a/modules/libcom/src/osi/epicsTime.h b/modules/libcom/src/osi/epicsTime.h
index 862bc22d2..90ad4e1d0 100644
--- a/modules/libcom/src/osi/epicsTime.h
+++ b/modules/libcom/src/osi/epicsTime.h
@@ -86,6 +86,7 @@ public:
static epicsTime getEvent ( const epicsTimeEvent & );
static epicsTime getCurrent ();
+ static epicsTime getMonotonic ();
/* convert to and from EPICS epicsTimeStamp format */
operator epicsTimeStamp () const;
@@ -192,6 +193,7 @@ extern "C" {
epicsShareFunc int epicsShareAPI epicsTimeGetCurrent ( epicsTimeStamp * pDest );
epicsShareFunc int epicsShareAPI epicsTimeGetEvent (
epicsTimeStamp *pDest, int eventNumber);
+epicsShareFunc int epicsTimeGetMonotonic ( epicsTimeStamp * pDest );
/* These are callable from an Interrupt Service Routine */
epicsShareFunc int epicsTimeGetCurrentInt(epicsTimeStamp *pDest);
From 8b9ad212c4eb43dca632bcc08d47677081d6fa70 Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Sun, 23 Dec 2018 20:45:21 -0800
Subject: [PATCH 015/281] dbScan periodic scan use monotonic time
---
modules/database/src/ioc/db/dbScan.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/modules/database/src/ioc/db/dbScan.c b/modules/database/src/ioc/db/dbScan.c
index 9224ed504..268e89c49 100644
--- a/modules/database/src/ioc/db/dbScan.c
+++ b/modules/database/src/ioc/db/dbScan.c
@@ -787,7 +787,7 @@ static void periodicTask(void *arg)
taskwdInsert(0, NULL, NULL);
epicsEventSignal(startStopEvent);
- epicsTimeGetCurrent(&next);
+ epicsTimeGetMonotonic(&next);
reported = next;
while (ppsl->scanCtl != ctlExit) {
@@ -798,7 +798,7 @@ static void periodicTask(void *arg)
scanList(&ppsl->scan_list);
epicsTimeAddSeconds(&next, ppsl->period);
- epicsTimeGetCurrent(&now);
+ epicsTimeGetMonotonic(&now);
delay = epicsTimeDiffInSeconds(&next, &now);
if (delay <= 0.0) {
if (overtime == 0.0) {
From 4f2228fb1d7527fb5ebc8b2d747c309f1dd7698d Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Sun, 23 Dec 2018 20:48:58 -0800
Subject: [PATCH 016/281] replace most internal getCurrent() -> getMonotonic()
every place where a time delta is computed, and then some.
---
modules/ca/src/client/CASG.cpp | 4 ++--
modules/ca/src/client/ca_client_context.cpp | 8 ++++----
modules/ca/src/client/cac.cpp | 2 +-
modules/ca/src/client/casw.cpp | 4 ++--
modules/ca/src/client/searchTimer.cpp | 2 +-
modules/ca/src/client/tcpiiu.cpp | 4 ++--
modules/ca/src/client/udpiiu.cpp | 2 +-
.../database/src/ioc/db/dbPutNotifyBlocker.cpp | 4 ++--
modules/libcom/src/fdmgr/fdManager.cpp | 6 +++---
modules/libcom/src/osi/epicsThread.cpp | 4 ++--
modules/libcom/src/timer/epicsTimer.cpp | 2 +-
modules/libcom/src/timer/epicsTimer.h | 2 +-
modules/libcom/src/timer/timer.cpp | 6 +++---
modules/libcom/src/timer/timerQueue.cpp | 4 ++--
modules/libcom/src/timer/timerQueueActive.cpp | 4 ++--
modules/libcom/test/cvtFastPerform.cpp | 4 ++--
modules/libcom/test/epicsAtomicPerform.cpp | 12 ++++++------
modules/libcom/test/epicsEventTest.cpp | 4 ++--
modules/libcom/test/epicsMutexTest.cpp | 12 ++++++------
modules/libcom/test/epicsThreadPerform.cpp | 16 ++++++++--------
modules/libcom/test/epicsTimerTest.cpp | 8 ++++----
21 files changed, 57 insertions(+), 57 deletions(-)
diff --git a/modules/ca/src/client/CASG.cpp b/modules/ca/src/client/CASG.cpp
index b2dbaac4d..4ffb414c7 100644
--- a/modules/ca/src/client/CASG.cpp
+++ b/modules/ca/src/client/CASG.cpp
@@ -83,7 +83,7 @@ int CASG::block (
return ECA_TIMEOUT;
}
- cur_time = epicsTime::getCurrent ();
+ cur_time = epicsTime::getMonotonic ();
this->client.flush ( guard );
@@ -121,7 +121,7 @@ int CASG::block (
/*
* force a time update
*/
- cur_time = epicsTime::getCurrent ();
+ cur_time = epicsTime::getMonotonic ();
delay = cur_time - beg_time;
}
diff --git a/modules/ca/src/client/ca_client_context.cpp b/modules/ca/src/client/ca_client_context.cpp
index 2bb3e24a8..3305e8d3e 100644
--- a/modules/ca/src/client/ca_client_context.cpp
+++ b/modules/ca/src/client/ca_client_context.cpp
@@ -481,7 +481,7 @@ int ca_client_context::pendIO ( const double & timeout )
}
int status = ECA_NORMAL;
- epicsTime beg_time = epicsTime::getCurrent ();
+ epicsTime beg_time = epicsTime::getMonotonic ();
double remaining = timeout;
epicsGuard < epicsMutex > guard ( this->mutex );
@@ -499,7 +499,7 @@ int ca_client_context::pendIO ( const double & timeout )
this->blockForEventAndEnableCallbacks ( this->ioDone, remaining );
}
- double delay = epicsTime::getCurrent () - beg_time;
+ double delay = epicsTime::getMonotonic () - beg_time;
if ( delay < timeout ) {
remaining = timeout - delay;
}
@@ -528,7 +528,7 @@ int ca_client_context::pendEvent ( const double & timeout )
return ECA_EVDISALLOW;
}
- epicsTime current = epicsTime::getCurrent ();
+ epicsTime current = epicsTime::getMonotonic ();
{
epicsGuard < epicsMutex > guard ( this->mutex );
@@ -569,7 +569,7 @@ int ca_client_context::pendEvent ( const double & timeout )
this->noWakeupSincePend = true;
}
- double elapsed = epicsTime::getCurrent() - current;
+ double elapsed = epicsTime::getMonotonic() - current;
double delay;
if ( timeout > elapsed ) {
diff --git a/modules/ca/src/client/cac.cpp b/modules/ca/src/client/cac.cpp
index 5748353bd..485b83d89 100644
--- a/modules/ca/src/client/cac.cpp
+++ b/modules/ca/src/client/cac.cpp
@@ -130,7 +130,7 @@ cac::cac (
epicsMutex & callbackControlIn,
cacContextNotify & notifyIn ) :
_refLocalHostName ( localHostNameCache.getReference () ),
- programBeginTime ( epicsTime::getCurrent() ),
+ programBeginTime ( epicsTime::getMonotonic() ),
connTMO ( CA_CONN_VERIFY_PERIOD ),
mutex ( mutualExclusionIn ),
cbMutex ( callbackControlIn ),
diff --git a/modules/ca/src/client/casw.cpp b/modules/ca/src/client/casw.cpp
index f69632c13..bdb08e04d 100644
--- a/modules/ca/src/client/casw.cpp
+++ b/modules/ca/src/client/casw.cpp
@@ -59,7 +59,7 @@ int main ( int argc, char ** argv )
epicsMutex mutex;
epicsGuard < epicsMutex > guard ( mutex );
bheFreeStoreMgr bheFreeList;
- epicsTime programBeginTime = epicsTime::getCurrent ();
+ epicsTime programBeginTime = epicsTime::getMonotonic ();
bool validCommandLine = false;
unsigned interest = 0u;
SOCKET sock;
@@ -244,7 +244,7 @@ int main ( int argc, char ** argv )
ca_uint32_t beaconNumber = ntohl ( pCurMsg->m_cid );
unsigned protocolRevision = ntohs ( pCurMsg->m_dataType );
- epicsTime currentTime = epicsTime::getCurrent();
+ epicsTime currentTime = epicsTime::getMonotonic();
/*
* look for it in the hash table
diff --git a/modules/ca/src/client/searchTimer.cpp b/modules/ca/src/client/searchTimer.cpp
index e55e49eab..bb9b9a91c 100644
--- a/modules/ca/src/client/searchTimer.cpp
+++ b/modules/ca/src/client/searchTimer.cpp
@@ -43,7 +43,7 @@ searchTimer::searchTimer (
const unsigned indexIn,
epicsMutex & mutexIn,
bool boostPossibleIn ) :
- timeAtLastSend ( epicsTime::getCurrent () ),
+ timeAtLastSend ( epicsTime::getMonotonic () ),
timer ( queueIn.createTimer () ),
iiu ( iiuIn ),
mutex ( mutexIn ),
diff --git a/modules/ca/src/client/tcpiiu.cpp b/modules/ca/src/client/tcpiiu.cpp
index 045f8d57e..9d174ab84 100644
--- a/modules/ca/src/client/tcpiiu.cpp
+++ b/modules/ca/src/client/tcpiiu.cpp
@@ -476,7 +476,7 @@ void tcpRecvThread::run ()
statusWireIO stat;
pComBuf->fillFromWire ( this->iiu, stat );
- epicsTime currentTime = epicsTime::getCurrent ();
+ epicsTime currentTime = epicsTime::getMonotonic ();
{
epicsGuard < epicsMutex > guard ( this->iiu.mutex );
@@ -1669,7 +1669,7 @@ bool tcpiiu::sendThreadFlush ( epicsGuard < epicsMutex > & guard )
if ( this->sendQue.occupiedBytes() > 0 ) {
while ( comBuf * pBuf = this->sendQue.popNextComBufToSend () ) {
- epicsTime current = epicsTime::getCurrent ();
+ epicsTime current = epicsTime::getMonotonic ();
unsigned bytesToBeSent = pBuf->occupiedBytes ();
bool success = false;
diff --git a/modules/ca/src/client/udpiiu.cpp b/modules/ca/src/client/udpiiu.cpp
index 1bcb03716..c378881f7 100644
--- a/modules/ca/src/client/udpiiu.cpp
+++ b/modules/ca/src/client/udpiiu.cpp
@@ -435,7 +435,7 @@ void udpRecvThread::run ()
}
else if ( status > 0 ) {
this->iiu.postMsg ( src, this->iiu.recvBuf,
- (arrayElementCount) status, epicsTime::getCurrent() );
+ (arrayElementCount) status, epicsTime::getMonotonic() );
}
} while ( ! this->iiu.shutdownCmd );
diff --git a/modules/database/src/ioc/db/dbPutNotifyBlocker.cpp b/modules/database/src/ioc/db/dbPutNotifyBlocker.cpp
index 1a796cdbd..2215f8468 100644
--- a/modules/database/src/ioc/db/dbPutNotifyBlocker.cpp
+++ b/modules/database/src/ioc/db/dbPutNotifyBlocker.cpp
@@ -144,12 +144,12 @@ void dbPutNotifyBlocker::initiatePutNotify (
break;
}
if ( beginTimeInit ) {
- if ( epicsTime::getCurrent () - begin > 30.0 ) {
+ if ( epicsTime::getMonotonic () - begin > 30.0 ) {
throw cacChannel::requestTimedOut ();
}
}
else {
- begin = epicsTime::getCurrent ();
+ begin = epicsTime::getMonotonic ();
beginTimeInit = true;
}
{
diff --git a/modules/libcom/src/fdmgr/fdManager.cpp b/modules/libcom/src/fdmgr/fdManager.cpp
index 00f4d4a87..f43fab25d 100644
--- a/modules/libcom/src/fdmgr/fdManager.cpp
+++ b/modules/libcom/src/fdmgr/fdManager.cpp
@@ -97,7 +97,7 @@ epicsShareFunc void fdManager::process (double delay)
// more than once here so that fd activity get serviced
// in a reasonable length of time.
//
- double minDelay = this->pTimerQueue->process(epicsTime::getCurrent());
+ double minDelay = this->pTimerQueue->process(epicsTime::getMonotonic());
if ( minDelay >= delay ) {
minDelay = delay;
@@ -121,7 +121,7 @@ epicsShareFunc void fdManager::process (double delay)
fd_set * pExceptSet = & this->fdSetsPtr[fdrException];
int status = select (this->maxFD, pReadSet, pWriteSet, pExceptSet, &tv);
- this->pTimerQueue->process(epicsTime::getCurrent());
+ this->pTimerQueue->process(epicsTime::getMonotonic());
if ( status > 0 ) {
@@ -204,7 +204,7 @@ epicsShareFunc void fdManager::process (double delay)
* of select()
*/
epicsThreadSleep(minDelay);
- this->pTimerQueue->process(epicsTime::getCurrent());
+ this->pTimerQueue->process(epicsTime::getMonotonic());
}
this->processInProg = false;
return;
diff --git a/modules/libcom/src/osi/epicsThread.cpp b/modules/libcom/src/osi/epicsThread.cpp
index 892d73de0..da7116476 100644
--- a/modules/libcom/src/osi/epicsThread.cpp
+++ b/modules/libcom/src/osi/epicsThread.cpp
@@ -143,7 +143,7 @@ bool epicsThread::exitWait ( const double delay ) throw ()
}
return true;
}
- epicsTime exitWaitBegin = epicsTime::getCurrent ();
+ epicsTime exitWaitBegin = epicsTime::getMonotonic ();
double exitWaitElapsed = 0.0;
epicsGuard < epicsMutex > guard ( this->mutex );
this->cancel = true;
@@ -151,7 +151,7 @@ bool epicsThread::exitWait ( const double delay ) throw ()
epicsGuardRelease < epicsMutex > unguard ( guard );
this->event.signal ();
this->exitEvent.wait ( delay - exitWaitElapsed );
- epicsTime current = epicsTime::getCurrent ();
+ epicsTime current = epicsTime::getMonotonic ();
exitWaitElapsed = current - exitWaitBegin;
}
}
diff --git a/modules/libcom/src/timer/epicsTimer.cpp b/modules/libcom/src/timer/epicsTimer.cpp
index e55280e7e..15bebef7f 100644
--- a/modules/libcom/src/timer/epicsTimer.cpp
+++ b/modules/libcom/src/timer/epicsTimer.cpp
@@ -173,7 +173,7 @@ extern "C" double epicsShareAPI
epicsTimerQueuePassiveProcess ( epicsTimerQueuePassiveId pQueue )
{
try {
- return pQueue->process ( epicsTime::getCurrent() );
+ return pQueue->process ( epicsTime::getMonotonic() );
}
catch ( ... ) {
return 1.0;
diff --git a/modules/libcom/src/timer/epicsTimer.h b/modules/libcom/src/timer/epicsTimer.h
index 72270f273..6e054ed4f 100644
--- a/modules/libcom/src/timer/epicsTimer.h
+++ b/modules/libcom/src/timer/epicsTimer.h
@@ -118,7 +118,7 @@ inline double epicsTimer::getExpireDelay ()
{
epicsTimer::expireInfo info = this->getExpireInfo ();
if ( info.active ) {
- double delay = info.expireTime - epicsTime::getCurrent ();
+ double delay = info.expireTime - epicsTime::getMonotonic ();
if ( delay < 0.0 ) {
delay = 0.0;
}
diff --git a/modules/libcom/src/timer/timer.cpp b/modules/libcom/src/timer/timer.cpp
index 35d6e47bf..6f0c7ea4a 100644
--- a/modules/libcom/src/timer/timer.cpp
+++ b/modules/libcom/src/timer/timer.cpp
@@ -54,7 +54,7 @@ void timer::destroy ()
void timer::start ( epicsTimerNotify & notify, double delaySeconds )
{
- this->start ( notify, epicsTime::getCurrent () + delaySeconds );
+ this->start ( notify, epicsTime::getMonotonic () + delaySeconds );
}
void timer::start ( epicsTimerNotify & notify, const epicsTime & expire )
@@ -129,7 +129,7 @@ void timer::privateStart ( epicsTimerNotify & notify, const epicsTime & expire )
debugPrintf ( ("Start of \"%s\" with delay %f at %p preempting %u\n",
typeid ( this->notify ).name (),
- expire - epicsTime::getCurrent (),
+ expire - epicsTime::getMonotonic (),
this, preemptCount ) );
}
@@ -190,7 +190,7 @@ void timer::show ( unsigned int level ) const
double delay;
if ( this->curState == statePending || this->curState == stateActive ) {
try {
- delay = this->exp - epicsTime::getCurrent();
+ delay = this->exp - epicsTime::getMonotonic();
}
catch ( ... ) {
delay = - DBL_MAX;
diff --git a/modules/libcom/src/timer/timerQueue.cpp b/modules/libcom/src/timer/timerQueue.cpp
index 5a798d4d4..d209bcbee 100644
--- a/modules/libcom/src/timer/timerQueue.cpp
+++ b/modules/libcom/src/timer/timerQueue.cpp
@@ -29,7 +29,7 @@ timerQueue::timerQueue ( epicsTimerQueueNotify & notifyIn ) :
pExpireTmr ( 0 ),
processThread ( 0 ),
exceptMsgTimeStamp (
- epicsTime :: getCurrent () - exceptMsgMinPeriod ),
+ epicsTime :: getMonotonic () - exceptMsgMinPeriod ),
cancelPending ( false )
{
}
@@ -48,7 +48,7 @@ void timerQueue ::
char date[64];
double delay;
try {
- epicsTime cur = epicsTime :: getCurrent ();
+ epicsTime cur = epicsTime :: getMonotonic ();
delay = cur - this->exceptMsgTimeStamp;
cur.strftime ( date, sizeof ( date ),
"%a %b %d %Y %H:%M:%S.%f" );
diff --git a/modules/libcom/src/timer/timerQueueActive.cpp b/modules/libcom/src/timer/timerQueueActive.cpp
index 5d8b72951..3611f1ad1 100644
--- a/modules/libcom/src/timer/timerQueueActive.cpp
+++ b/modules/libcom/src/timer/timerQueueActive.cpp
@@ -72,7 +72,7 @@ void timerQueueActive :: _printLastChanceExceptionMessage (
{
char date[64];
try {
- epicsTime cur = epicsTime :: getCurrent ();
+ epicsTime cur = epicsTime :: getMonotonic ();
cur.strftime ( date, sizeof ( date ), "%a %b %d %Y %H:%M:%S.%f");
}
catch ( ... ) {
@@ -90,7 +90,7 @@ void timerQueueActive :: run ()
this->exitFlag = false;
while ( ! this->terminateFlag ) {
try {
- double delay = this->queue.process ( epicsTime::getCurrent() );
+ double delay = this->queue.process ( epicsTime::getMonotonic() );
debugPrintf ( ( "timer thread sleeping for %g sec (max)\n", delay ) );
this->rescheduleEvent.wait ( delay );
}
diff --git a/modules/libcom/test/cvtFastPerform.cpp b/modules/libcom/test/cvtFastPerform.cpp
index c3f99dc61..28c831349 100644
--- a/modules/libcom/test/cvtFastPerform.cpp
+++ b/modules/libcom/test/cvtFastPerform.cpp
@@ -147,11 +147,11 @@ void Perf :: measure (double srcD, float srcF, int prec)
std::memset(buf, 0, sizeof(buf));
- epicsTime beg = epicsTime :: getCurrent ();
+ epicsTime beg = epicsTime :: getMonotonic ();
for ( unsigned i = 0; i < nIterations; i++ ) {
c->target (srcD, srcF, buf, sizeof(buf) - 1, prec);
}
- epicsTime end = epicsTime :: getCurrent ();
+ epicsTime end = epicsTime :: getMonotonic ();
double elapsed = end - beg;
elapsed /= nIterations * nUnrolled;
diff --git a/modules/libcom/test/epicsAtomicPerform.cpp b/modules/libcom/test/epicsAtomicPerform.cpp
index 2ef005efe..98eaf1778 100644
--- a/modules/libcom/test/epicsAtomicPerform.cpp
+++ b/modules/libcom/test/epicsAtomicPerform.cpp
@@ -394,12 +394,12 @@ static const unsigned N = 10000;
void recursiveOwnershipRetPerformance ()
{
RefCtr refCtr;
- epicsTime begin = epicsTime::getCurrent ();
+ epicsTime begin = epicsTime::getMonotonic ();
for ( size_t i = 0; i < N; i++ ) {
Ownership ownership ( refCtr );
recurRetOwner1000 ( ownership );
}
- double delay = epicsTime::getCurrent () - begin;
+ double delay = epicsTime::getMonotonic () - begin;
delay /= N * 1000u; // convert to delay per call
delay *= 1e6; // convert to micro seconds
testDiag ( "retOwnership() takes %f microseconds", delay );
@@ -408,13 +408,13 @@ void recursiveOwnershipRetPerformance ()
void ownershipPassRefPerformance ()
{
RefCtr refCtr;
- epicsTime begin = epicsTime::getCurrent ();
+ epicsTime begin = epicsTime::getMonotonic ();
for ( size_t i = 0; i < N; i++ ) {
Ownership ownershipSrc ( refCtr );
Ownership ownershipDest;
passRefOwnership1000 ( ownershipSrc, ownershipDest );
}
- double delay = epicsTime::getCurrent () - begin;
+ double delay = epicsTime::getMonotonic () - begin;
delay /= N * 1000u; // convert to delay per call
delay *= 1e6; // convert to micro seconds
testDiag ( "passRefOwnership() takes %f microseconds", delay );
@@ -456,7 +456,7 @@ void Ten < T > :: diagnostic ( double delay )
template < class T >
void measurePerformance ()
{
- epicsTime begin = epicsTime::getCurrent ();
+ epicsTime begin = epicsTime::getMonotonic ();
T target;
for ( size_t i = 0; i < N; i++ ) {
target.run ();
@@ -470,7 +470,7 @@ void measurePerformance ()
target.run ();
target.run ();
}
- double delay = epicsTime::getCurrent () - begin;
+ double delay = epicsTime::getMonotonic () - begin;
delay /= ( N * 10u ); // convert to delay per call
target.diagnostic ( delay );
}
diff --git a/modules/libcom/test/epicsEventTest.cpp b/modules/libcom/test/epicsEventTest.cpp
index 7c1a24141..b3e48f8f2 100644
--- a/modules/libcom/test/epicsEventTest.cpp
+++ b/modules/libcom/test/epicsEventTest.cpp
@@ -156,9 +156,9 @@ static void eventWakeupTest(void)
static double eventWaitMeasureDelayError( const epicsEventId &id, const double & delay )
{
- epicsTime beg = epicsTime::getCurrent();
+ epicsTime beg = epicsTime::getMonotonic();
epicsEventWaitWithTimeout ( id, delay );
- epicsTime end = epicsTime::getCurrent();
+ epicsTime end = epicsTime::getMonotonic();
double meas = end - beg;
double error = fabs ( delay - meas );
testDiag("epicsEventWaitWithTimeout(%.6f) delay error %.6f sec",
diff --git a/modules/libcom/test/epicsMutexTest.cpp b/modules/libcom/test/epicsMutexTest.cpp
index d44e5c0f1..e3ad5868f 100644
--- a/modules/libcom/test/epicsMutexTest.cpp
+++ b/modules/libcom/test/epicsMutexTest.cpp
@@ -168,32 +168,32 @@ void epicsMutexPerformance ()
unsigned i;
// test a single lock pair
- epicsTime begin = epicsTime::getCurrent ();
+ epicsTime begin = epicsTime::getMonotonic ();
static const unsigned N = 1000;
for ( i = 0; i < N; i++ ) {
tenLockPairsSquared ( mutex );
}
- double delay = epicsTime::getCurrent () - begin;
+ double delay = epicsTime::getMonotonic () - begin;
delay /= N * 100u; // convert to delay per lock pair
delay *= 1e6; // convert to micro seconds
testDiag("lock()*1/unlock()*1 takes %f microseconds", delay);
// test a two times recursive lock pair
- begin = epicsTime::getCurrent ();
+ begin = epicsTime::getMonotonic ();
for ( i = 0; i < N; i++ ) {
tenDoubleRecursiveLockPairsSquared ( mutex );
}
- delay = epicsTime::getCurrent () - begin;
+ delay = epicsTime::getMonotonic () - begin;
delay /= N * 100u; // convert to delay per lock pair
delay *= 1e6; // convert to micro seconds
testDiag("lock()*2/unlock()*2 takes %f microseconds", delay);
// test a four times recursive lock pair
- begin = epicsTime::getCurrent ();
+ begin = epicsTime::getMonotonic ();
for ( i = 0; i < N; i++ ) {
tenQuadRecursiveLockPairsSquared ( mutex );
}
- delay = epicsTime::getCurrent () - begin;
+ delay = epicsTime::getMonotonic () - begin;
delay /= N * 100u; // convert to delay per lock pair
delay *= 1e6; // convert to micro seconds
testDiag("lock()*4/unlock()*4 takes %f microseconds", delay);
diff --git a/modules/libcom/test/epicsThreadPerform.cpp b/modules/libcom/test/epicsThreadPerform.cpp
index c8fd34515..b73d90c9e 100644
--- a/modules/libcom/test/epicsThreadPerform.cpp
+++ b/modules/libcom/test/epicsThreadPerform.cpp
@@ -55,9 +55,9 @@ static void epicsThreadPriorityTest()
static double threadSleepMeasureDelayError ( const double & delay )
{
- epicsTime beg = epicsTime::getCurrent();
+ epicsTime beg = epicsTime::getMonotonic();
epicsThreadSleep ( delay );
- epicsTime end = epicsTime::getCurrent();
+ epicsTime end = epicsTime::getMonotonic();
double meas = end - beg;
double error = fabs ( delay - meas );
return error;
@@ -76,10 +76,10 @@ static double measureSleepQuantum (
double interval = rand ();
interval /= RAND_MAX;
interval *= testInterval;
- epicsTime start = epicsTime::getCurrent ();
+ epicsTime start = epicsTime::getMonotonic ();
epicsTime current = start;
while ( current - start < interval ) {
- current = epicsTime::getCurrent ();
+ current = epicsTime::getMonotonic ();
}
errorSum += threadSleepMeasureDelayError ( testInterval );
if ( i % ( iterations / 10 ) == 0 ) {
@@ -150,7 +150,7 @@ static void epicsThreadGetIdSelfPerfTest ()
{
static const unsigned N = 10000;
static const double microSecPerSec = 1e6;
- epicsTime begin = epicsTime::getCurrent ();
+ epicsTime begin = epicsTime::getMonotonic ();
for ( unsigned i = 0u; i < N; i++ ) {
epicsThreadGetIdSelf ();
epicsThreadGetIdSelf ();
@@ -164,7 +164,7 @@ static void epicsThreadGetIdSelfPerfTest ()
epicsThreadGetIdSelf ();
epicsThreadGetIdSelf ();
};
- epicsTime end = epicsTime::getCurrent ();
+ epicsTime end = epicsTime::getMonotonic ();
printf ( "It takes %f micro sec to call epicsThreadGetIdSelf ()\n",
microSecPerSec * ( end - begin ) / (10 * N) );
}
@@ -204,12 +204,12 @@ static void timeEpicsThreadPrivateGet ()
{
priv.set ( 0 );
- epicsTime begin = epicsTime::getCurrent ();
+ epicsTime begin = epicsTime::getMonotonic ();
static const unsigned N = 1000u;
for ( unsigned i = 0u; i < N; i++ ) {
callItTenTimesSquared ();
}
- double delay = epicsTime::getCurrent() - begin;
+ double delay = epicsTime::getMonotonic() - begin;
delay /= N * 100u; // convert to sec per call
delay *= 1e6; // convert to micro sec
printf("epicsThreadPrivateGet() takes %f microseconds\n", delay);
diff --git a/modules/libcom/test/epicsTimerTest.cpp b/modules/libcom/test/epicsTimerTest.cpp
index f2a510819..55ce82eeb 100644
--- a/modules/libcom/test/epicsTimerTest.cpp
+++ b/modules/libcom/test/epicsTimerTest.cpp
@@ -156,7 +156,7 @@ void testAccuracy ()
expireCount = nTimers;
for ( i = 0u; i < nTimers; i++ ) {
- epicsTime cur = epicsTime::getCurrent ();
+ epicsTime cur = epicsTime::getMonotonic ();
pTimers[i]->setBegin ( cur );
pTimers[i]->start ( cur + pTimers[i]->delay () );
}
@@ -253,7 +253,7 @@ void testCancel ()
testDiag ( "cancelCount = %u", cancelVerify::cancelCount );
testDiag ( "starting %d timers", nTimers );
- epicsTime exp = epicsTime::getCurrent () + 4.0;
+ epicsTime exp = epicsTime::getMonotonic () + 4.0;
for ( i = 0u; i < nTimers; i++ ) {
pTimers[i]->start ( exp );
}
@@ -339,7 +339,7 @@ void testExpireDestroy ()
testOk1 ( expireDestroyVerify::destroyCount == 0 );
testDiag ( "starting %d timers", nTimers );
- epicsTime cur = epicsTime::getCurrent ();
+ epicsTime cur = epicsTime::getMonotonic ();
for ( i = 0u; i < nTimers; i++ ) {
pTimers[i]->start ( cur );
}
@@ -432,7 +432,7 @@ void testPeriodic ()
testOk1 ( timerCount == nTimers );
testDiag ( "starting %d timers", nTimers );
- epicsTime cur = epicsTime::getCurrent ();
+ epicsTime cur = epicsTime::getMonotonic ();
for ( i = 0u; i < nTimers; i++ ) {
pTimers[i]->start ( cur );
}
From 49b323b93c4aba4dbdc5d49094eaea56db876b7a Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Mon, 5 Nov 2018 15:57:55 -0800
Subject: [PATCH 017/281] avoided embedded TOP for non sub-modules
---
.../configure => configure}/CONFIG_CA_MODULE | 0
.../configure => configure}/CONFIG_CA_VERSION | 0
.../CONFIG_DATABASE_MODULE | 0
.../CONFIG_DATABASE_VERSION | 0
.../CONFIG_LIBCOM_MODULE | 0
.../CONFIG_LIBCOM_VERSION | 0
configure/Makefile | 9 ++
modules/Makefile | 8 +-
modules/ca/.ci/travis-build.sh | 21 ---
modules/ca/.ci/travis-prepare.sh | 132 -----------------
modules/ca/.travis.yml | 26 ----
modules/ca/Makefile | 6 +-
modules/ca/configure/CONFIG | 28 ----
modules/ca/configure/CONFIG_SITE | 42 ------
modules/ca/configure/Makefile | 15 --
modules/ca/configure/RELEASE | 39 -----
modules/ca/configure/RULES | 6 -
modules/ca/configure/RULES_DIRS | 2 -
modules/ca/configure/RULES_TOP | 2 -
modules/ca/src/Makefile | 2 +-
modules/ca/src/client/Makefile | 6 +-
modules/ca/src/perl/Makefile | 2 +-
modules/ca/src/template/Makefile | 2 +-
modules/ca/src/tools/Makefile | 2 +-
modules/database/.ci/travis-build.sh | 21 ---
modules/database/.ci/travis-prepare.sh | 133 ------------------
modules/database/.travis.yml | 26 ----
modules/database/Makefile | 5 +-
modules/database/configure/CONFIG | 36 -----
modules/database/configure/CONFIG_SITE | 42 ------
modules/database/configure/Makefile | 15 --
modules/database/configure/RELEASE | 40 ------
modules/database/configure/RULES | 6 -
modules/database/configure/RULES.ioc | 2 -
modules/database/configure/RULES_DIRS | 2 -
modules/database/configure/RULES_TOP | 2 -
modules/database/src/Makefile | 2 +-
modules/database/src/ioc/Makefile | 8 +-
modules/database/src/std/Makefile | 8 +-
modules/database/src/template/Makefile | 2 +-
modules/database/src/tools/Makefile | 2 +-
modules/database/test/Makefile | 2 +-
modules/database/test/ioc/db/Makefile | 7 +-
modules/database/test/ioc/dbtemplate/Makefile | 2 +-
modules/database/test/std/filters/Makefile | 4 +-
modules/database/test/std/link/Makefile | 2 +
modules/database/test/std/rec/Makefile | 4 +-
modules/database/test/tools/Makefile | 2 +-
modules/libcom/.ci/travis-build.sh | 21 ---
modules/libcom/.ci/travis-prepare.sh | 123 ----------------
modules/libcom/.travis.yml | 26 ----
modules/libcom/Makefile | 7 +-
modules/libcom/RTEMS/Makefile | 2 +-
modules/libcom/configure/CONFIG | 44 ------
modules/libcom/configure/CONFIG_SITE | 42 ------
modules/libcom/configure/Makefile | 15 --
modules/libcom/configure/RELEASE | 38 -----
modules/libcom/configure/RULES | 6 -
modules/libcom/configure/RULES_DIRS | 2 -
modules/libcom/configure/RULES_TOP | 2 -
modules/libcom/src/Makefile | 6 +-
modules/libcom/test/Makefile | 2 +-
modules/libcom/vxWorks/Makefile | 2 +-
63 files changed, 60 insertions(+), 1003 deletions(-)
rename {modules/ca/configure => configure}/CONFIG_CA_MODULE (100%)
rename {modules/ca/configure => configure}/CONFIG_CA_VERSION (100%)
rename {modules/database/configure => configure}/CONFIG_DATABASE_MODULE (100%)
rename {modules/database/configure => configure}/CONFIG_DATABASE_VERSION (100%)
rename {modules/libcom/configure => configure}/CONFIG_LIBCOM_MODULE (100%)
rename {modules/libcom/configure => configure}/CONFIG_LIBCOM_VERSION (100%)
delete mode 100755 modules/ca/.ci/travis-build.sh
delete mode 100755 modules/ca/.ci/travis-prepare.sh
delete mode 100644 modules/ca/.travis.yml
delete mode 100644 modules/ca/configure/CONFIG
delete mode 100644 modules/ca/configure/CONFIG_SITE
delete mode 100644 modules/ca/configure/Makefile
delete mode 100644 modules/ca/configure/RELEASE
delete mode 100644 modules/ca/configure/RULES
delete mode 100644 modules/ca/configure/RULES_DIRS
delete mode 100644 modules/ca/configure/RULES_TOP
delete mode 100755 modules/database/.ci/travis-build.sh
delete mode 100755 modules/database/.ci/travis-prepare.sh
delete mode 100644 modules/database/.travis.yml
delete mode 100644 modules/database/configure/CONFIG
delete mode 100644 modules/database/configure/CONFIG_SITE
delete mode 100644 modules/database/configure/Makefile
delete mode 100644 modules/database/configure/RELEASE
delete mode 100644 modules/database/configure/RULES
delete mode 100644 modules/database/configure/RULES.ioc
delete mode 100644 modules/database/configure/RULES_DIRS
delete mode 100644 modules/database/configure/RULES_TOP
delete mode 100755 modules/libcom/.ci/travis-build.sh
delete mode 100755 modules/libcom/.ci/travis-prepare.sh
delete mode 100644 modules/libcom/.travis.yml
delete mode 100644 modules/libcom/configure/CONFIG
delete mode 100644 modules/libcom/configure/CONFIG_SITE
delete mode 100644 modules/libcom/configure/Makefile
delete mode 100644 modules/libcom/configure/RELEASE
delete mode 100644 modules/libcom/configure/RULES
delete mode 100644 modules/libcom/configure/RULES_DIRS
delete mode 100644 modules/libcom/configure/RULES_TOP
diff --git a/modules/ca/configure/CONFIG_CA_MODULE b/configure/CONFIG_CA_MODULE
similarity index 100%
rename from modules/ca/configure/CONFIG_CA_MODULE
rename to configure/CONFIG_CA_MODULE
diff --git a/modules/ca/configure/CONFIG_CA_VERSION b/configure/CONFIG_CA_VERSION
similarity index 100%
rename from modules/ca/configure/CONFIG_CA_VERSION
rename to configure/CONFIG_CA_VERSION
diff --git a/modules/database/configure/CONFIG_DATABASE_MODULE b/configure/CONFIG_DATABASE_MODULE
similarity index 100%
rename from modules/database/configure/CONFIG_DATABASE_MODULE
rename to configure/CONFIG_DATABASE_MODULE
diff --git a/modules/database/configure/CONFIG_DATABASE_VERSION b/configure/CONFIG_DATABASE_VERSION
similarity index 100%
rename from modules/database/configure/CONFIG_DATABASE_VERSION
rename to configure/CONFIG_DATABASE_VERSION
diff --git a/modules/libcom/configure/CONFIG_LIBCOM_MODULE b/configure/CONFIG_LIBCOM_MODULE
similarity index 100%
rename from modules/libcom/configure/CONFIG_LIBCOM_MODULE
rename to configure/CONFIG_LIBCOM_MODULE
diff --git a/modules/libcom/configure/CONFIG_LIBCOM_VERSION b/configure/CONFIG_LIBCOM_VERSION
similarity index 100%
rename from modules/libcom/configure/CONFIG_LIBCOM_VERSION
rename to configure/CONFIG_LIBCOM_VERSION
diff --git a/configure/Makefile b/configure/Makefile
index 377879766..f48278dd3 100644
--- a/configure/Makefile
+++ b/configure/Makefile
@@ -20,5 +20,14 @@ CONFIGS += $(subst ../,,$(wildcard ../os/CONFIG*))
CONFIGS += $(subst ../,,$(wildcard ../RELEASE*))
CONFIGS += $(subst ../,,$(wildcard ../RULES*))
+CFG += CONFIG_LIBCOM_MODULE
+CFG += CONFIG_LIBCOM_VERSION
+
+CFG += CONFIG_CA_MODULE
+CFG += CONFIG_CA_VERSION
+
+CFG += CONFIG_DATABASE_MODULE
+CFG += CONFIG_DATABASE_VERSION
+
include $(TOP)/configure/RULES
diff --git a/modules/Makefile b/modules/Makefile
index 214e914f1..e1ce468c4 100644
--- a/modules/Makefile
+++ b/modules/Makefile
@@ -6,15 +6,15 @@
TOP = ..
include $(TOP)/configure/CONFIG
-# Submodules for bundle build
-SUBMODULES += libcom
+DIRS += libcom
-SUBMODULES += ca
+DIRS += ca
ca_DEPEND_DIRS = libcom
-SUBMODULES += database
+DIRS += database
database_DEPEND_DIRS = ca
+# Submodules for bundle build
SUBMODULES += pvData
pvData_DEPEND_DIRS = libcom
diff --git a/modules/ca/.ci/travis-build.sh b/modules/ca/.ci/travis-build.sh
deleted file mode 100755
index 622979b9d..000000000
--- a/modules/ca/.ci/travis-build.sh
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/bin/sh
-set -e -x
-
-# set RTEMS to eg. "4.9" or "4.10"
-# requires qemu, bison, flex, texinfo, install-info
-if [ -n "$RTEMS" ]
-then
- # find local qemu-system-i386
- export PATH="$HOME/.cache/qemu/usr/bin:$PATH"
- echo -n "Using QEMU: "
- type qemu-system-i386 || echo "Missing qemu"
- EXTRA=RTEMS_QEMU_FIXUPS=YES
-fi
-
-make -j2 $EXTRA
-
-if [ "$TEST" != "NO" ]
-then
- make -j2 tapfiles
- make -s test-results
-fi
diff --git a/modules/ca/.ci/travis-prepare.sh b/modules/ca/.ci/travis-prepare.sh
deleted file mode 100755
index 85dc8d7ce..000000000
--- a/modules/ca/.ci/travis-prepare.sh
+++ /dev/null
@@ -1,132 +0,0 @@
-#!/bin/sh
-set -e -x
-
-CURDIR="$PWD"
-
-QDIR="$HOME/.cache/qemu"
-
-if [ -n "$RTEMS" -a "$TEST" = "YES" ]
-then
- git clone --quiet --branch vme --depth 10 https://github.com/mdavidsaver/qemu.git "$HOME/.build/qemu"
- cd "$HOME/.build/qemu"
-
- HEAD=`git log -n1 --pretty=format:%H`
- echo "HEAD revision $HEAD"
-
- [ -e "$HOME/.cache/qemu/built" ] && BUILT=`cat "$HOME/.cache/qemu/built"`
- echo "Cached revision $BUILT"
-
- if [ "$HEAD" != "$BUILT" ]
- then
- echo "Building QEMU"
- git submodule --quiet update --init
-
- install -d "$HOME/.build/qemu/build"
- cd "$HOME/.build/qemu/build"
-
- "$HOME/.build/qemu/configure" --prefix="$HOME/.cache/qemu/usr" --target-list=i386-softmmu --disable-werror
- make -j2
- make install
-
- echo "$HEAD" > "$HOME/.cache/qemu/built"
- fi
-fi
-
-cd "$CURDIR"
-
-cat << EOF > configure/RELEASE.local
-EPICS_BASE=$HOME/.source/epics-base
-EOF
-
-install -d "$HOME/.source"
-cd "$HOME/.source"
-
-add_base_module() {
- MODULE=$1
- BRANCH=$2
- ( cd epics-base/modules && \
- git clone --quiet --depth 5 --branch "$MODULE"/"$BRANCH" https://github.com/${REPOBASE:-epics-base}/epics-base.git "$MODULE" && \
- cd "$MODULE" && git log -n1 )
-}
-
-git clone --quiet --depth 5 --branch core/"${BRCORE:-master}" https://github.com/${REPOBASE:-epics-base}/epics-base.git epics-base
-( cd epics-base && git log -n1 )
-add_base_module libcom "${BRLIBCOM:-master}"
-
-EPICS_HOST_ARCH=`sh epics-base/startup/EpicsHostArch`
-
-# requires wine and g++-mingw-w64-i686
-if [ "$WINE" = "32" ]
-then
- echo "Cross mingw32"
- sed -i -e '/CMPLR_PREFIX/d' epics-base/configure/os/CONFIG_SITE.linux-x86.win32-x86-mingw
- cat << EOF >> epics-base/configure/os/CONFIG_SITE.linux-x86.win32-x86-mingw
-CMPLR_PREFIX=i686-w64-mingw32-
-EOF
- cat << EOF >> epics-base/configure/CONFIG_SITE
-CROSS_COMPILER_TARGET_ARCHS+=win32-x86-mingw
-EOF
-fi
-
-if [ "$STATIC" = "YES" ]
-then
- echo "Build static libraries/executables"
- cat << EOF >> epics-base/configure/CONFIG_SITE
-SHARED_LIBRARIES=NO
-STATIC_BUILD=YES
-EOF
-fi
-
-case "$CMPLR" in
-clang)
- echo "Host compiler is clang"
- cat << EOF >> epics-base/configure/os/CONFIG_SITE.Common.$EPICS_HOST_ARCH
-GNU = NO
-CMPLR_CLASS = clang
-CC = clang
-CCC = clang++
-EOF
-
- # hack
- sed -i -e 's/CMPLR_CLASS = gcc/CMPLR_CLASS = clang/' epics-base/configure/CONFIG.gnuCommon
-
- clang --version
- ;;
-*)
- echo "Host compiler is default"
- gcc --version
- ;;
-esac
-
-cat <> epics-base/configure/CONFIG_SITE
-USR_CPPFLAGS += $USR_CPPFLAGS
-USR_CFLAGS += $USR_CFLAGS
-USR_CXXFLAGS += $USR_CXXFLAGS
-EOF
-
-# set RTEMS to eg. "4.9" or "4.10"
-# requires qemu, bison, flex, texinfo, install-info
-if [ -n "$RTEMS" ]
-then
- echo "Cross RTEMS${RTEMS} for pc386"
- install -d /home/travis/.cache
- curl -L "https://github.com/mdavidsaver/rsb/releases/download/travis-20160306-2/rtems${RTEMS}-i386-trusty-20190306-2.tar.gz" \
- | tar -C /home/travis/.cache -xj
-
- sed -i -e '/^RTEMS_VERSION/d' -e '/^RTEMS_BASE/d' epics-base/configure/os/CONFIG_SITE.Common.RTEMS
- cat << EOF >> epics-base/configure/os/CONFIG_SITE.Common.RTEMS
-RTEMS_VERSION=$RTEMS
-RTEMS_BASE=/home/travis/.cache/rtems${RTEMS}-i386
-EOF
- cat << EOF >> epics-base/configure/CONFIG_SITE
-CROSS_COMPILER_TARGET_ARCHS+=RTEMS-pc386
-EOF
-
- # find local qemu-system-i386
- export PATH="$HOME/.cache/qemu/usr/bin:$PATH"
- echo -n "Using QEMU: "
- type qemu-system-i386 || echo "Missing qemu"
- EXTRA=RTEMS_QEMU_FIXUPS=YES
-fi
-
-make -j2 -C epics-base $EXTRA
diff --git a/modules/ca/.travis.yml b/modules/ca/.travis.yml
deleted file mode 100644
index 5a99b886a..000000000
--- a/modules/ca/.travis.yml
+++ /dev/null
@@ -1,26 +0,0 @@
-sudo: false
-dist: trusty
-language: c
-compiler:
- - gcc
-addons:
- apt:
- packages:
- - libreadline6-dev
- - libncurses5-dev
- - perl
- - clang
- - g++-mingw-w64-i686
-install:
- - ./.ci/travis-prepare.sh
-script:
- - ./.ci/travis-build.sh
-env:
- - BRCORE=master BRLIBCOM=master TEST=NO
- - CMPLR=clang TEST=NO
- - USR_CXXFLAGS=-std=c++11 TEST=NO
- - CMPLR=clang USR_CXXFLAGS=-std=c++11 TEST=NO
- - WINE=32 TEST=NO STATIC=YES
- - WINE=32 TEST=NO STATIC=NO
- - RTEMS=4.10 TEST=NO
- - RTEMS=4.9 TEST=NO
diff --git a/modules/ca/Makefile b/modules/ca/Makefile
index 13feff6c2..eeb147b07 100644
--- a/modules/ca/Makefile
+++ b/modules/ca/Makefile
@@ -7,11 +7,9 @@
# in file LICENSE that is included with this distribution.
#*************************************************************************
-TOP = .
+TOP = ../..
include $(TOP)/configure/CONFIG
-DIRS += configure src
-
-src_DEPEND_DIRS = configure
+DIRS += src
include $(TOP)/configure/RULES_TOP
diff --git a/modules/ca/configure/CONFIG b/modules/ca/configure/CONFIG
deleted file mode 100644
index 331fd7024..000000000
--- a/modules/ca/configure/CONFIG
+++ /dev/null
@@ -1,28 +0,0 @@
-# CONFIG - Load build configuration data
-#
-# Do not make changes to this file!
-
-# Allow user to override where the build rules come from
-RULES = $(EPICS_BASE)
-
-# RELEASE files point to other application tops
-include $(TOP)/configure/RELEASE
--include $(TOP)/configure/RELEASE.$(EPICS_HOST_ARCH).Common
-ifdef T_A
--include $(TOP)/configure/RELEASE.Common.$(T_A)
--include $(TOP)/configure/RELEASE.$(EPICS_HOST_ARCH).$(T_A)
-endif
-
-CONFIG = $(RULES)/configure
-include $(CONFIG)/CONFIG
-
-# Override the Base definition:
-INSTALL_LOCATION = $(TOP)
-
-# CONFIG_SITE files contain other build configuration settings
-include $(TOP)/configure/CONFIG_SITE
--include $(TOP)/configure/CONFIG_SITE.$(EPICS_HOST_ARCH).Common
-ifdef T_A
- -include $(TOP)/configure/CONFIG_SITE.Common.$(T_A)
- -include $(TOP)/configure/CONFIG_SITE.$(EPICS_HOST_ARCH).$(T_A)
-endif
diff --git a/modules/ca/configure/CONFIG_SITE b/modules/ca/configure/CONFIG_SITE
deleted file mode 100644
index d78c7f514..000000000
--- a/modules/ca/configure/CONFIG_SITE
+++ /dev/null
@@ -1,42 +0,0 @@
-# CONFIG_SITE
-
-# Make any application-specific changes to the EPICS build
-# configuration variables in this file.
-#
-# Host/target specific settings can be specified in files named
-# CONFIG_SITE.$(EPICS_HOST_ARCH).Common
-# CONFIG_SITE.Common.$(T_A)
-# CONFIG_SITE.$(EPICS_HOST_ARCH).$(T_A)
-
-# CHECK_RELEASE controls the consistency checking of the support
-# applications pointed to by the RELEASE* files.
-# Normally CHECK_RELEASE should be set to YES.
-# Set CHECK_RELEASE to NO to disable checking completely.
-# Set CHECK_RELEASE to WARN to perform consistency checking but
-# continue building even if conflicts are found.
-CHECK_RELEASE = YES
-
-# Set this when you only want to compile this application
-# for a subset of the cross-compiled target architectures
-# that Base is built for.
-#CROSS_COMPILER_TARGET_ARCHS = vxWorks-ppc32
-
-# To install files into a location other than $(TOP) define
-# INSTALL_LOCATION here.
-#INSTALL_LOCATION=
-
-# Set this when the IOC and build host use different paths
-# to the install location. This may be needed to boot from
-# a Microsoft FTP server say, or on some NFS configurations.
-#IOCS_APPL_TOP =
-
-# For application debugging purposes, override the HOST_OPT and/
-# or CROSS_OPT settings from base/configure/CONFIG_SITE
-#HOST_OPT = NO
-#CROSS_OPT = NO
-
-# These allow developers to override the CONFIG_SITE variable
-# settings without having to modify the configure/CONFIG_SITE
-# file itself.
--include $(TOP)/../CONFIG_SITE.local
--include $(TOP)/configure/CONFIG_SITE.local
diff --git a/modules/ca/configure/Makefile b/modules/ca/configure/Makefile
deleted file mode 100644
index e2f373893..000000000
--- a/modules/ca/configure/Makefile
+++ /dev/null
@@ -1,15 +0,0 @@
-#*************************************************************************
-# EPICS BASE is distributed subject to a Software License Agreement found
-# in file LICENSE that is included with this distribution.
-#*************************************************************************
-TOP = ..
-
-include $(TOP)/configure/CONFIG
-
-TARGETS = $(CONFIG_TARGETS)
-CONFIGS += $(subst ../,,$(wildcard $(CONFIG_INSTALLS)))
-
-CFG += CONFIG_CA_MODULE
-CFG += CONFIG_CA_VERSION
-
-include $(TOP)/configure/RULES
diff --git a/modules/ca/configure/RELEASE b/modules/ca/configure/RELEASE
deleted file mode 100644
index a2d9f554a..000000000
--- a/modules/ca/configure/RELEASE
+++ /dev/null
@@ -1,39 +0,0 @@
-# RELEASE - Location of external support modules
-#
-# IF YOU CHANGE ANY PATHS in this file or make API changes to
-# any modules it refers to, you should do a "make rebuild" in
-# this application's top level directory.
-#
-# The EPICS build process does not check dependencies against
-# any files from outside the application, so it is safest to
-# rebuild it completely if any modules it depends on change.
-#
-# Host- or target-specific settings can be given in files named
-# RELEASE.$(EPICS_HOST_ARCH).Common
-# RELEASE.Common.$(T_A)
-# RELEASE.$(EPICS_HOST_ARCH).$(T_A)
-#
-# This file is parsed by both GNUmake and an EPICS Perl script,
-# so it may ONLY contain definititions of paths to other support
-# modules, variable definitions that are used in module paths,
-# and include statements that pull in other RELEASE files.
-# Variables may be used before their values have been set.
-# Build variables that are NOT used in paths should be set in
-# the CONFIG_SITE file.
-
-# Variables and paths to dependent modules:
-#MODULES = /path/to/modules
-#MYMODULE = $(MODULES)/my-module
-
-# If building the EPICS modules individually, set these:
-#EPICS_LIBCOM = $(MODULES)/libcom-3.17.0
-#EPICS_BASE = $(MODULES)/core-7.0.1
-
-# Set RULES here if you want to use build rules from elsewhere:
-#RULES = $(MODULES)/build-rules
-
-# These lines allow developers to override these RELEASE settings
-# without having to modify this file directly.
--include $(TOP)/../RELEASE.local
--include $(TOP)/../RELEASE.$(EPICS_HOST_ARCH).local
--include $(TOP)/configure/RELEASE.local
diff --git a/modules/ca/configure/RULES b/modules/ca/configure/RULES
deleted file mode 100644
index 6d56e14e8..000000000
--- a/modules/ca/configure/RULES
+++ /dev/null
@@ -1,6 +0,0 @@
-# RULES
-
-include $(CONFIG)/RULES
-
-# Library should be rebuilt because LIBOBJS may have changed.
-$(LIBNAME): ../Makefile
diff --git a/modules/ca/configure/RULES_DIRS b/modules/ca/configure/RULES_DIRS
deleted file mode 100644
index 3ba269dcc..000000000
--- a/modules/ca/configure/RULES_DIRS
+++ /dev/null
@@ -1,2 +0,0 @@
-#RULES_DIRS
-include $(CONFIG)/RULES_DIRS
diff --git a/modules/ca/configure/RULES_TOP b/modules/ca/configure/RULES_TOP
deleted file mode 100644
index 2b8cbc6da..000000000
--- a/modules/ca/configure/RULES_TOP
+++ /dev/null
@@ -1,2 +0,0 @@
-#RULES_TOP
-include $(CONFIG)/RULES_TOP
diff --git a/modules/ca/src/Makefile b/modules/ca/src/Makefile
index 650b3550f..335cec67e 100644
--- a/modules/ca/src/Makefile
+++ b/modules/ca/src/Makefile
@@ -7,7 +7,7 @@
# in the file LICENSE that is included with this distribution.
#*************************************************************************
-TOP = ..
+TOP = ../../..
include $(TOP)/configure/CONFIG
# Channel Access Client
diff --git a/modules/ca/src/client/Makefile b/modules/ca/src/client/Makefile
index c5fc5c49d..cae6ee065 100644
--- a/modules/ca/src/client/Makefile
+++ b/modules/ca/src/client/Makefile
@@ -6,8 +6,8 @@
# EPICS BASE is distributed subject to a Software License Agreement found
# in file LICENSE that is included with this distribution.
#*************************************************************************
-
-TOP = ../..
+CURDIR := $(or $(dir $(lastword $(MAKEFILE_LIST))), .)
+TOP = ../../../..
include $(TOP)/configure/CONFIG
@@ -109,7 +109,7 @@ EXPAND += S99caRepeater@
EXPAND += caRepeater.service@
EXPAND_VARS = INSTALL_BIN=$(abspath $(INSTALL_BIN))
-SRC_DIRS += $(TOP)/src/client/test
+SRC_DIRS += $(CURDIR)/test
PROD_HOST += ca_test
ca_test_SRCS = ca_test_main.c ca_test.c
ca_test_LIBS = ca Com
diff --git a/modules/ca/src/perl/Makefile b/modules/ca/src/perl/Makefile
index 5b2bd5920..65f42ee3a 100644
--- a/modules/ca/src/perl/Makefile
+++ b/modules/ca/src/perl/Makefile
@@ -5,7 +5,7 @@
# in file LICENSE that is included with this distribution.
#*************************************************************************
-TOP = ../..
+TOP = ../../../..
include $(TOP)/configure/CONFIG
# Special settings for Darwin:
diff --git a/modules/ca/src/template/Makefile b/modules/ca/src/template/Makefile
index 913d92bf4..8683dfd3c 100644
--- a/modules/ca/src/template/Makefile
+++ b/modules/ca/src/template/Makefile
@@ -1,4 +1,4 @@
-TOP=../..
+TOP=../../../..
include $(TOP)/configure/CONFIG
diff --git a/modules/ca/src/tools/Makefile b/modules/ca/src/tools/Makefile
index 7ebcc021b..c56e21eca 100644
--- a/modules/ca/src/tools/Makefile
+++ b/modules/ca/src/tools/Makefile
@@ -9,7 +9,7 @@
# in file LICENSE that is included with this distribution.
#*************************************************************************
-TOP = ../..
+TOP = ../../../..
include $(TOP)/configure/CONFIG
diff --git a/modules/database/.ci/travis-build.sh b/modules/database/.ci/travis-build.sh
deleted file mode 100755
index 622979b9d..000000000
--- a/modules/database/.ci/travis-build.sh
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/bin/sh
-set -e -x
-
-# set RTEMS to eg. "4.9" or "4.10"
-# requires qemu, bison, flex, texinfo, install-info
-if [ -n "$RTEMS" ]
-then
- # find local qemu-system-i386
- export PATH="$HOME/.cache/qemu/usr/bin:$PATH"
- echo -n "Using QEMU: "
- type qemu-system-i386 || echo "Missing qemu"
- EXTRA=RTEMS_QEMU_FIXUPS=YES
-fi
-
-make -j2 $EXTRA
-
-if [ "$TEST" != "NO" ]
-then
- make -j2 tapfiles
- make -s test-results
-fi
diff --git a/modules/database/.ci/travis-prepare.sh b/modules/database/.ci/travis-prepare.sh
deleted file mode 100755
index ce2c18b5d..000000000
--- a/modules/database/.ci/travis-prepare.sh
+++ /dev/null
@@ -1,133 +0,0 @@
-#!/bin/sh
-set -e -x
-
-CURDIR="$PWD"
-
-QDIR="$HOME/.cache/qemu"
-
-if [ -n "$RTEMS" -a "$TEST" = "YES" ]
-then
- git clone --quiet --branch vme --depth 10 https://github.com/mdavidsaver/qemu.git "$HOME/.build/qemu"
- cd "$HOME/.build/qemu"
-
- HEAD=`git log -n1 --pretty=format:%H`
- echo "HEAD revision $HEAD"
-
- [ -e "$HOME/.cache/qemu/built" ] && BUILT=`cat "$HOME/.cache/qemu/built"`
- echo "Cached revision $BUILT"
-
- if [ "$HEAD" != "$BUILT" ]
- then
- echo "Building QEMU"
- git submodule --quiet update --init
-
- install -d "$HOME/.build/qemu/build"
- cd "$HOME/.build/qemu/build"
-
- "$HOME/.build/qemu/configure" --prefix="$HOME/.cache/qemu/usr" --target-list=i386-softmmu --disable-werror
- make -j2
- make install
-
- echo "$HEAD" > "$HOME/.cache/qemu/built"
- fi
-fi
-
-cd "$CURDIR"
-
-cat << EOF > configure/RELEASE.local
-EPICS_BASE=$HOME/.source/epics-base
-EOF
-
-install -d "$HOME/.source"
-cd "$HOME/.source"
-
-add_base_module() {
- MODULE=$1
- BRANCH=$2
- ( cd epics-base/modules && \
- git clone --quiet --depth 5 --branch "$MODULE"/"$BRANCH" https://github.com/${REPOBASE:-epics-base}/epics-base.git "$MODULE" && \
- cd "$MODULE" && git log -n1 )
-}
-
-git clone --quiet --depth 5 --branch core/"${BRCORE:-master}" https://github.com/${REPOBASE:-epics-base}/epics-base.git epics-base
-( cd epics-base && git log -n1 )
-add_base_module libcom "${BRLIBCOM:-master}"
-add_base_module ca "${BRCA:-master}"
-
-EPICS_HOST_ARCH=`sh epics-base/startup/EpicsHostArch`
-
-# requires wine and g++-mingw-w64-i686
-if [ "$WINE" = "32" ]
-then
- echo "Cross mingw32"
- sed -i -e '/CMPLR_PREFIX/d' epics-base/configure/os/CONFIG_SITE.linux-x86.win32-x86-mingw
- cat << EOF >> epics-base/configure/os/CONFIG_SITE.linux-x86.win32-x86-mingw
-CMPLR_PREFIX=i686-w64-mingw32-
-EOF
- cat << EOF >> epics-base/configure/CONFIG_SITE
-CROSS_COMPILER_TARGET_ARCHS+=win32-x86-mingw
-EOF
-fi
-
-if [ "$STATIC" = "YES" ]
-then
- echo "Build static libraries/executables"
- cat << EOF >> epics-base/configure/CONFIG_SITE
-SHARED_LIBRARIES=NO
-STATIC_BUILD=YES
-EOF
-fi
-
-case "$CMPLR" in
-clang)
- echo "Host compiler is clang"
- cat << EOF >> epics-base/configure/os/CONFIG_SITE.Common.$EPICS_HOST_ARCH
-GNU = NO
-CMPLR_CLASS = clang
-CC = clang
-CCC = clang++
-EOF
-
- # hack
- sed -i -e 's/CMPLR_CLASS = gcc/CMPLR_CLASS = clang/' epics-base/configure/CONFIG.gnuCommon
-
- clang --version
- ;;
-*)
- echo "Host compiler is default"
- gcc --version
- ;;
-esac
-
-cat <> epics-base/configure/CONFIG_SITE
-USR_CPPFLAGS += $USR_CPPFLAGS
-USR_CFLAGS += $USR_CFLAGS
-USR_CXXFLAGS += $USR_CXXFLAGS
-EOF
-
-# set RTEMS to eg. "4.9" or "4.10"
-# requires qemu, bison, flex, texinfo, install-info
-if [ -n "$RTEMS" ]
-then
- echo "Cross RTEMS${RTEMS} for pc386"
- install -d /home/travis/.cache
- curl -L "https://github.com/mdavidsaver/rsb/releases/download/travis-20160306-2/rtems${RTEMS}-i386-trusty-20190306-2.tar.gz" \
- | tar -C /home/travis/.cache -xj
-
- sed -i -e '/^RTEMS_VERSION/d' -e '/^RTEMS_BASE/d' epics-base/configure/os/CONFIG_SITE.Common.RTEMS
- cat << EOF >> epics-base/configure/os/CONFIG_SITE.Common.RTEMS
-RTEMS_VERSION=$RTEMS
-RTEMS_BASE=/home/travis/.cache/rtems${RTEMS}-i386
-EOF
- cat << EOF >> epics-base/configure/CONFIG_SITE
-CROSS_COMPILER_TARGET_ARCHS+=RTEMS-pc386
-EOF
-
- # find local qemu-system-i386
- export PATH="$HOME/.cache/qemu/usr/bin:$PATH"
- echo -n "Using QEMU: "
- type qemu-system-i386 || echo "Missing qemu"
- EXTRA=RTEMS_QEMU_FIXUPS=YES
-fi
-
-make -j2 -C epics-base $EXTRA
diff --git a/modules/database/.travis.yml b/modules/database/.travis.yml
deleted file mode 100644
index 127ae7bc9..000000000
--- a/modules/database/.travis.yml
+++ /dev/null
@@ -1,26 +0,0 @@
-sudo: false
-dist: trusty
-language: c
-compiler:
- - gcc
-addons:
- apt:
- packages:
- - libreadline6-dev
- - libncurses5-dev
- - perl
- - clang
- - g++-mingw-w64-i686
-install:
- - ./.ci/travis-prepare.sh
-script:
- - ./.ci/travis-build.sh
-env:
- - BRCORE=master BRLIBCOM=master BRCA=master
- - CMPLR=clang
- - USR_CXXFLAGS=-std=c++11
- - CMPLR=clang USR_CXXFLAGS=-std=c++11
- - WINE=32 TEST=NO STATIC=YES
- - WINE=32 TEST=NO STATIC=NO
- - RTEMS=4.10 TEST=NO
- - RTEMS=4.9 TEST=NO
diff --git a/modules/database/Makefile b/modules/database/Makefile
index 9998e6cf5..9bb746e82 100644
--- a/modules/database/Makefile
+++ b/modules/database/Makefile
@@ -7,11 +7,10 @@
# in file LICENSE that is included with this distribution.
#*************************************************************************
-TOP = .
+TOP = ../..
include $(TOP)/configure/CONFIG
-DIRS += configure src
-src_DEPEND_DIRS = configure
+DIRS += src
DIRS += test
test_DEPEND_DIRS = src
diff --git a/modules/database/configure/CONFIG b/modules/database/configure/CONFIG
deleted file mode 100644
index c471407c4..000000000
--- a/modules/database/configure/CONFIG
+++ /dev/null
@@ -1,36 +0,0 @@
-# CONFIG - Load build configuration data
-#
-# Do not make changes to this file!
-
-# Allow user to override where the build rules come from
-RULES = $(EPICS_BASE)
-
-# RELEASE files point to other application tops
-include $(TOP)/configure/RELEASE
--include $(TOP)/configure/RELEASE.$(EPICS_HOST_ARCH).Common
-ifdef T_A
--include $(TOP)/configure/RELEASE.Common.$(T_A)
--include $(TOP)/configure/RELEASE.$(EPICS_HOST_ARCH).$(T_A)
-endif
-
-BUILDING_DATABASE = DEFINED
-
-CONFIG = $(RULES)/configure
-include $(CONFIG)/CONFIG
-
-# Override the Base definition:
-INSTALL_LOCATION = $(TOP)
-
-# Use new RSET definition
-BASE_CPPFLAGS += -DUSE_TYPED_RSET
-
-# Shared library ABI version.
-SHRLIB_VERSION = $(EPICS_DATABASE_MAJOR_VERSION).$(EPICS_DATABASE_MINOR_VERSION).$(EPICS_DATABASE_MAINTENANCE_VERSION)
-
-# CONFIG_SITE files contain other build configuration settings
-include $(TOP)/configure/CONFIG_SITE
--include $(TOP)/configure/CONFIG_SITE.$(EPICS_HOST_ARCH).Common
-ifdef T_A
- -include $(TOP)/configure/CONFIG_SITE.Common.$(T_A)
- -include $(TOP)/configure/CONFIG_SITE.$(EPICS_HOST_ARCH).$(T_A)
-endif
diff --git a/modules/database/configure/CONFIG_SITE b/modules/database/configure/CONFIG_SITE
deleted file mode 100644
index d78c7f514..000000000
--- a/modules/database/configure/CONFIG_SITE
+++ /dev/null
@@ -1,42 +0,0 @@
-# CONFIG_SITE
-
-# Make any application-specific changes to the EPICS build
-# configuration variables in this file.
-#
-# Host/target specific settings can be specified in files named
-# CONFIG_SITE.$(EPICS_HOST_ARCH).Common
-# CONFIG_SITE.Common.$(T_A)
-# CONFIG_SITE.$(EPICS_HOST_ARCH).$(T_A)
-
-# CHECK_RELEASE controls the consistency checking of the support
-# applications pointed to by the RELEASE* files.
-# Normally CHECK_RELEASE should be set to YES.
-# Set CHECK_RELEASE to NO to disable checking completely.
-# Set CHECK_RELEASE to WARN to perform consistency checking but
-# continue building even if conflicts are found.
-CHECK_RELEASE = YES
-
-# Set this when you only want to compile this application
-# for a subset of the cross-compiled target architectures
-# that Base is built for.
-#CROSS_COMPILER_TARGET_ARCHS = vxWorks-ppc32
-
-# To install files into a location other than $(TOP) define
-# INSTALL_LOCATION here.
-#INSTALL_LOCATION=
-
-# Set this when the IOC and build host use different paths
-# to the install location. This may be needed to boot from
-# a Microsoft FTP server say, or on some NFS configurations.
-#IOCS_APPL_TOP =
-
-# For application debugging purposes, override the HOST_OPT and/
-# or CROSS_OPT settings from base/configure/CONFIG_SITE
-#HOST_OPT = NO
-#CROSS_OPT = NO
-
-# These allow developers to override the CONFIG_SITE variable
-# settings without having to modify the configure/CONFIG_SITE
-# file itself.
--include $(TOP)/../CONFIG_SITE.local
--include $(TOP)/configure/CONFIG_SITE.local
diff --git a/modules/database/configure/Makefile b/modules/database/configure/Makefile
deleted file mode 100644
index dd292091c..000000000
--- a/modules/database/configure/Makefile
+++ /dev/null
@@ -1,15 +0,0 @@
-#*************************************************************************
-# EPICS BASE is distributed subject to a Software License Agreement found
-# in file LICENSE that is included with this distribution.
-#*************************************************************************
-TOP = ..
-
-include $(TOP)/configure/CONFIG
-
-TARGETS = $(CONFIG_TARGETS)
-CONFIGS += $(subst ../,,$(wildcard $(CONFIG_INSTALLS)))
-
-CFG += CONFIG_DATABASE_MODULE
-CFG += CONFIG_DATABASE_VERSION
-
-include $(TOP)/configure/RULES
diff --git a/modules/database/configure/RELEASE b/modules/database/configure/RELEASE
deleted file mode 100644
index 9b685a13e..000000000
--- a/modules/database/configure/RELEASE
+++ /dev/null
@@ -1,40 +0,0 @@
-# RELEASE - Location of external support modules
-#
-# IF YOU CHANGE ANY PATHS in this file or make API changes to
-# any modules it refers to, you should do a "make rebuild" in
-# this application's top level directory.
-#
-# The EPICS build process does not check dependencies against
-# any files from outside the application, so it is safest to
-# rebuild it completely if any modules it depends on change.
-#
-# Host- or target-specific settings can be given in files named
-# RELEASE.$(EPICS_HOST_ARCH).Common
-# RELEASE.Common.$(T_A)
-# RELEASE.$(EPICS_HOST_ARCH).$(T_A)
-#
-# This file is parsed by both GNUmake and an EPICS Perl script,
-# so it may ONLY contain definititions of paths to other support
-# modules, variable definitions that are used in module paths,
-# and include statements that pull in other RELEASE files.
-# Variables may be used before their values have been set.
-# Build variables that are NOT used in paths should be set in
-# the CONFIG_SITE file.
-
-# Variables and paths to dependent modules:
-#MODULES = /path/to/modules
-#MYMODULE = $(MODULES)/my-module
-
-# If building the EPICS modules individually, set these:
-#EPICS_CA = $(MODULES)/ca-4.13.1
-#EPICS_LIBCOM = $(MODULES)/libcom-3.17.0
-#EPICS_BASE = $(MODULES)/core-7.0.1
-
-# Set RULES here if you want to use build rules from elsewhere:
-#RULES = $(MODULES)/build-rules
-
-# These lines allow developers to override these RELEASE settings
-# without having to modify this file directly.
--include $(TOP)/../RELEASE.local
--include $(TOP)/../RELEASE.$(EPICS_HOST_ARCH).local
--include $(TOP)/configure/RELEASE.local
diff --git a/modules/database/configure/RULES b/modules/database/configure/RULES
deleted file mode 100644
index 6d56e14e8..000000000
--- a/modules/database/configure/RULES
+++ /dev/null
@@ -1,6 +0,0 @@
-# RULES
-
-include $(CONFIG)/RULES
-
-# Library should be rebuilt because LIBOBJS may have changed.
-$(LIBNAME): ../Makefile
diff --git a/modules/database/configure/RULES.ioc b/modules/database/configure/RULES.ioc
deleted file mode 100644
index 901987c6c..000000000
--- a/modules/database/configure/RULES.ioc
+++ /dev/null
@@ -1,2 +0,0 @@
-#RULES.ioc
-include $(CONFIG)/RULES.ioc
diff --git a/modules/database/configure/RULES_DIRS b/modules/database/configure/RULES_DIRS
deleted file mode 100644
index 3ba269dcc..000000000
--- a/modules/database/configure/RULES_DIRS
+++ /dev/null
@@ -1,2 +0,0 @@
-#RULES_DIRS
-include $(CONFIG)/RULES_DIRS
diff --git a/modules/database/configure/RULES_TOP b/modules/database/configure/RULES_TOP
deleted file mode 100644
index 2b8cbc6da..000000000
--- a/modules/database/configure/RULES_TOP
+++ /dev/null
@@ -1,2 +0,0 @@
-#RULES_TOP
-include $(CONFIG)/RULES_TOP
diff --git a/modules/database/src/Makefile b/modules/database/src/Makefile
index dfd1d3be9..0cc46fa40 100644
--- a/modules/database/src/Makefile
+++ b/modules/database/src/Makefile
@@ -7,7 +7,7 @@
# in the file LICENSE that is included with this distribution.
#*************************************************************************
-TOP = ..
+TOP = ../../..
include $(TOP)/configure/CONFIG
# PDB Tools
diff --git a/modules/database/src/ioc/Makefile b/modules/database/src/ioc/Makefile
index 325872826..9ff00407d 100644
--- a/modules/database/src/ioc/Makefile
+++ b/modules/database/src/ioc/Makefile
@@ -6,12 +6,14 @@
# EPICS BASE is distributed subject to a Software License Agreement found
# in file LICENSE that is included with this distribution.
#*************************************************************************
-TOP=../..
+IOCDIR := $(or $(dir $(lastword $(MAKEFILE_LIST))), .)
+TOP=../../../..
include $(TOP)/configure/CONFIG
-SRC = $(TOP)/src
-IOCDIR = $(SRC)/ioc
+USR_CPPFLAGS += -DUSE_TYPED_RSET
+
+SHRLIB_VERSION = 3.17.0
LIBRARY_IOC += dbCore
dbCore_LIBS += ca Com
diff --git a/modules/database/src/std/Makefile b/modules/database/src/std/Makefile
index 20a8658cb..2434bf0a4 100644
--- a/modules/database/src/std/Makefile
+++ b/modules/database/src/std/Makefile
@@ -6,11 +6,13 @@
# EPICS BASE is distributed subject to a Software License Agreement found
# in file LICENSE that is included with this distribution.
#*************************************************************************
-
-TOP = ../..
+STDDIR := $(or $(dir $(lastword $(MAKEFILE_LIST))), .)
+TOP = ../../../..
include $(TOP)/configure/CONFIG
-STDDIR=$(TOP)/src/std
+USR_CPPFLAGS += -DUSE_TYPED_RSET
+
+SHRLIB_VERSION = 3.17.0
LIBRARY_IOC += dbRecStd
dbRecStd_LIBS = dbCore ca Com
diff --git a/modules/database/src/template/Makefile b/modules/database/src/template/Makefile
index 6259b6fca..8dfc6c6f3 100644
--- a/modules/database/src/template/Makefile
+++ b/modules/database/src/template/Makefile
@@ -1,4 +1,4 @@
-TOP=../..
+TOP=../../../..
include $(TOP)/configure/CONFIG
diff --git a/modules/database/src/tools/Makefile b/modules/database/src/tools/Makefile
index e19c0c58f..bfd329fbe 100644
--- a/modules/database/src/tools/Makefile
+++ b/modules/database/src/tools/Makefile
@@ -4,7 +4,7 @@
# EPICS BASE is distributed subject to a Software License Agreement found
# in file LICENSE that is included with this distribution.
#*************************************************************************
-TOP=../..
+TOP=../../../..
include $(TOP)/configure/CONFIG
diff --git a/modules/database/test/Makefile b/modules/database/test/Makefile
index 0f2c64263..b54d01757 100644
--- a/modules/database/test/Makefile
+++ b/modules/database/test/Makefile
@@ -7,7 +7,7 @@
# in file LICENSE that is included with this distribution.
#*************************************************************************
-TOP = ..
+TOP = ../../..
include $(TOP)/configure/CONFIG
DIRS += ioc/db
diff --git a/modules/database/test/ioc/db/Makefile b/modules/database/test/ioc/db/Makefile
index e1f06cd3b..ee2ab7632 100644
--- a/modules/database/test/ioc/db/Makefile
+++ b/modules/database/test/ioc/db/Makefile
@@ -6,12 +6,13 @@
# EPICS BASE is distributed subject to a Software License Agreement found
# in the file LICENSE that is included with this distribution.
#*************************************************************************
-
-TOP = ../../..
+CURDIR := $(or $(dir $(lastword $(MAKEFILE_LIST))), .)
+TOP = ../../../../..
include $(TOP)/configure/CONFIG
# Allow access to private headers in db/
-USR_CPPFLAGS = -I $(TOP)/src/ioc/db
+USR_CPPFLAGS += -I $(CURDIR)/../../../src/ioc/db
+USR_CPPFLAGS += -DUSE_TYPED_RSET
TESTLIBRARY = dbTestIoc
diff --git a/modules/database/test/ioc/dbtemplate/Makefile b/modules/database/test/ioc/dbtemplate/Makefile
index 4442a4ff8..dbb13f337 100644
--- a/modules/database/test/ioc/dbtemplate/Makefile
+++ b/modules/database/test/ioc/dbtemplate/Makefile
@@ -5,7 +5,7 @@
# in the file LICENSE that is included with this distribution.
#*************************************************************************
-TOP = ../../..
+TOP = ../../../../..
include $(TOP)/configure/CONFIG
diff --git a/modules/database/test/std/filters/Makefile b/modules/database/test/std/filters/Makefile
index 718b8d523..c95e10861 100644
--- a/modules/database/test/std/filters/Makefile
+++ b/modules/database/test/std/filters/Makefile
@@ -7,10 +7,12 @@
# in the file LICENSE that is included with this distribution.
#*************************************************************************
-TOP = ../../..
+TOP = ../../../../..
include $(TOP)/configure/CONFIG
+USR_CPPFLAGS += -DUSE_TYPED_RSET
+
TESTLIBRARY = Recs
Recs_SRCS += xRecord.c
diff --git a/modules/database/test/std/link/Makefile b/modules/database/test/std/link/Makefile
index 8540cdf84..c72301bea 100644
--- a/modules/database/test/std/link/Makefile
+++ b/modules/database/test/std/link/Makefile
@@ -8,6 +8,8 @@ TOP=../../..
include $(TOP)/configure/CONFIG
+USR_CPPFLAGS += -DUSE_TYPED_RSET
+
TESTLIBRARY = Recs
Recs_SRCS += ioRecord.c
diff --git a/modules/database/test/std/rec/Makefile b/modules/database/test/std/rec/Makefile
index 185c2c0c1..872087521 100644
--- a/modules/database/test/std/rec/Makefile
+++ b/modules/database/test/std/rec/Makefile
@@ -7,9 +7,11 @@
# in the file LICENSE that is included with this distribution.
#*************************************************************************
-TOP = ../../..
+TOP = ../../../../..
include $(TOP)/configure/CONFIG
+USR_CPPFLAGS += -DUSE_TYPED_RSET
+
TESTLIBRARY = dbRecStdTest
dbRecStdTest_SRCS += asTestLib.c
diff --git a/modules/database/test/tools/Makefile b/modules/database/test/tools/Makefile
index ac7f42071..2dd045e8e 100644
--- a/modules/database/test/tools/Makefile
+++ b/modules/database/test/tools/Makefile
@@ -5,7 +5,7 @@
# in the file LICENSE that is included with this distribution.
#*************************************************************************
-TOP = ../..
+TOP = ../../../..
include $(TOP)/configure/CONFIG
diff --git a/modules/libcom/.ci/travis-build.sh b/modules/libcom/.ci/travis-build.sh
deleted file mode 100755
index 622979b9d..000000000
--- a/modules/libcom/.ci/travis-build.sh
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/bin/sh
-set -e -x
-
-# set RTEMS to eg. "4.9" or "4.10"
-# requires qemu, bison, flex, texinfo, install-info
-if [ -n "$RTEMS" ]
-then
- # find local qemu-system-i386
- export PATH="$HOME/.cache/qemu/usr/bin:$PATH"
- echo -n "Using QEMU: "
- type qemu-system-i386 || echo "Missing qemu"
- EXTRA=RTEMS_QEMU_FIXUPS=YES
-fi
-
-make -j2 $EXTRA
-
-if [ "$TEST" != "NO" ]
-then
- make -j2 tapfiles
- make -s test-results
-fi
diff --git a/modules/libcom/.ci/travis-prepare.sh b/modules/libcom/.ci/travis-prepare.sh
deleted file mode 100755
index 9a4d23772..000000000
--- a/modules/libcom/.ci/travis-prepare.sh
+++ /dev/null
@@ -1,123 +0,0 @@
-#!/bin/sh
-set -e -x
-
-CURDIR="$PWD"
-
-QDIR="$HOME/.cache/qemu"
-
-if [ -n "$RTEMS" -a "$TEST" = "YES" ]
-then
- git clone --quiet --branch vme --depth 10 https://github.com/mdavidsaver/qemu.git "$HOME/.build/qemu"
- cd "$HOME/.build/qemu"
-
- HEAD=`git log -n1 --pretty=format:%H`
- echo "HEAD revision $HEAD"
-
- [ -e "$HOME/.cache/qemu/built" ] && BUILT=`cat "$HOME/.cache/qemu/built"`
- echo "Cached revision $BUILT"
-
- if [ "$HEAD" != "$BUILT" ]
- then
- echo "Building QEMU"
- git submodule --quiet update --init
-
- install -d "$HOME/.build/qemu/build"
- cd "$HOME/.build/qemu/build"
-
- "$HOME/.build/qemu/configure" --prefix="$HOME/.cache/qemu/usr" --target-list=i386-softmmu --disable-werror
- make -j2
- make install
-
- echo "$HEAD" > "$HOME/.cache/qemu/built"
- fi
-fi
-
-cd "$CURDIR"
-
-cat << EOF > configure/RELEASE.local
-EPICS_BASE=$HOME/.source/epics-base
-EOF
-
-install -d "$HOME/.source"
-cd "$HOME/.source"
-
-git clone --quiet --depth 5 --branch core/"${BRCORE:-master}" https://github.com/${REPOBASE:-epics-base}/epics-base.git epics-base
-(cd epics-base && git log -n1 )
-
-EPICS_HOST_ARCH=`sh epics-base/startup/EpicsHostArch`
-
-# requires wine and g++-mingw-w64-i686
-if [ "$WINE" = "32" ]
-then
- echo "Cross mingw32"
- sed -i -e '/CMPLR_PREFIX/d' epics-base/configure/os/CONFIG_SITE.linux-x86.win32-x86-mingw
- cat << EOF >> epics-base/configure/os/CONFIG_SITE.linux-x86.win32-x86-mingw
-CMPLR_PREFIX=i686-w64-mingw32-
-EOF
- cat << EOF >> epics-base/configure/CONFIG_SITE
-CROSS_COMPILER_TARGET_ARCHS+=win32-x86-mingw
-EOF
-fi
-
-if [ "$STATIC" = "YES" ]
-then
- echo "Build static libraries/executables"
- cat << EOF >> epics-base/configure/CONFIG_SITE
-SHARED_LIBRARIES=NO
-STATIC_BUILD=YES
-EOF
-fi
-
-case "$CMPLR" in
-clang)
- echo "Host compiler is clang"
- cat << EOF >> epics-base/configure/os/CONFIG_SITE.Common.$EPICS_HOST_ARCH
-GNU = NO
-CMPLR_CLASS = clang
-CC = clang
-CCC = clang++
-EOF
-
- # hack
- sed -i -e 's/CMPLR_CLASS = gcc/CMPLR_CLASS = clang/' epics-base/configure/CONFIG.gnuCommon
-
- clang --version
- ;;
-*)
- echo "Host compiler is default"
- gcc --version
- ;;
-esac
-
-cat <> epics-base/configure/CONFIG_SITE
-USR_CPPFLAGS += $USR_CPPFLAGS
-USR_CFLAGS += $USR_CFLAGS
-USR_CXXFLAGS += $USR_CXXFLAGS
-EOF
-
-# set RTEMS to eg. "4.9" or "4.10"
-# requires qemu, bison, flex, texinfo, install-info
-if [ -n "$RTEMS" ]
-then
- echo "Cross RTEMS${RTEMS} for pc386"
- install -d /home/travis/.cache
- curl -L "https://github.com/mdavidsaver/rsb/releases/download/travis-20160306-2/rtems${RTEMS}-i386-trusty-20190306-2.tar.gz" \
- | tar -C /home/travis/.cache -xj
-
- sed -i -e '/^RTEMS_VERSION/d' -e '/^RTEMS_BASE/d' epics-base/configure/os/CONFIG_SITE.Common.RTEMS
- cat << EOF >> epics-base/configure/os/CONFIG_SITE.Common.RTEMS
-RTEMS_VERSION=$RTEMS
-RTEMS_BASE=/home/travis/.cache/rtems${RTEMS}-i386
-EOF
- cat << EOF >> epics-base/configure/CONFIG_SITE
-CROSS_COMPILER_TARGET_ARCHS+=RTEMS-pc386
-EOF
-
- # find local qemu-system-i386
- export PATH="$HOME/.cache/qemu/usr/bin:$PATH"
- echo -n "Using QEMU: "
- type qemu-system-i386 || echo "Missing qemu"
- EXTRA=RTEMS_QEMU_FIXUPS=YES
-fi
-
-make -j2 -C epics-base $EXTRA
diff --git a/modules/libcom/.travis.yml b/modules/libcom/.travis.yml
deleted file mode 100644
index 8b1e2ab3e..000000000
--- a/modules/libcom/.travis.yml
+++ /dev/null
@@ -1,26 +0,0 @@
-sudo: false
-dist: trusty
-language: c
-compiler:
- - gcc
-addons:
- apt:
- packages:
- - libreadline6-dev
- - libncurses5-dev
- - perl
- - clang
- - g++-mingw-w64-i686
-install:
- - ./.ci/travis-prepare.sh
-script:
- - ./.ci/travis-build.sh
-env:
- - BRCORE=master
- - CMPLR=clang
- - USR_CXXFLAGS=-std=c++11
- - CMPLR=clang USR_CXXFLAGS=-std=c++11
- - WINE=32 TEST=NO STATIC=YES
- - WINE=32 TEST=NO STATIC=NO
- - RTEMS=4.10 TEST=NO
- - RTEMS=4.9 TEST=NO
diff --git a/modules/libcom/Makefile b/modules/libcom/Makefile
index 442a6f7d1..5a03a4348 100644
--- a/modules/libcom/Makefile
+++ b/modules/libcom/Makefile
@@ -7,11 +7,10 @@
# in file LICENSE that is included with this distribution.
#*************************************************************************
-TOP = .
+TOP = ../..
include $(TOP)/configure/CONFIG
-DIRS += configure src
-src_DEPEND_DIRS = configure
+DIRS += src
DIRS += RTEMS
RTEMS_DEPEND_DIRS = src
@@ -22,4 +21,4 @@ vxWorks_DEPEND_DIRS = src
DIRS += test
test_DEPEND_DIRS = RTEMS vxWorks
-include $(TOP)/configure/RULES_TOP
+include $(TOP)/configure/RULES_DIRS
diff --git a/modules/libcom/RTEMS/Makefile b/modules/libcom/RTEMS/Makefile
index 311c25024..b46889db1 100644
--- a/modules/libcom/RTEMS/Makefile
+++ b/modules/libcom/RTEMS/Makefile
@@ -7,7 +7,7 @@
# in file LICENSE that is included with this distribution.
#*************************************************************************
-TOP = ..
+TOP = ../../..
include $(TOP)/configure/CONFIG
include $(TOP)/configure/CONFIG_LIBCOM_VERSION
diff --git a/modules/libcom/configure/CONFIG b/modules/libcom/configure/CONFIG
deleted file mode 100644
index 774ed251c..000000000
--- a/modules/libcom/configure/CONFIG
+++ /dev/null
@@ -1,44 +0,0 @@
-# CONFIG - Load build configuration data
-#
-# Do not make changes to this file!
-
-ifeq ($(strip $(EPICS_HOST_ARCH)),)
- $(warning EPICS_HOST_ARCH is not set.)
-endif
-
-# Allow user to override where the build rules come from
-RULES = $(EPICS_BASE)
-
-# RELEASE files point to other application tops
-include $(TOP)/configure/RELEASE
--include $(TOP)/configure/RELEASE.$(EPICS_HOST_ARCH).Common
-ifdef T_A
--include $(TOP)/configure/RELEASE.Common.$(T_A)
--include $(TOP)/configure/RELEASE.$(EPICS_HOST_ARCH).$(T_A)
-endif
-
-ifeq ($(strip $(RULES)),)
- ifeq ($(strip $(EPICS_BASE)),)
- $(warning Build error: EPICS_BASE not set after including RELEASE files.)
- else
- $(warning Build error: EPICS_BASE set but RULES variable empty.)
- endif
- $(error Makefiles loaded: $(MAKEFILE_LIST))
- # Die before the include of $(CONFIG)/CONFIG below does
-endif
-
-BUILDING_LIBCOM = DEFINED
-
-CONFIG = $(RULES)/configure
-include $(CONFIG)/CONFIG
-
-# Override the Base definition:
-INSTALL_LOCATION = $(TOP)
-
-# CONFIG_SITE files contain other build configuration settings
-include $(TOP)/configure/CONFIG_SITE
--include $(TOP)/configure/CONFIG_SITE.$(EPICS_HOST_ARCH).Common
-ifdef T_A
- -include $(TOP)/configure/CONFIG_SITE.Common.$(T_A)
- -include $(TOP)/configure/CONFIG_SITE.$(EPICS_HOST_ARCH).$(T_A)
-endif
diff --git a/modules/libcom/configure/CONFIG_SITE b/modules/libcom/configure/CONFIG_SITE
deleted file mode 100644
index d78c7f514..000000000
--- a/modules/libcom/configure/CONFIG_SITE
+++ /dev/null
@@ -1,42 +0,0 @@
-# CONFIG_SITE
-
-# Make any application-specific changes to the EPICS build
-# configuration variables in this file.
-#
-# Host/target specific settings can be specified in files named
-# CONFIG_SITE.$(EPICS_HOST_ARCH).Common
-# CONFIG_SITE.Common.$(T_A)
-# CONFIG_SITE.$(EPICS_HOST_ARCH).$(T_A)
-
-# CHECK_RELEASE controls the consistency checking of the support
-# applications pointed to by the RELEASE* files.
-# Normally CHECK_RELEASE should be set to YES.
-# Set CHECK_RELEASE to NO to disable checking completely.
-# Set CHECK_RELEASE to WARN to perform consistency checking but
-# continue building even if conflicts are found.
-CHECK_RELEASE = YES
-
-# Set this when you only want to compile this application
-# for a subset of the cross-compiled target architectures
-# that Base is built for.
-#CROSS_COMPILER_TARGET_ARCHS = vxWorks-ppc32
-
-# To install files into a location other than $(TOP) define
-# INSTALL_LOCATION here.
-#INSTALL_LOCATION=
-
-# Set this when the IOC and build host use different paths
-# to the install location. This may be needed to boot from
-# a Microsoft FTP server say, or on some NFS configurations.
-#IOCS_APPL_TOP =
-
-# For application debugging purposes, override the HOST_OPT and/
-# or CROSS_OPT settings from base/configure/CONFIG_SITE
-#HOST_OPT = NO
-#CROSS_OPT = NO
-
-# These allow developers to override the CONFIG_SITE variable
-# settings without having to modify the configure/CONFIG_SITE
-# file itself.
--include $(TOP)/../CONFIG_SITE.local
--include $(TOP)/configure/CONFIG_SITE.local
diff --git a/modules/libcom/configure/Makefile b/modules/libcom/configure/Makefile
deleted file mode 100644
index 85a7b5843..000000000
--- a/modules/libcom/configure/Makefile
+++ /dev/null
@@ -1,15 +0,0 @@
-#*************************************************************************
-# EPICS BASE is distributed subject to a Software License Agreement found
-# in file LICENSE that is included with this distribution.
-#*************************************************************************
-TOP = ..
-
-include $(TOP)/configure/CONFIG
-
-TARGETS = $(CONFIG_TARGETS)
-CONFIGS += $(subst ../,,$(wildcard $(CONFIG_INSTALLS)))
-
-CFG += CONFIG_LIBCOM_MODULE
-CFG += CONFIG_LIBCOM_VERSION
-
-include $(TOP)/configure/RULES
diff --git a/modules/libcom/configure/RELEASE b/modules/libcom/configure/RELEASE
deleted file mode 100644
index 819b441e7..000000000
--- a/modules/libcom/configure/RELEASE
+++ /dev/null
@@ -1,38 +0,0 @@
-# RELEASE - Location of external support modules
-#
-# IF YOU CHANGE ANY PATHS in this file or make API changes to
-# any modules it refers to, you should do a "make rebuild" in
-# this application's top level directory.
-#
-# The EPICS build process does not check dependencies against
-# any files from outside the application, so it is safest to
-# rebuild it completely if any modules it depends on change.
-#
-# Host- or target-specific settings can be given in files named
-# RELEASE.$(EPICS_HOST_ARCH).Common
-# RELEASE.Common.$(T_A)
-# RELEASE.$(EPICS_HOST_ARCH).$(T_A)
-#
-# This file is parsed by both GNUmake and an EPICS Perl script,
-# so it may ONLY contain definititions of paths to other support
-# modules, variable definitions that are used in module paths,
-# and include statements that pull in other RELEASE files.
-# Variables may be used before their values have been set.
-# Build variables that are NOT used in paths should be set in
-# the CONFIG_SITE file.
-
-# Variables and paths to dependent modules:
-#MODULES = /path/to/modules
-#MYMODULE = $(MODULES)/my-module
-
-# If building the EPICS modules individually, set these:
-#EPICS_BASE = $(MODULES)/core-7.0.1
-
-# Set RULES here if you want to use build rules from elsewhere:
-#RULES = $(MODULES)/build-rules
-
-# These lines allow developers to override these RELEASE settings
-# without having to modify this file directly.
--include $(TOP)/../RELEASE.local
--include $(TOP)/../RELEASE.$(EPICS_HOST_ARCH).local
--include $(TOP)/configure/RELEASE.local
diff --git a/modules/libcom/configure/RULES b/modules/libcom/configure/RULES
deleted file mode 100644
index 6d56e14e8..000000000
--- a/modules/libcom/configure/RULES
+++ /dev/null
@@ -1,6 +0,0 @@
-# RULES
-
-include $(CONFIG)/RULES
-
-# Library should be rebuilt because LIBOBJS may have changed.
-$(LIBNAME): ../Makefile
diff --git a/modules/libcom/configure/RULES_DIRS b/modules/libcom/configure/RULES_DIRS
deleted file mode 100644
index 3ba269dcc..000000000
--- a/modules/libcom/configure/RULES_DIRS
+++ /dev/null
@@ -1,2 +0,0 @@
-#RULES_DIRS
-include $(CONFIG)/RULES_DIRS
diff --git a/modules/libcom/configure/RULES_TOP b/modules/libcom/configure/RULES_TOP
deleted file mode 100644
index 2b8cbc6da..000000000
--- a/modules/libcom/configure/RULES_TOP
+++ /dev/null
@@ -1,2 +0,0 @@
-#RULES_TOP
-include $(CONFIG)/RULES_TOP
diff --git a/modules/libcom/src/Makefile b/modules/libcom/src/Makefile
index d61b26803..4a1c61e38 100644
--- a/modules/libcom/src/Makefile
+++ b/modules/libcom/src/Makefile
@@ -6,15 +6,13 @@
# EPICS BASE is distributed subject to a Software License Agreement found
# in file LICENSE that is included with this distribution.
#*************************************************************************
-
-TOP = ..
+LIBCOM := $(or $(dir $(lastword $(MAKEFILE_LIST))), .)
+TOP = ../../..
include $(TOP)/configure/CONFIG
# Uncomment this to remove the (benign) valgrind helper stubs
#USR_CFLAGS += -DNVALGRIND
-LIBCOM = $(TOP)/src
-
INC += valgrind/valgrind.h
INC += libComVersion.h
diff --git a/modules/libcom/test/Makefile b/modules/libcom/test/Makefile
index 1c26c44b7..87405f72f 100755
--- a/modules/libcom/test/Makefile
+++ b/modules/libcom/test/Makefile
@@ -7,7 +7,7 @@
# in file LICENSE that is included with this distribution.
#*************************************************************************
-TOP = ..
+TOP = ../../..
include $(TOP)/configure/CONFIG
PROD_LIBS += Com
diff --git a/modules/libcom/vxWorks/Makefile b/modules/libcom/vxWorks/Makefile
index e2c0b5d2d..3eced3b08 100644
--- a/modules/libcom/vxWorks/Makefile
+++ b/modules/libcom/vxWorks/Makefile
@@ -5,7 +5,7 @@
# in file LICENSE that is included with this distribution.
#*************************************************************************
-TOP = ..
+TOP = ../../..
include $(TOP)/configure/CONFIG
# Install Boost smart_ptr headers needed by VxWorks 6.x
From d7841f407a7a62773fcab625384fa80d18fcf02a Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Mon, 31 Dec 2018 10:25:17 -0800
Subject: [PATCH 018/281] drop unnecessary *ModuleDirs
---
modules/ca/src/perl/Makefile | 2 --
modules/ca/src/perl/capr.pl | 1 -
modules/database/src/tools/Makefile | 2 --
modules/database/src/tools/dbExpand.pl | 1 -
modules/database/src/tools/dbdExpand.pl | 1 -
modules/database/src/tools/dbdReport.pl | 1 -
modules/database/src/tools/dbdToHtml.pl | 1 -
modules/database/src/tools/dbdToMenuH.pl | 1 -
modules/database/src/tools/dbdToRecordtypeH.pl | 1 -
modules/database/src/tools/registerRecordDeviceDriver.pl | 1 -
modules/libcom/src/env/Makefile | 1 -
modules/libcom/src/env/RULES | 2 +-
modules/libcom/src/env/bldEnvData.pl | 1 -
13 files changed, 1 insertion(+), 15 deletions(-)
diff --git a/modules/ca/src/perl/Makefile b/modules/ca/src/perl/Makefile
index 65f42ee3a..12e0c82b5 100644
--- a/modules/ca/src/perl/Makefile
+++ b/modules/ca/src/perl/Makefile
@@ -42,8 +42,6 @@ ifeq ($(findstring $(OS_CLASS),WIN32 cygwin32),) # Doesn't build on WIN32
PERL_MODULES += CA.pm
PERL_MODULES += $(PERL_ARCHPATH)/$(LOADABLE_SHRLIB_PREFIX)Cap5$(LOADABLE_SHRLIB_SUFFIX)
- PERL_SCRIPTS += caModuleDirs.pm
-
HTMLS_DIR = .
HTMLS = CA.html
endif
diff --git a/modules/ca/src/perl/capr.pl b/modules/ca/src/perl/capr.pl
index e551afcef..ba5998a14 100644
--- a/modules/ca/src/perl/capr.pl
+++ b/modules/ca/src/perl/capr.pl
@@ -11,7 +11,6 @@ use strict;
use FindBin qw($Bin);
use lib ($Bin, "$Bin/../../lib/perl");
-use caModuleDirs;
no lib $Bin;
use Getopt::Std;
diff --git a/modules/database/src/tools/Makefile b/modules/database/src/tools/Makefile
index bfd329fbe..f54ab9c07 100644
--- a/modules/database/src/tools/Makefile
+++ b/modules/database/src/tools/Makefile
@@ -27,8 +27,6 @@ PERL_MODULES += DBD/Variable.pm
PERL_MODULES += EPICS/IOC.pm
HTMLS += EPICS/IOC.html
-PERL_SCRIPTS += databaseModuleDirs.pm
-
PERL_SCRIPTS += makeIncludeDbd.pl
PERL_SCRIPTS += dbdToMenuH.pl
diff --git a/modules/database/src/tools/dbExpand.pl b/modules/database/src/tools/dbExpand.pl
index 25cab26cc..35d7750d9 100644
--- a/modules/database/src/tools/dbExpand.pl
+++ b/modules/database/src/tools/dbExpand.pl
@@ -13,7 +13,6 @@ use strict;
use FindBin qw($Bin);
use lib ($Bin, "$Bin/../../lib/perl");
-use databaseModuleDirs;
no lib $Bin;
use DBD;
diff --git a/modules/database/src/tools/dbdExpand.pl b/modules/database/src/tools/dbdExpand.pl
index a87ef47bb..5f12dc23e 100644
--- a/modules/database/src/tools/dbdExpand.pl
+++ b/modules/database/src/tools/dbdExpand.pl
@@ -11,7 +11,6 @@ use strict;
use FindBin qw($Bin);
use lib ($Bin, "$Bin/../../lib/perl");
-use databaseModuleDirs;
no lib $Bin;
use DBD;
diff --git a/modules/database/src/tools/dbdReport.pl b/modules/database/src/tools/dbdReport.pl
index 0d4c85494..adc268345 100644
--- a/modules/database/src/tools/dbdReport.pl
+++ b/modules/database/src/tools/dbdReport.pl
@@ -9,7 +9,6 @@
use FindBin qw($Bin);
use lib ($Bin, "$Bin/../../lib/perl");
-use databaseModuleDirs;
no lib $Bin;
use DBD;
diff --git a/modules/database/src/tools/dbdToHtml.pl b/modules/database/src/tools/dbdToHtml.pl
index e9711f757..fa1cc99d7 100644
--- a/modules/database/src/tools/dbdToHtml.pl
+++ b/modules/database/src/tools/dbdToHtml.pl
@@ -11,7 +11,6 @@ use strict;
use FindBin qw($Bin);
use lib ($Bin, "$Bin/../../lib/perl");
-use databaseModuleDirs;
no lib $Bin;
use DBD;
diff --git a/modules/database/src/tools/dbdToMenuH.pl b/modules/database/src/tools/dbdToMenuH.pl
index ac4345b42..30418a351 100644
--- a/modules/database/src/tools/dbdToMenuH.pl
+++ b/modules/database/src/tools/dbdToMenuH.pl
@@ -9,7 +9,6 @@
use FindBin qw($Bin);
use lib ($Bin, "$Bin/../../lib/perl");
-use databaseModuleDirs;
no lib $Bin;
use EPICS::Getopts;
diff --git a/modules/database/src/tools/dbdToRecordtypeH.pl b/modules/database/src/tools/dbdToRecordtypeH.pl
index b1eb77d93..f1336a147 100644
--- a/modules/database/src/tools/dbdToRecordtypeH.pl
+++ b/modules/database/src/tools/dbdToRecordtypeH.pl
@@ -9,7 +9,6 @@
use FindBin qw($Bin);
use lib ($Bin, "$Bin/../../lib/perl");
-use databaseModuleDirs;
no lib $Bin;
use EPICS::Getopts;
diff --git a/modules/database/src/tools/registerRecordDeviceDriver.pl b/modules/database/src/tools/registerRecordDeviceDriver.pl
index 02bb9b772..671d24051 100644
--- a/modules/database/src/tools/registerRecordDeviceDriver.pl
+++ b/modules/database/src/tools/registerRecordDeviceDriver.pl
@@ -13,7 +13,6 @@ use strict;
use FindBin qw($Bin);
use lib ($Bin, "$Bin/../../lib/perl");
-use databaseModuleDirs;
no lib $Bin;
use DBD;
diff --git a/modules/libcom/src/env/Makefile b/modules/libcom/src/env/Makefile
index 804426bc0..94322a228 100644
--- a/modules/libcom/src/env/Makefile
+++ b/modules/libcom/src/env/Makefile
@@ -12,7 +12,6 @@ SRC_DIRS += $(LIBCOM)/env
vpath %.pl $(USR_VPATH) $(SRC_DIRS)
PERL_SCRIPTS += bldEnvData.pl
-PERL_SCRIPTS += libcomModuleDirs.pm
INC += envDefs.h
diff --git a/modules/libcom/src/env/RULES b/modules/libcom/src/env/RULES
index 02df6aa7a..5ef766d57 100644
--- a/modules/libcom/src/env/RULES
+++ b/modules/libcom/src/env/RULES
@@ -8,7 +8,7 @@
# This is a Makefile fragment, see src/libCom/Makefile.
envData.c: $(LIBCOM)/env/envDefs.h \
- $(INSTALL_HOST_BIN)/bldEnvData.pl $(INSTALL_HOST_BIN)/libcomModuleDirs.pm \
+ $(INSTALL_HOST_BIN)/bldEnvData.pl \
$(CONFIG)/CONFIG_ENV $(CONFIG)/CONFIG_SITE_ENV \
$(wildcard $(CONFIG)/os/CONFIG_SITE_ENV.$(T_A))
$(PERL) $(INSTALL_HOST_BIN)/bldEnvData.pl $(QUIET_FLAG) -t $(T_A) \
diff --git a/modules/libcom/src/env/bldEnvData.pl b/modules/libcom/src/env/bldEnvData.pl
index e3c21b772..f638c841f 100644
--- a/modules/libcom/src/env/bldEnvData.pl
+++ b/modules/libcom/src/env/bldEnvData.pl
@@ -15,7 +15,6 @@ use strict;
use FindBin qw($Bin);
use lib ($Bin, "$Bin/../../lib/perl");
-use libcomModuleDirs;
no lib $Bin;
use Getopt::Std;
From d38fcd52473a5c9d43cee891f7d47a526fbe1a99 Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Mon, 31 Dec 2018 10:31:48 -0800
Subject: [PATCH 019/281] no need to search for perl modules under bin/
---
modules/ca/src/perl/capr.pl | 2 +-
modules/ca/src/template/top/caPerlApp/caget.pl | 2 +-
modules/ca/src/template/top/caPerlApp/cainfo.pl | 2 +-
modules/ca/src/template/top/caPerlApp/camonitor.pl | 2 +-
modules/ca/src/template/top/caPerlApp/caput.pl | 2 +-
modules/database/src/tools/dbExpand.pl | 2 +-
modules/database/src/tools/dbdExpand.pl | 2 +-
modules/database/src/tools/dbdReport.pl | 2 +-
modules/database/src/tools/dbdToHtml.pl | 2 +-
modules/database/src/tools/dbdToMenuH.pl | 2 +-
modules/database/src/tools/dbdToRecordtypeH.pl | 2 +-
modules/database/src/tools/registerRecordDeviceDriver.pl | 2 +-
modules/libcom/src/env/bldEnvData.pl | 2 +-
src/template/base/makeBaseApp.pl | 2 +-
src/tools/convertRelease.pl | 2 +-
src/tools/expandVars.pl | 2 +-
src/tools/fullPathName.pl | 2 +-
17 files changed, 17 insertions(+), 17 deletions(-)
diff --git a/modules/ca/src/perl/capr.pl b/modules/ca/src/perl/capr.pl
index ba5998a14..17a6dd04b 100644
--- a/modules/ca/src/perl/capr.pl
+++ b/modules/ca/src/perl/capr.pl
@@ -10,7 +10,7 @@
use strict;
use FindBin qw($Bin);
-use lib ($Bin, "$Bin/../../lib/perl");
+use lib ("$Bin/../../lib/perl");
no lib $Bin;
use Getopt::Std;
diff --git a/modules/ca/src/template/top/caPerlApp/caget.pl b/modules/ca/src/template/top/caPerlApp/caget.pl
index 0d9af37a1..dae3a778a 100644
--- a/modules/ca/src/template/top/caPerlApp/caget.pl
+++ b/modules/ca/src/template/top/caPerlApp/caget.pl
@@ -4,7 +4,7 @@ use strict;
# This construct sets @INC to search lib/perl of all RELEASE entries
use FindBin qw($Bin);
-use lib ($Bin, "$Bin/../../lib/perl");
+use lib ("$Bin/../../lib/perl");
use _APPNAME_ModuleDirs;
no lib $Bin;
diff --git a/modules/ca/src/template/top/caPerlApp/cainfo.pl b/modules/ca/src/template/top/caPerlApp/cainfo.pl
index 3e38e8baf..37c705e26 100644
--- a/modules/ca/src/template/top/caPerlApp/cainfo.pl
+++ b/modules/ca/src/template/top/caPerlApp/cainfo.pl
@@ -4,7 +4,7 @@ use strict;
# This construct sets @INC to search lib/perl of all RELEASE entries
use FindBin qw($Bin);
-use lib ($Bin, "$Bin/../../lib/perl");
+use lib ("$Bin/../../lib/perl");
use _APPNAME_ModuleDirs;
no lib $Bin;
diff --git a/modules/ca/src/template/top/caPerlApp/camonitor.pl b/modules/ca/src/template/top/caPerlApp/camonitor.pl
index 564688d68..15c0212f0 100644
--- a/modules/ca/src/template/top/caPerlApp/camonitor.pl
+++ b/modules/ca/src/template/top/caPerlApp/camonitor.pl
@@ -4,7 +4,7 @@ use strict;
# This construct sets @INC to search lib/perl of all RELEASE entries
use FindBin qw($Bin);
-use lib ($Bin, "$Bin/../../lib/perl");
+use lib ("$Bin/../../lib/perl");
use _APPNAME_ModuleDirs;
no lib $Bin;
diff --git a/modules/ca/src/template/top/caPerlApp/caput.pl b/modules/ca/src/template/top/caPerlApp/caput.pl
index 64ff9cbda..b08c9d200 100644
--- a/modules/ca/src/template/top/caPerlApp/caput.pl
+++ b/modules/ca/src/template/top/caPerlApp/caput.pl
@@ -4,7 +4,7 @@ use strict;
# This construct sets @INC to search lib/perl of all RELEASE entries
use FindBin qw($Bin);
-use lib ($Bin, "$Bin/../../lib/perl");
+use lib ("$Bin/../../lib/perl");
use _APPNAME_ModuleDirs;
no lib $Bin;
diff --git a/modules/database/src/tools/dbExpand.pl b/modules/database/src/tools/dbExpand.pl
index 35d7750d9..e4088d007 100644
--- a/modules/database/src/tools/dbExpand.pl
+++ b/modules/database/src/tools/dbExpand.pl
@@ -12,7 +12,7 @@
use strict;
use FindBin qw($Bin);
-use lib ($Bin, "$Bin/../../lib/perl");
+use lib ("$Bin/../../lib/perl");
no lib $Bin;
use DBD;
diff --git a/modules/database/src/tools/dbdExpand.pl b/modules/database/src/tools/dbdExpand.pl
index 5f12dc23e..7e9ba8b5a 100644
--- a/modules/database/src/tools/dbdExpand.pl
+++ b/modules/database/src/tools/dbdExpand.pl
@@ -10,7 +10,7 @@
use strict;
use FindBin qw($Bin);
-use lib ($Bin, "$Bin/../../lib/perl");
+use lib ("$Bin/../../lib/perl");
no lib $Bin;
use DBD;
diff --git a/modules/database/src/tools/dbdReport.pl b/modules/database/src/tools/dbdReport.pl
index adc268345..8a82d690b 100644
--- a/modules/database/src/tools/dbdReport.pl
+++ b/modules/database/src/tools/dbdReport.pl
@@ -8,7 +8,7 @@
#*************************************************************************
use FindBin qw($Bin);
-use lib ($Bin, "$Bin/../../lib/perl");
+use lib ("$Bin/../../lib/perl");
no lib $Bin;
use DBD;
diff --git a/modules/database/src/tools/dbdToHtml.pl b/modules/database/src/tools/dbdToHtml.pl
index fa1cc99d7..87fc23230 100644
--- a/modules/database/src/tools/dbdToHtml.pl
+++ b/modules/database/src/tools/dbdToHtml.pl
@@ -10,7 +10,7 @@
use strict;
use FindBin qw($Bin);
-use lib ($Bin, "$Bin/../../lib/perl");
+use lib ("$Bin/../../lib/perl");
no lib $Bin;
use DBD;
diff --git a/modules/database/src/tools/dbdToMenuH.pl b/modules/database/src/tools/dbdToMenuH.pl
index 30418a351..e80ae3340 100644
--- a/modules/database/src/tools/dbdToMenuH.pl
+++ b/modules/database/src/tools/dbdToMenuH.pl
@@ -8,7 +8,7 @@
#*************************************************************************
use FindBin qw($Bin);
-use lib ($Bin, "$Bin/../../lib/perl");
+use lib ("$Bin/../../lib/perl");
no lib $Bin;
use EPICS::Getopts;
diff --git a/modules/database/src/tools/dbdToRecordtypeH.pl b/modules/database/src/tools/dbdToRecordtypeH.pl
index f1336a147..028309fc4 100644
--- a/modules/database/src/tools/dbdToRecordtypeH.pl
+++ b/modules/database/src/tools/dbdToRecordtypeH.pl
@@ -8,7 +8,7 @@
#*************************************************************************
use FindBin qw($Bin);
-use lib ($Bin, "$Bin/../../lib/perl");
+use lib ("$Bin/../../lib/perl");
no lib $Bin;
use EPICS::Getopts;
diff --git a/modules/database/src/tools/registerRecordDeviceDriver.pl b/modules/database/src/tools/registerRecordDeviceDriver.pl
index 671d24051..507385e57 100644
--- a/modules/database/src/tools/registerRecordDeviceDriver.pl
+++ b/modules/database/src/tools/registerRecordDeviceDriver.pl
@@ -12,7 +12,7 @@
use strict;
use FindBin qw($Bin);
-use lib ($Bin, "$Bin/../../lib/perl");
+use lib ("$Bin/../../lib/perl");
no lib $Bin;
use DBD;
diff --git a/modules/libcom/src/env/bldEnvData.pl b/modules/libcom/src/env/bldEnvData.pl
index f638c841f..180d63870 100644
--- a/modules/libcom/src/env/bldEnvData.pl
+++ b/modules/libcom/src/env/bldEnvData.pl
@@ -14,7 +14,7 @@
use strict;
use FindBin qw($Bin);
-use lib ($Bin, "$Bin/../../lib/perl");
+use lib ("$Bin/../../lib/perl");
no lib $Bin;
use Getopt::Std;
diff --git a/src/template/base/makeBaseApp.pl b/src/template/base/makeBaseApp.pl
index d6da8adf8..0421f004b 100644
--- a/src/template/base/makeBaseApp.pl
+++ b/src/template/base/makeBaseApp.pl
@@ -3,7 +3,7 @@
# Authors: Ralph Lange, Marty Kraimer, Andrew Johnson and Janet Anderson
use FindBin qw($Bin);
-use lib ("$Bin/../../lib/perl", $Bin);
+use lib ("$Bin/../../lib/perl");
use Cwd;
use Getopt::Std;
diff --git a/src/tools/convertRelease.pl b/src/tools/convertRelease.pl
index a5a1a77d8..253378422 100644
--- a/src/tools/convertRelease.pl
+++ b/src/tools/convertRelease.pl
@@ -19,7 +19,7 @@ use Getopt::Std;
$Getopt::Std::STANDARD_HELP_VERSION = 1;
use FindBin qw($Bin);
-use lib ("$Bin/../../lib/perl", $Bin);
+use lib ("$Bin/../../lib/perl");
use EPICS::Path;
use EPICS::Release;
diff --git a/src/tools/expandVars.pl b/src/tools/expandVars.pl
index 01e65fb3a..855aca329 100644
--- a/src/tools/expandVars.pl
+++ b/src/tools/expandVars.pl
@@ -10,7 +10,7 @@
use strict;
use FindBin qw($Bin);
-use lib ("$Bin/../../lib/perl", $Bin);
+use lib ("$Bin/../../lib/perl");
use EPICS::Getopts;
use EPICS::Path;
diff --git a/src/tools/fullPathName.pl b/src/tools/fullPathName.pl
index c8ac49a12..20ef6d231 100644
--- a/src/tools/fullPathName.pl
+++ b/src/tools/fullPathName.pl
@@ -17,7 +17,7 @@ use Getopt::Std;
$Getopt::Std::STANDARD_HELP_VERSION = 1;
use FindBin qw($Bin);
-use lib ("$Bin/../../lib/perl", $Bin);
+use lib ("$Bin/../../lib/perl");
use EPICS::Path;
From 834e202671e4c47ac1428e4c4b71a67eb697d553 Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Thu, 3 Jan 2019 20:44:50 -0800
Subject: [PATCH 020/281] missed a TOP
---
modules/database/test/std/link/Makefile | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/modules/database/test/std/link/Makefile b/modules/database/test/std/link/Makefile
index c72301bea..2563fd591 100644
--- a/modules/database/test/std/link/Makefile
+++ b/modules/database/test/std/link/Makefile
@@ -4,7 +4,7 @@
# EPICS BASE is distributed subject to a Software License Agreement found
# in the file LICENSE that is included with this distribution.
#*************************************************************************
-TOP=../../..
+TOP=../../../../..
include $(TOP)/configure/CONFIG
From 3e8f3a1ee9b5b9f6a720b1476f97458023f14575 Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Wed, 6 Mar 2019 16:14:55 -0800
Subject: [PATCH 021/281] configure: Don't hide any perl scripts being run
---
configure/RULES_BUILD | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/configure/RULES_BUILD b/configure/RULES_BUILD
index 2dc9dd501..864d2d9bf 100644
--- a/configure/RULES_BUILD
+++ b/configure/RULES_BUILD
@@ -190,7 +190,7 @@ endif
checkRelease:
+$(CONVERTRELEASE) checkRelease
warnRelease:
- -$(CONVERTRELEASE) checkRelease
+ $(CONVERTRELEASE) checkRelease
noCheckRelease:
ifeq ($(EPICS_HOST_ARCH),$(T_A))
$(info Warning: RELEASE file consistency checks have been disabled)
@@ -346,7 +346,7 @@ $(MODNAME): %$(MODEXT): %$(EXE)
runtests: $(TESTSCRIPTS)
ifdef RUNTESTS_ENABLED
- -$(PERL) -MTest::Harness -e 'runtests @ARGV if @ARGV;' $^
+ $(PERL) -MTest::Harness -e 'runtests @ARGV if @ARGV;' $^
endif
testspec: $(TESTSCRIPTS)
@@ -381,7 +381,7 @@ junitfiles: $(JUNITFILES)
# A .tap file is the output from running the associated test script
%.tap: %.t
ifdef RUNTESTS_ENABLED
- -$(PERL) $< -tap > $@
+ $(PERL) $< -tap > $@
endif
%.xml: %.tap
From 0e2d0cde5fea48168faf3b4ce1f6d3fff18889ec Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Wed, 6 Mar 2019 16:50:47 -0800
Subject: [PATCH 022/281] skip RELEASE_TOPS
---
configure/CONFIG | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/configure/CONFIG b/configure/CONFIG
index e40d3f5f7..153e69e8c 100644
--- a/configure/CONFIG
+++ b/configure/CONFIG
@@ -58,8 +58,12 @@ include $(CONFIG)/CONFIG_BASE_VERSION
include $(CONFIG)/os/CONFIG.$(EPICS_HOST_ARCH).Common
-include $(CONFIG)/os/CONFIG_SITE.$(EPICS_HOST_ARCH).Common
+# Parse configure/RELEASE
+# except when building Base itself, where this file is empty,
+# and would error in src/tools/ anyway.
+ifndef BASE_TOP
RELEASE_TOPS := $(shell $(CONVERTRELEASE) -T $(TOP) releaseTops)
-
+endif
ifdef T_A
From 2e80a97da9dd6384ad6992f711569d3b72e298df Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Thu, 2 May 2019 09:25:19 -0700
Subject: [PATCH 023/281] iocsh catch exceptions
---
modules/libcom/src/iocsh/iocsh.cpp | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/modules/libcom/src/iocsh/iocsh.cpp b/modules/libcom/src/iocsh/iocsh.cpp
index 0de90c87a..23e363ad4 100644
--- a/modules/libcom/src/iocsh/iocsh.cpp
+++ b/modules/libcom/src/iocsh/iocsh.cpp
@@ -11,6 +11,8 @@
/* Heavily modified by Eric Norum Date: 03MAY2000 */
/* Adapted to C++ by Eric Norum Date: 18DEC2000 */
+#include
+
#include
#include
#include
@@ -834,7 +836,14 @@ iocshBody (const char *pathname, const char *commandLine, const char *macros)
for (int iarg = 0 ; ; ) {
if (iarg == piocshFuncDef->nargs) {
startRedirect(filename, lineno, redirects);
- (*found->def.func)(argBuf);
+ /* execute */
+ try {
+ (*found->def.func)(argBuf);
+ } catch(std::exception& e){
+ fprintf(epicsGetStderr(), "c++ error: %s\n", e.what());
+ } catch(...) {
+ fprintf(epicsGetStderr(), "c++ error unknown\n");
+ }
break;
}
if (iarg >= argBufCapacity) {
From 89c269e2d5ba5f7775dd557ae919000ba199b9da Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Thu, 2 May 2019 10:38:47 -0700
Subject: [PATCH 024/281] iocsh control error behavior
---
modules/libcom/src/iocsh/iocsh.cpp | 101 +++++++++++++++++++++++++----
1 file changed, 88 insertions(+), 13 deletions(-)
diff --git a/modules/libcom/src/iocsh/iocsh.cpp b/modules/libcom/src/iocsh/iocsh.cpp
index 23e363ad4..57e8be671 100644
--- a/modules/libcom/src/iocsh/iocsh.cpp
+++ b/modules/libcom/src/iocsh/iocsh.cpp
@@ -58,7 +58,7 @@ static char iocshVarID[] = "iocshVar";
extern "C" { static void varCallFunc(const iocshArgBuf *); }
static epicsMutexId iocshTableMutex;
static epicsThreadOnceId iocshOnceId = EPICS_THREAD_ONCE_INIT;
-static epicsThreadPrivateId iocshMacroHandleId;
+static epicsThreadPrivateId iocshScopeId;
/*
* I/O redirection
@@ -78,7 +78,7 @@ struct iocshRedirect {
static void iocshOnce (void *)
{
iocshTableMutex = epicsMutexMustCreate ();
- iocshMacroHandleId = epicsThreadPrivateCreate();
+ iocshScopeId = epicsThreadPrivateCreate();
}
static void iocshInit (void)
@@ -498,6 +498,19 @@ static void helpCallFunc(const iocshArgBuf *args)
}
}
+typedef enum {
+ Continue,
+ Break,
+ Halt
+} OnError;
+
+typedef struct {
+ MAC_HANDLE *handle;
+ OnError onerr;
+ double timeout;
+ bool errored;
+} Scope;
+
/*
* The body of the command interpreter
*/
@@ -526,8 +539,10 @@ iocshBody (const char *pathname, const char *commandLine, const char *macros)
void *readlineContext = NULL;
int wasOkToBlock;
static const char * pairs[] = {"", "environ", NULL, NULL};
+ Scope *scope;
MAC_HANDLE *handle;
char ** defines = NULL;
+ int ret = 0;
iocshInit();
@@ -588,17 +603,20 @@ iocshBody (const char *pathname, const char *commandLine, const char *macros)
/*
* Check for existing macro context or construct a new one.
*/
- handle = (MAC_HANDLE *) epicsThreadPrivateGet(iocshMacroHandleId);
+ scope = (Scope *) epicsThreadPrivateGet(iocshScopeId);
- if (handle == NULL) {
- if (macCreateHandle(&handle, pairs)) {
+ if (!scope) {
+ scope = (Scope*)calloc(1, sizeof(*scope));
+ if (!scope || macCreateHandle(&scope->handle, pairs)) {
errlogMessage("iocsh: macCreateHandle failed.");
free(redirects);
+ free(scope);
return -1;
}
- epicsThreadPrivateSet(iocshMacroHandleId, (void *) handle);
+ epicsThreadPrivateSet(iocshScopeId, (void *) scope);
}
+ handle = scope->handle;
macPushScope(handle);
macInstallMacros(handle, defines);
@@ -837,12 +855,15 @@ iocshBody (const char *pathname, const char *commandLine, const char *macros)
if (iarg == piocshFuncDef->nargs) {
startRedirect(filename, lineno, redirects);
/* execute */
+ scope->errored = false;
try {
(*found->def.func)(argBuf);
} catch(std::exception& e){
fprintf(epicsGetStderr(), "c++ error: %s\n", e.what());
+ scope->errored = true;
} catch(...) {
fprintf(epicsGetStderr(), "c++ error unknown\n");
+ scope->errored = true;
}
break;
}
@@ -877,15 +898,33 @@ iocshBody (const char *pathname, const char *commandLine, const char *macros)
}
else {
showError(filename, lineno, "Command %s not found.", argv[0]);
+ scope->errored = true;
}
}
stopRedirect(filename, lineno, redirects);
+ if(!commandLine && !scope->errored) {
+ if(scope->onerr==Continue) {
+ } else if(scope->onerr==Break) {
+ ret = -1;
+ break;
+ } else if(scope->onerr==Halt) {
+ ret = -1;
+ if(scope->timeout<=0.0) {
+ epicsThreadSuspendSelf();
+ } else {
+ fprintf(epicsGetStderr(), "Wait %f sec\n", scope->timeout);
+ epicsThreadSleep(scope->timeout);
+ }
+ break;
+ }
+ }
}
macPopScope(handle);
if (handle->level == 0) {
macDeleteHandle(handle);
- epicsThreadPrivateSet(iocshMacroHandleId, NULL);
+ free(scope);
+ epicsThreadPrivateSet(iocshScopeId, NULL);
}
if (fp && (fp != stdin))
fclose (fp);
@@ -900,7 +939,7 @@ iocshBody (const char *pathname, const char *commandLine, const char *macros)
if (readlineContext)
epicsReadlineEnd(readlineContext);
epicsThreadSetOkToBlock(wasOkToBlock);
- return 0;
+ return ret;
}
/*
@@ -952,13 +991,13 @@ iocshRun(const char *cmd, const char *macros)
void epicsShareAPI
iocshEnvClear(const char *name)
{
- MAC_HANDLE *handle;
+ Scope *scope;
- if (iocshMacroHandleId) {
- handle = (MAC_HANDLE *) epicsThreadPrivateGet(iocshMacroHandleId);
+ if (iocshScopeId) {
+ scope = (Scope *) epicsThreadPrivateGet(iocshScopeId);
- if (handle != NULL) {
- macPutValue(handle, name, NULL);
+ if (scope != NULL) {
+ macPutValue(scope->handle, name, NULL);
}
}
}
@@ -1063,6 +1102,41 @@ static void iocshRunCallFunc(const iocshArgBuf *args)
iocshRun(args[0].sval, args[1].sval);
}
+/* on */
+static const iocshArg onArg0 = { "...", iocshArgArgv };
+static const iocshArg *onArgs[1] = {&onArg0};
+static const iocshFuncDef onFuncDef = {"on", 1, onArgs};
+static void onCallFunc(const iocshArgBuf *args)
+{
+ Scope *scope = (Scope *) epicsThreadPrivateGet(iocshScopeId);
+
+ if(!scope || args->aval.ac<=2) {
+ } else if(strcmp(args->aval.av[1], "error")==0) {
+ if(args->aval.ac==2) {
+ } else if(strcmp(args->aval.av[2], "continue")==0) {
+ scope->onerr = Continue;
+ return;
+
+ } else if(strcmp(args->aval.av[2], "break")==0) {
+ scope->onerr = Break;
+ return;
+
+ } else if(strcmp(args->aval.av[2], "halt")==0) {
+ scope->onerr = Halt;
+ scope->timeout = 0.0;
+ return;
+
+ } else if(strcmp(args->aval.av[2], "wait")==0) {
+ scope->onerr = Halt;
+ if(args->aval.ac==3 || !epicsParseDouble(args->aval.av[3], &scope->timeout, NULL)) {
+ scope->timeout = 5.0;
+ }
+ return;
+ }
+ }
+ fprintf(epicsGetStderr(), "Invalid 'on'\n");
+}
+
/*
* Dummy internal commands -- register and install in command table
* so they show up in the help display
@@ -1092,6 +1166,7 @@ static void localRegister (void)
iocshRegister(&iocshCmdFuncDef,iocshCmdCallFunc);
iocshRegister(&iocshLoadFuncDef,iocshLoadCallFunc);
iocshRegister(&iocshRunFuncDef,iocshRunCallFunc);
+ iocshRegister(&onFuncDef, onCallFunc);
}
} /* extern "C" */
From eba8a13a2c1ba9fdfdea1380de636fc3c88824be Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Thu, 2 May 2019 20:18:12 -0700
Subject: [PATCH 025/281] iocsh allow setting of error code
---
modules/libcom/src/iocsh/iocsh.cpp | 11 +++++++++++
modules/libcom/src/iocsh/iocsh.h | 2 ++
2 files changed, 13 insertions(+)
diff --git a/modules/libcom/src/iocsh/iocsh.cpp b/modules/libcom/src/iocsh/iocsh.cpp
index 57e8be671..2e64e8b05 100644
--- a/modules/libcom/src/iocsh/iocsh.cpp
+++ b/modules/libcom/src/iocsh/iocsh.cpp
@@ -511,6 +511,17 @@ typedef struct {
bool errored;
} Scope;
+int iocshSetError(int err)
+{
+ Scope *scope;
+ if (err && iocshScopeId) {
+ scope = (Scope *) epicsThreadPrivateGet(iocshScopeId);
+
+ if(scope) scope->errored = 1;
+ }
+ return err;
+}
+
/*
* The body of the command interpreter
*/
diff --git a/modules/libcom/src/iocsh/iocsh.h b/modules/libcom/src/iocsh/iocsh.h
index 84b38f224..2e8dc225e 100644
--- a/modules/libcom/src/iocsh/iocsh.h
+++ b/modules/libcom/src/iocsh/iocsh.h
@@ -89,6 +89,8 @@ epicsShareFunc int epicsShareAPI iocshCmd(const char *cmd);
epicsShareFunc int epicsShareAPI iocshLoad(const char *pathname, const char* macros);
epicsShareFunc int epicsShareAPI iocshRun(const char *cmd, const char* macros);
+epicsShareFunc int iocshSetError(int err);
+
/* Makes macros that shadow environment variables work correctly with epicsEnvSet */
epicsShareFunc void epicsShareAPI iocshEnvClear(const char *name);
From 4d5a677239ecc2f0224c0dcc2bbbc92c5134f1a4 Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Thu, 2 May 2019 20:24:47 -0700
Subject: [PATCH 026/281] use iocshSetError()
---
modules/database/src/ioc/as/asIocRegister.c | 2 +-
modules/database/src/ioc/db/dbIocRegister.c | 4 ++--
.../database/src/ioc/dbtemplate/dbtoolsIocRegister.c | 2 +-
modules/database/src/ioc/misc/miscIocRegister.c | 10 +++++-----
.../database/src/tools/registerRecordDeviceDriver.pl | 2 +-
modules/libcom/src/iocsh/libComRegister.c | 2 +-
6 files changed, 11 insertions(+), 11 deletions(-)
diff --git a/modules/database/src/ioc/as/asIocRegister.c b/modules/database/src/ioc/as/asIocRegister.c
index 16cba90c6..d5926a551 100644
--- a/modules/database/src/ioc/as/asIocRegister.c
+++ b/modules/database/src/ioc/as/asIocRegister.c
@@ -39,7 +39,7 @@ static void asSetSubstitutionsCallFunc(const iocshArgBuf *args)
static const iocshFuncDef asInitFuncDef = {"asInit",0};
static void asInitCallFunc(const iocshArgBuf *args)
{
- asInit();
+ iocshSetError(asInit());
}
/* asdbdump */
diff --git a/modules/database/src/ioc/db/dbIocRegister.c b/modules/database/src/ioc/db/dbIocRegister.c
index c40af92c1..afb31151d 100644
--- a/modules/database/src/ioc/db/dbIocRegister.c
+++ b/modules/database/src/ioc/db/dbIocRegister.c
@@ -39,7 +39,7 @@ static const iocshFuncDef dbLoadDatabaseFuncDef =
{"dbLoadDatabase",3,dbLoadDatabaseArgs};
static void dbLoadDatabaseCallFunc(const iocshArgBuf *args)
{
- dbLoadDatabase(args[0].sval,args[1].sval,args[2].sval);
+ iocshSetError(dbLoadDatabase(args[0].sval,args[1].sval,args[2].sval));
}
/* dbLoadRecords */
@@ -49,7 +49,7 @@ static const iocshArg * const dbLoadRecordsArgs[2] = {&dbLoadRecordsArg0,&dbLoad
static const iocshFuncDef dbLoadRecordsFuncDef = {"dbLoadRecords",2,dbLoadRecordsArgs};
static void dbLoadRecordsCallFunc(const iocshArgBuf *args)
{
- dbLoadRecords(args[0].sval,args[1].sval);
+ iocshSetError(dbLoadRecords(args[0].sval,args[1].sval));
}
/* dbb */
diff --git a/modules/database/src/ioc/dbtemplate/dbtoolsIocRegister.c b/modules/database/src/ioc/dbtemplate/dbtoolsIocRegister.c
index 201a32398..879f67e19 100644
--- a/modules/database/src/ioc/dbtemplate/dbtoolsIocRegister.c
+++ b/modules/database/src/ioc/dbtemplate/dbtoolsIocRegister.c
@@ -22,7 +22,7 @@ static const iocshFuncDef dbLoadTemplateFuncDef =
{"dbLoadTemplate", 2, dbLoadTemplateArgs};
static void dbLoadTemplateCallFunc(const iocshArgBuf *args)
{
- dbLoadTemplate(args[0].sval, args[1].sval);
+ iocshSetError(dbLoadTemplate(args[0].sval, args[1].sval));
}
diff --git a/modules/database/src/ioc/misc/miscIocRegister.c b/modules/database/src/ioc/misc/miscIocRegister.c
index 6c08ef0c9..4dffdfca0 100644
--- a/modules/database/src/ioc/misc/miscIocRegister.c
+++ b/modules/database/src/ioc/misc/miscIocRegister.c
@@ -22,28 +22,28 @@
static const iocshFuncDef iocInitFuncDef = {"iocInit",0,NULL};
static void iocInitCallFunc(const iocshArgBuf *args)
{
- iocInit();
+ iocshSetError(iocInit());
}
/* iocBuild */
static const iocshFuncDef iocBuildFuncDef = {"iocBuild",0,NULL};
static void iocBuildCallFunc(const iocshArgBuf *args)
{
- iocBuild();
+ iocshSetError(iocBuild());
}
/* iocRun */
static const iocshFuncDef iocRunFuncDef = {"iocRun",0,NULL};
static void iocRunCallFunc(const iocshArgBuf *args)
{
- iocRun();
+ iocshSetError(iocRun());
}
/* iocPause */
static const iocshFuncDef iocPauseFuncDef = {"iocPause",0,NULL};
static void iocPauseCallFunc(const iocshArgBuf *args)
{
- iocPause();
+ iocshSetError(iocPause());
}
/* coreRelease */
@@ -77,7 +77,7 @@ static const iocshArg * const systemArgs[] = {&systemArg0};
static const iocshFuncDef systemFuncDef = {"system",1,systemArgs};
static void systemCallFunc(const iocshArgBuf *args)
{
- system(args[0].sval);
+ iocshSetError(system(args[0].sval));
}
#endif
diff --git a/modules/database/src/tools/registerRecordDeviceDriver.pl b/modules/database/src/tools/registerRecordDeviceDriver.pl
index 02bb9b772..10147db4b 100644
--- a/modules/database/src/tools/registerRecordDeviceDriver.pl
+++ b/modules/database/src/tools/registerRecordDeviceDriver.pl
@@ -277,7 +277,7 @@ static const iocshFuncDef rrddFuncDef =
{"$subname", 1, rrddArgs};
static void rrddCallFunc(const iocshArgBuf *)
{
- $subname(*iocshPpdbbase);
+ iocshSetError($subname(*iocshPpdbbase));
}
} // extern "C"
diff --git a/modules/libcom/src/iocsh/libComRegister.c b/modules/libcom/src/iocsh/libComRegister.c
index b105ea12f..11396e984 100644
--- a/modules/libcom/src/iocsh/libComRegister.c
+++ b/modules/libcom/src/iocsh/libComRegister.c
@@ -76,7 +76,7 @@ static const iocshFuncDef chdirFuncDef = {"cd",1,chdirArgs};
static void chdirCallFunc(const iocshArgBuf *args)
{
if (args[0].sval == NULL ||
- chdir(args[0].sval)) {
+ iocshSetError(chdir(args[0].sval))) {
fprintf(stderr, "Invalid directory path, ignored\n");
}
}
From 73cdea5517d625243ba149abf4a1368fbae8fe81 Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Wed, 8 May 2019 19:02:13 -0700
Subject: [PATCH 027/281] as-hostname address review comments
---
documentation/RELEASE_NOTES.html | 22 ++++++++++++++++++++
modules/database/src/ioc/rsrv/camessage.c | 4 ++--
modules/database/src/ioc/rsrv/caservertask.c | 2 +-
modules/libcom/src/as/asLib.h | 2 +-
modules/libcom/src/as/asLibRoutines.c | 4 ++--
modules/libcom/src/iocsh/libComRegister.c | 6 +++---
modules/libcom/test/aslibtest.c | 8 +++----
7 files changed, 35 insertions(+), 13 deletions(-)
diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html
index 63f8880a7..25f0eac62 100644
--- a/documentation/RELEASE_NOTES.html
+++ b/documentation/RELEASE_NOTES.html
@@ -26,6 +26,28 @@ release.
-->
+
ACF Hostname from DNS
+
+
ACF hostname has so far been a string provided by a CA client,
+which may or may not agree with DNS. An option is now available
+to cause IOCs to resolve hostnames, and compare against the actual
+client IP address.
+
This resolution is done at ACF file load time,
+which has two consequences.
+
+
+
Slow/unavailable DNS will cause problems during ACF file
+loading. eg. during IOC start.
+
Changes in host -> IP mapping will not be picked up until/unless
+the ACF file is reloaded.
+
+
+
This may be enabled with:
+
+
+var("asCheckClientIP",1)
+
+
Launchpad Bugs
The list of tracked bugs fixed in this release can be found on the
diff --git a/modules/database/src/ioc/rsrv/camessage.c b/modules/database/src/ioc/rsrv/camessage.c
index 40448d018..f54bb4888 100644
--- a/modules/database/src/ioc/rsrv/camessage.c
+++ b/modules/database/src/ioc/rsrv/camessage.c
@@ -862,9 +862,9 @@ static int host_name_action ( caHdrLargeArray *mp, void *pPayload,
}
/* after all validation */
- if(asUseIP) {
+ if(asCheckClientIP) {
- DLOG (2, ( "CAS: host_name_action for \"%s\" ignores clist provided host name\n",
+ DLOG (2, ( "CAS: host_name_action for \"%s\" ignores client provided host name\n",
client->pHostName ) );
return RSRV_OK;
}
diff --git a/modules/database/src/ioc/rsrv/caservertask.c b/modules/database/src/ioc/rsrv/caservertask.c
index 048487b20..7a9ae63b3 100644
--- a/modules/database/src/ioc/rsrv/caservertask.c
+++ b/modules/database/src/ioc/rsrv/caservertask.c
@@ -1421,7 +1421,7 @@ struct client *create_tcp_client (SOCKET sock , const osiSockAddr *peerAddr)
}
client->addr = peerAddr->ia;
- if(asUseIP) {
+ if(asCheckClientIP) {
epicsUInt32 ip = ntohl(client->addr.sin_addr.s_addr);
client->pHostName = malloc(24);
if(!client->pHostName) {
diff --git a/modules/libcom/src/as/asLib.h b/modules/libcom/src/as/asLib.h
index b4e5139ce..a29cf3b65 100644
--- a/modules/libcom/src/as/asLib.h
+++ b/modules/libcom/src/as/asLib.h
@@ -24,7 +24,7 @@ extern "C" {
/* 0 - Use (unverified) client provided host name string.
* 1 - Use actual client IP address. HAG() are resolved to IPs at ACF load time.
*/
-epicsShareExtern int asUseIP;
+epicsShareExtern int asCheckClientIP;
typedef struct asgMember *ASMEMBERPVT;
typedef struct asgClient *ASCLIENTPVT;
diff --git a/modules/libcom/src/as/asLibRoutines.c b/modules/libcom/src/as/asLibRoutines.c
index ceade030e..e3105facd 100644
--- a/modules/libcom/src/as/asLibRoutines.c
+++ b/modules/libcom/src/as/asLibRoutines.c
@@ -29,7 +29,7 @@
#include "postfix.h"
#include "asLib.h"
-int asUseIP;
+int asCheckClientIP;
static epicsMutexId asLock;
#define LOCK epicsMutexMustLock(asLock)
@@ -1210,7 +1210,7 @@ static long asHagAddHost(HAG *phag,const char *host)
int len, i;
if (!phag) return 0;
- if(!asUseIP) {
+ if(!asCheckClientIP) {
len = strlen(host);
phagname = asCalloc(1, sizeof(HAGNAME) + len + 1);
phagname->host = (char *)(phagname + 1);
diff --git a/modules/libcom/src/iocsh/libComRegister.c b/modules/libcom/src/iocsh/libComRegister.c
index 2bbb09f3e..c842dce27 100644
--- a/modules/libcom/src/iocsh/libComRegister.c
+++ b/modules/libcom/src/iocsh/libComRegister.c
@@ -393,7 +393,7 @@ static void installLastResortEventProviderCallFunc(const iocshArgBuf *args)
installLastResortEventProvider();
}
-static iocshVarDef asUseIPDef = {"asUseIP", iocshArgInt, 0};
+static iocshVarDef asCheckClientIPDef = {"asCheckClientIP", iocshArgInt, 0};
void epicsShareAPI libComRegister(void)
{
@@ -428,6 +428,6 @@ void epicsShareAPI libComRegister(void)
iocshRegister(&generalTimeReportFuncDef,generalTimeReportCallFunc);
iocshRegister(&installLastResortEventProviderFuncDef, installLastResortEventProviderCallFunc);
- asUseIPDef.pval = &asUseIP;
- iocshRegisterVariable(&asUseIPDef);
+ asCheckClientIPDef.pval = &asCheckClientIP;
+ iocshRegisterVariable(&asCheckClientIPDef);
}
diff --git a/modules/libcom/test/aslibtest.c b/modules/libcom/test/aslibtest.c
index 367a12426..4237fafb1 100644
--- a/modules/libcom/test/aslibtest.c
+++ b/modules/libcom/test/aslibtest.c
@@ -82,7 +82,7 @@ static const char hostname_config[] = ""
static void testHostNames(void)
{
testDiag("testHostNames()");
- asUseIP = 0;
+ asCheckClientIP = 0;
testOk1(asInitMem(hostname_config, NULL)==0);
@@ -102,7 +102,7 @@ static void testHostNames(void)
testAccess("ro", 0);
testAccess("rw", 0);
- setHost("nosuchhost");
+ setHost("guaranteed.invalid.");
testAccess("invalid", 0);
testAccess("DEFAULT", 0);
@@ -113,7 +113,7 @@ static void testHostNames(void)
static void testUseIP(void)
{
testDiag("testUseIP()");
- asUseIP = 1;
+ asCheckClientIP = 1;
/* still host names in .acf */
testOk1(asInitMem(hostname_config, NULL)==0);
@@ -135,7 +135,7 @@ static void testUseIP(void)
testAccess("ro", 1);
testAccess("rw", 3);
- setHost("nosuchhost");
+ setHost("guaranteed.invalid.");
testAccess("invalid", 0);
testAccess("DEFAULT", 0);
From 251304e280c023ed72031d25981d6db4bd0b73d4 Mon Sep 17 00:00:00 2001
From: Dirk Zimoch
Date: Mon, 13 May 2019 09:43:44 +0200
Subject: [PATCH 028/281] use dynamic binding of vxWorks BSP functions because
some BSPs don't provide them
---
.../libcom/src/osi/os/vxWorks/devLibVMEOSD.c | 48 ++++++++++++-------
1 file changed, 31 insertions(+), 17 deletions(-)
diff --git a/modules/libcom/src/osi/os/vxWorks/devLibVMEOSD.c b/modules/libcom/src/osi/os/vxWorks/devLibVMEOSD.c
index 82bec603d..93d30fc38 100644
--- a/modules/libcom/src/osi/os/vxWorks/devLibVMEOSD.c
+++ b/modules/libcom/src/osi/os/vxWorks/devLibVMEOSD.c
@@ -110,7 +110,21 @@ static long vxDevWriteProbe (unsigned wordSize, volatile void *ptr, const void *
static void *devA24Malloc(size_t size);
static void devA24Free(void *pBlock);
-static long devInit(void) { return 0;}
+
+/* We don't know which functions are implemented in the BSP */
+static int (*sysIntEnableFunc)(int) = NULL;
+static int (*sysIntDisableFunc)(int) = NULL;
+static int (*sysIntEnablePICFunc)(int) = NULL;
+static int (*sysIntDisablePICFunc)(int) = NULL;
+
+static long devInit(void)
+{
+ sysIntEnableFunc = epicsFindSymbol ("sysIntEnable");
+ sysIntDisableFunc = epicsFindSymbol ("sysIntDisable");
+ sysIntDisablePICFunc = epicsFindSymbol ("sysIntDisablePIC");
+ sysIntEnablePICFunc = epicsFindSymbol ("sysIntEnablePIC");
+ return 0;
+}
static long vxDevConnectInterruptVME (
unsigned vectorNumber,
@@ -214,16 +228,16 @@ static long vxDevDisconnectInterruptVME (
*/
static long vxDevEnableInterruptLevelVME (unsigned level)
{
-# if CPU_FAMILY != I80X86
+ if (sysIntEnableFunc) {
int s;
- s = sysIntEnable (level);
+ s = sysIntEnableFunc (level);
if (s!=OK) {
return S_dev_intEnFail;
}
return 0;
-# else
+ } else {
return S_dev_intEnFail;
-# endif
+ }
}
/*
@@ -231,16 +245,16 @@ static long vxDevEnableInterruptLevelVME (unsigned level)
*/
long devEnableInterruptLevelISA (unsigned level)
{
-# if CPU_FAMILY == I80X86
+ if (sysIntEnablePICFunc) {
int s;
- s = sysIntEnablePIC (level);
+ s = sysIntEnablePICFunc (level);
if (s!=OK) {
return S_dev_intEnFail;
}
return 0;
-# else
+ } else {
return S_dev_intEnFail;
-# endif
+ }
}
/*
@@ -248,15 +262,15 @@ long devEnableInterruptLevelISA (unsigned level)
*/
long devDisableInterruptLevelISA (unsigned level)
{
-# if CPU_FAMILY == I80X86
+ if (sysIntDisablePICFunc) {
int s;
- s = sysIntDisablePIC (level);
+ s = sysIntDisablePICFunc (level);
if (s!=OK) {
return S_dev_intEnFail;
}
-# else
+ } else {
return S_dev_intEnFail;
-# endif
+ }
return 0;
}
@@ -266,16 +280,16 @@ long devDisableInterruptLevelISA (unsigned level)
*/
static long vxDevDisableInterruptLevelVME (unsigned level)
{
-# if CPU_FAMILY != I80X86
+ if (sysIntDisableFunc) {
int s;
- s = sysIntDisable (level);
+ s = sysIntDisableFunc (level);
if (s!=OK) {
return S_dev_intDissFail;
}
return 0;
-# else
+ } else {
return S_dev_intEnFail;
-# endif
+ }
}
/*
From 6eb6cc0d805e3ccab87963895b9a94f1c4485035 Mon Sep 17 00:00:00 2001
From: Andrew Johnson
Date: Mon, 13 May 2019 14:52:10 -0500
Subject: [PATCH 029/281] Expand Release Note entry for as-hostname changes.
---
documentation/RELEASE_NOTES.html | 46 ++++++++++++++++++++++----------
1 file changed, 32 insertions(+), 14 deletions(-)
diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html
index 25f0eac62..376279fc9 100644
--- a/documentation/RELEASE_NOTES.html
+++ b/documentation/RELEASE_NOTES.html
@@ -26,28 +26,46 @@ release.
-->
-
ACF Hostname from DNS
+
Channel Access Security: Check Hostname Against DNS
-
ACF hostname has so far been a string provided by a CA client,
-which may or may not agree with DNS. An option is now available
-to cause IOCs to resolve hostnames, and compare against the actual
-client IP address.
-
This resolution is done at ACF file load time,
-which has two consequences.
+
Host names given in a HAG entry of an IOC's Access Security
+Configuration File (ACF) have to date been compared against the hostname
+provided by the CA client at connection time, which may or may not be the actual
+name of that client. This allows rogue clients to pretend to be a different
+host, and the IOC would believe them.
-
-
Slow/unavailable DNS will cause problems during ACF file
-loading. eg. during IOC start.
-
Changes in host -> IP mapping will not be picked up until/unless
-the ACF file is reloaded.
-
+
An option is now available to cause an IOC to ask its operating system to
+look up the IP address of any hostnames listed in its ACF (which will normally
+be done using the DNS or the /etc/hosts file). The IOC will then
+compare the resulting IP address against the client's actual IP address when
+checking access permissions at connection time. This name resolution gets done
+at ACF file load time, which has a few consequences:
-
This may be enabled with:
+
+
+
If the DNS is slow when the names are resolved this will delay the process
+of loading the ACF file.
+
+
If a host name cannot be resolved the IOC will treat the ACF as invalid,
+which prevents any CA clients from connecting.
+
+
Any changes in the hostname to IP address mapping will not be picked up by
+the IOC unless and until the ACF file gets reloaded.
A new server-side filter has been added to the IOC for reducing the number
+and frequency of monitor updates from a channel by a client-specified factor.
+The filter's behaviour is quite simplistic, it passes the first monitor event it
+sees to the client and then drops the next N-1 events before passing another
+event. For example to sample a 60Hz channel at 1Hz, a 10Hz channel every 6
+seconds, or a 1Hz channel once every minute:
Cleaning up with Multiple CA contexts in a Process
Bruno Martins reported a problem with the CA client library at shutdown in a
diff --git a/src/std/filters/Makefile b/src/std/filters/Makefile
index d4539898f..3a27361f6 100644
--- a/src/std/filters/Makefile
+++ b/src/std/filters/Makefile
@@ -15,6 +15,7 @@ dbRecStd_SRCS += ts.c
dbRecStd_SRCS += dbnd.c
dbRecStd_SRCS += arr.c
dbRecStd_SRCS += sync.c
+dbRecStd_SRCS += decimate.c
HTMLS += filters.html
diff --git a/src/std/filters/decimate.c b/src/std/filters/decimate.c
new file mode 100644
index 000000000..d34884ebe
--- /dev/null
+++ b/src/std/filters/decimate.c
@@ -0,0 +1,115 @@
+/*************************************************************************\
+* Copyright (c) 2019 UChicago Argonne LLC, as Operator of Argonne
+* National Laboratory.
+* Copyright (c) 2010 Brookhaven National Laboratory.
+* Copyright (c) 2010 Helmholtz-Zentrum Berlin
+* fuer Materialien und Energie GmbH.
+* EPICS BASE is distributed subject to a Software License Agreement found
+* in file LICENSE that is included with this distribution.
+\*************************************************************************/
+
+/*
+ * Authors: Ralph Lange ,
+ * Andrew Johnson
+ */
+
+#include
+
+#include "freeList.h"
+#include "db_field_log.h"
+#include "chfPlugin.h"
+#include "epicsExport.h"
+
+typedef struct myStruct {
+ epicsInt32 n, i;
+} myStruct;
+
+static void *myStructFreeList;
+
+static const
+chfPluginArgDef opts[] = {
+ chfInt32(myStruct, n, "n", 1, 0),
+ chfPluginArgEnd
+};
+
+static void * allocPvt(void)
+{
+ myStruct *my = (myStruct*) freeListCalloc(myStructFreeList);
+ return (void *) my;
+}
+
+static void freePvt(void *pvt)
+{
+ freeListFree(myStructFreeList, pvt);
+}
+
+static int parse_ok(void *pvt)
+{
+ myStruct *my = (myStruct*) pvt;
+
+ if (my->n < 1)
+ return -1;
+
+ return 0;
+}
+
+static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) {
+ db_field_log *passfl = NULL;
+ myStruct *my = (myStruct*) pvt;
+ epicsInt32 i = my->i;
+
+ if (pfl->ctx == dbfl_context_read)
+ return pfl;
+
+ if (i++ == 0)
+ passfl = pfl;
+
+ if (i >= my->n)
+ i = 0;
+
+ my->i = i;
+ return passfl;
+}
+
+static void channelRegisterPre(dbChannel *chan, void *pvt,
+ chPostEventFunc **cb_out, void **arg_out, db_field_log *probe)
+{
+ *cb_out = filter;
+ *arg_out = pvt;
+}
+
+static void channel_report(dbChannel *chan, void *pvt, int level, const unsigned short indent)
+{
+ myStruct *my = (myStruct*) pvt;
+ printf("%*sDecimate (dec): n=%d, i=%d\n", indent, "",
+ my->n, my->i);
+}
+
+static chfPluginIf pif = {
+ allocPvt,
+ freePvt,
+
+ NULL, /* parse_error, */
+ parse_ok,
+
+ NULL, /* channel_open, */
+ channelRegisterPre,
+ NULL, /* channelRegisterPost, */
+ channel_report,
+ NULL /* channel_close */
+};
+
+static void decInitialize(void)
+{
+ static int firstTime = 1;
+
+ if (!firstTime) return;
+ firstTime = 0;
+
+ if (!myStructFreeList)
+ freeListInitPvt(&myStructFreeList, sizeof(myStruct), 64);
+
+ chfPluginRegister("dec", &pif, opts);
+}
+
+epicsExportRegistrar(decInitialize);
diff --git a/src/std/filters/filters.dbd.pod b/src/std/filters/filters.dbd.pod
index f1a848469..d352d0e5c 100644
--- a/src/std/filters/filters.dbd.pod
+++ b/src/std/filters/filters.dbd.pod
@@ -14,6 +14,8 @@ The following filters are available in this release:
=item * L
+=item * L
+
=back
=head2 Using Filters
@@ -245,3 +247,41 @@ periods only when "blue" is true by using
...
=cut
+
+registrar(decInitialize)
+
+=head3 Decimation Filter C<"dec">
+
+This filter is used to reduce the number or rate of monitor updates from a
+channel by an integer factor C that is provided as a filter argument,
+discarding the other updates. A true decimation following the original meaning
+of the word would be achieved by giving C as 10, to only allow every tenth
+update through.
+
+=head4 Parameters
+
+=over
+
+=item Number C<"n">
+
+The decimation factor, a positive integer. Giving n=1 is equivalent to a no-op
+that allows all updates to be passed to the client.
+
+=back
+
+This filter is intentionally very simplistic. It passes on the first monitor
+event that it sees after the channel connects, then discards the next N-1 events
+before sending the next event. If several clients connect to a channel using the
+same filter settings they may see completely different data streams since each
+client gets its own instance of the filter whose event counter starts when that
+client connects.
+
+=head4 Example
+
+To sample a 60Hz channel at 1Hz, a 10Hz channel every 6 seconds or a 1Hz channel
+once every minute:
+
+ Hal$ camonitor 'test:channel' 'test:channel.{"dec":{"n":60}}'
+ ...
+
+=cut
diff --git a/src/std/filters/test/Makefile b/src/std/filters/test/Makefile
index 6e6ad79c6..ad77c5d11 100644
--- a/src/std/filters/test/Makefile
+++ b/src/std/filters/test/Makefile
@@ -56,6 +56,12 @@ syncTest_SRCS += filterTest_registerRecordDeviceDriver.cpp
testHarness_SRCS += syncTest.c
TESTS += syncTest
+TESTPROD_HOST += decTest
+decTest_SRCS += decTest.c
+decTest_SRCS += filterTest_registerRecordDeviceDriver.cpp
+testHarness_SRCS += decTest.c
+TESTS += decTest
+
# epicsRunFilterTests runs all the test programs in a known working order.
testHarness_SRCS += epicsRunFilterTests.c
diff --git a/src/std/filters/test/decTest.c b/src/std/filters/test/decTest.c
new file mode 100644
index 000000000..cc9317eb4
--- /dev/null
+++ b/src/std/filters/test/decTest.c
@@ -0,0 +1,273 @@
+/*************************************************************************\
+* Copyright (c) 2019 UChicago Argonne LLC, as Operator of Argonne
+* National Laboratory.
+* Copyright (c) 2010 Brookhaven National Laboratory.
+* Copyright (c) 2010 Helmholtz-Zentrum Berlin
+* fuer Materialien und Energie GmbH.
+* EPICS BASE is distributed subject to a Software License Agreement found
+* in file LICENSE that is included with this distribution.
+\*************************************************************************/
+
+/*
+ * Authors: Ralph Lange ,
+ * Andrew Johnson
+ */
+
+#include
+
+#include "dbStaticLib.h"
+#include "dbAccessDefs.h"
+#include "db_field_log.h"
+#include "dbCommon.h"
+#include "dbChannel.h"
+#include "registry.h"
+#include "chfPlugin.h"
+#include "errlog.h"
+#include "dbmf.h"
+#include "epicsUnitTest.h"
+#include "dbUnitTest.h"
+#include "epicsTime.h"
+#include "testMain.h"
+#include "osiFileName.h"
+
+void filterTest_registerRecordDeviceDriver(struct dbBase *);
+
+static int fl_equal(const db_field_log *pfl1, const db_field_log *pfl2) {
+ return !(memcmp(pfl1, pfl2, sizeof(db_field_log)));
+}
+
+static void fl_setup(dbChannel *chan, db_field_log *pfl, long val) {
+ struct dbCommon *prec = dbChannelRecord(chan);
+
+ pfl->ctx = dbfl_context_event;
+ pfl->type = dbfl_type_val;
+ pfl->stat = prec->stat;
+ pfl->sevr = prec->sevr;
+ pfl->time = prec->time;
+ pfl->field_type = DBF_LONG;
+ pfl->no_elements = 1;
+ /*
+ * use memcpy to avoid a bus error on
+ * union copy of char in the db at an odd
+ * address
+ */
+ memcpy(&pfl->u.v.field,
+ dbChannelField(chan),
+ dbChannelFieldSize(chan));
+ pfl->u.v.field.dbf_long = val;
+}
+
+static void testHead (char* title) {
+ testDiag("--------------------------------------------------------");
+ testDiag("%s", title);
+ testDiag("--------------------------------------------------------");
+}
+
+static void mustDrop(dbChannel *pch, db_field_log *pfl, char* m) {
+ db_field_log *pfl2 = dbChannelRunPreChain(pch, pfl);
+
+ testOk(NULL == pfl2, "filter drops field_log (%s)", m);
+}
+
+static void mustPass(dbChannel *pch, db_field_log *pfl, char* m) {
+ db_field_log *pfl2 = dbChannelRunPreChain(pch, pfl);
+
+ testOk(pfl == pfl2, "filter passes field_log (%s)", m);
+}
+
+static void checkAndOpenChannel(dbChannel *pch, const chFilterPlugin *plug) {
+ ELLNODE *node;
+ chFilter *filter;
+ chPostEventFunc *cb_out = NULL;
+ void *arg_out = NULL;
+ db_field_log fl, fl1;
+
+ testDiag("Test filter structure and open channel");
+
+ testOk((ellCount(&pch->filters) == 1), "channel has one plugin");
+
+ fl_setup(pch, &fl, 1);
+ fl1 = fl;
+ node = ellFirst(&pch->filters);
+ filter = CONTAINER(node, chFilter, list_node);
+ plug->fif->channel_register_pre(filter, &cb_out, &arg_out, &fl1);
+ testOk(cb_out && arg_out,
+ "register_pre registers one filter with argument");
+ testOk(fl_equal(&fl1, &fl),
+ "register_pre does not change field_log data type");
+
+ testOk(!(dbChannelOpen(pch)), "dbChannel with plugin dec opened");
+ node = ellFirst(&pch->pre_chain);
+ filter = CONTAINER(node, chFilter, pre_node);
+ testOk((ellCount(&pch->pre_chain) == 1 && filter->pre_arg != NULL),
+ "dec has one filter with argument in pre chain");
+ testOk((ellCount(&pch->post_chain) == 0),
+ "sync has no filter in post chain");
+}
+
+MAIN(decTest)
+{
+ dbChannel *pch;
+ const chFilterPlugin *plug;
+ char myname[] = "dec";
+ db_field_log *pfl[10];
+ int i;
+ dbEventCtx evtctx;
+
+ testPlan(68);
+
+ testdbPrepare();
+
+ testdbReadDatabase("filterTest.dbd", NULL, NULL);
+
+ filterTest_registerRecordDeviceDriver(pdbbase);
+
+ testdbReadDatabase("xRecord.db", NULL, NULL);
+
+ eltc(0);
+ testIocInitOk();
+ eltc(1);
+
+ evtctx = db_init_events();
+
+ testOk(!!(plug = dbFindFilter(myname, strlen(myname))),
+ "plugin '%s' registered correctly", myname);
+
+ /* N < 1 */
+ testOk(!(pch = dbChannelCreate("x.VAL{\"dec\":{\"n\":-1}}")),
+ "dbChannel with dec (n=-1) failed");
+ testOk(!(pch = dbChannelCreate("x.VAL{\"dec\":{\"n\":0}}")),
+ "dbChannel with dec (n=0) failed");
+ /* Bad parms */
+ testOk(!(pch = dbChannelCreate("x.VAL{\"dec\":{}}")),
+ "dbChannel with dec (no parm) failed");
+ testOk(!(pch = dbChannelCreate("x.VAL{\"dec\":{\"x\":true}}")),
+ "dbChannel with dec (x=true) failed");
+
+ /* No Decimation (N=1) */
+
+ testHead("No Decimation (n=1)");
+ testOk(!!(pch = dbChannelCreate("x.VAL{\"dec\":{\"n\":1}}")),
+ "dbChannel with plugin dec (n=1) created");
+
+ checkAndOpenChannel(pch, plug);
+
+ for (i = 0; i < 5; i++) {
+ pfl[i] = db_create_read_log(pch);
+ fl_setup(pch, pfl[i], 10 + i);
+ }
+
+ testDiag("Test event stream");
+
+ mustPass(pch, pfl[0], "i=0");
+ mustPass(pch, pfl[1], "i=1");
+ mustPass(pch, pfl[2], "i=2");
+ mustPass(pch, pfl[3], "i=3");
+ mustPass(pch, pfl[4], "i=4");
+
+ for (i = 0; i < 5; i++)
+ db_delete_field_log(pfl[i]);
+
+ dbChannelDelete(pch);
+
+ /* Decimation (N=2) */
+
+ testHead("Decimation (n=2)");
+ testOk(!!(pch = dbChannelCreate("x.VAL{\"dec\":{\"n\":2}}")),
+ "dbChannel with plugin dec (n=2) created");
+
+ checkAndOpenChannel(pch, plug);
+
+ for (i = 0; i < 10; i++) {
+ pfl[i] = db_create_read_log(pch);
+ fl_setup(pch, pfl[i], 20 + i);
+ }
+
+ testDiag("Test event stream");
+
+ mustPass(pch, pfl[0], "i=0");
+ mustDrop(pch, pfl[1], "i=1");
+ mustPass(pch, pfl[2], "i=2");
+ mustDrop(pch, pfl[3], "i=3");
+ mustPass(pch, pfl[4], "i=4");
+ mustDrop(pch, pfl[5], "i=5");
+ mustPass(pch, pfl[6], "i=6");
+ mustDrop(pch, pfl[7], "i=7");
+ mustPass(pch, pfl[8], "i=8");
+ mustDrop(pch, pfl[9], "i=9");
+
+ for (i = 0; i < 10; i++)
+ db_delete_field_log(pfl[i]);
+
+ dbChannelDelete(pch);
+
+ /* Decimation (N=3) */
+
+ testHead("Decimation (n=3)");
+ testOk(!!(pch = dbChannelCreate("x.VAL{\"dec\":{\"n\":3}}")),
+ "dbChannel with plugin dec (n=3) created");
+
+ checkAndOpenChannel(pch, plug);
+
+ for (i = 0; i < 10; i++) {
+ pfl[i] = db_create_read_log(pch);
+ fl_setup(pch, pfl[i], 30 + i);
+ }
+
+ testDiag("Test event stream");
+
+ mustPass(pch, pfl[0], "i=0");
+ mustDrop(pch, pfl[1], "i=1");
+ mustDrop(pch, pfl[2], "i=2");
+ mustPass(pch, pfl[3], "i=3");
+ mustDrop(pch, pfl[4], "i=4");
+ mustDrop(pch, pfl[5], "i=5");
+ mustPass(pch, pfl[6], "i=6");
+ mustDrop(pch, pfl[7], "i=7");
+ mustDrop(pch, pfl[8], "i=8");
+ mustPass(pch, pfl[9], "i=9");
+
+ for (i = 0; i < 10; i++)
+ db_delete_field_log(pfl[i]);
+
+ dbChannelDelete(pch);
+
+ /* Decimation (N=4) */
+
+ testHead("Decimation (n=4)");
+ testOk(!!(pch = dbChannelCreate("x.VAL{\"dec\":{\"n\":4}}")),
+ "dbChannel with plugin dec (n=4) created");
+
+ checkAndOpenChannel(pch, plug);
+
+ for (i = 0; i < 10; i++) {
+ pfl[i] = db_create_read_log(pch);
+ fl_setup(pch, pfl[i], 40 + i);
+ }
+
+ testDiag("Test event stream");
+
+ mustPass(pch, pfl[0], "i=0");
+ mustDrop(pch, pfl[1], "i=1");
+ mustDrop(pch, pfl[2], "i=2");
+ mustDrop(pch, pfl[3], "i=3");
+ mustPass(pch, pfl[4], "i=4");
+ mustDrop(pch, pfl[5], "i=5");
+ mustDrop(pch, pfl[6], "i=6");
+ mustDrop(pch, pfl[7], "i=7");
+ mustPass(pch, pfl[8], "i=8");
+ mustDrop(pch, pfl[9], "i=9");
+
+ for (i = 0; i < 10; i++)
+ db_delete_field_log(pfl[i]);
+
+ dbChannelDelete(pch);
+
+ db_close_events(evtctx);
+
+ testIocShutdownOk();
+
+ testdbCleanup();
+
+ return testDone();
+}
diff --git a/src/std/filters/test/epicsRunFilterTests.c b/src/std/filters/test/epicsRunFilterTests.c
index 236364391..5737d77dc 100644
--- a/src/std/filters/test/epicsRunFilterTests.c
+++ b/src/std/filters/test/epicsRunFilterTests.c
@@ -17,6 +17,7 @@ int tsTest(void);
int dbndTest(void);
int syncTest(void);
int arrTest(void);
+int decTest(void);
void epicsRunFilterTests(void)
{
@@ -26,6 +27,7 @@ void epicsRunFilterTests(void)
runTest(dbndTest);
runTest(syncTest);
runTest(arrTest);
+ runTest(decTest);
dbmfFreeChunks();
From 048975ccc708987afdda43712485ff9dc24b0b29 Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Wed, 5 Jun 2019 11:40:50 +0200
Subject: [PATCH 037/281] asLib more string size...
---
modules/libcom/src/as/asLib.h | 4 ++--
modules/libcom/src/as/asLibRoutines.c | 6 ++----
2 files changed, 4 insertions(+), 6 deletions(-)
diff --git a/modules/libcom/src/as/asLib.h b/modules/libcom/src/as/asLib.h
index a29cf3b65..528ce6ed9 100644
--- a/modules/libcom/src/as/asLib.h
+++ b/modules/libcom/src/as/asLib.h
@@ -170,8 +170,8 @@ typedef struct uag{
} UAG;
/*Defs for Host Access Groups*/
typedef struct{
- ELLNODE node;
- char *host;
+ ELLNODE node;
+ char host[1];
} HAGNAME;
typedef struct hag{
ELLNODE node;
diff --git a/modules/libcom/src/as/asLibRoutines.c b/modules/libcom/src/as/asLibRoutines.c
index 4ca1916bd..ab0bf5071 100644
--- a/modules/libcom/src/as/asLibRoutines.c
+++ b/modules/libcom/src/as/asLibRoutines.c
@@ -1211,8 +1211,7 @@ static long asHagAddHost(HAG *phag,const char *host)
if (!phag) return 0;
if(!asCheckClientIP) {
size_t i, len = strlen(host);
- phagname = asCalloc(1, sizeof(HAGNAME) + len + 1);
- phagname->host = (char *)(phagname + 1);
+ phagname = asCalloc(1, sizeof(HAGNAME) + len);
for (i = 0; i < len; i++) {
phagname->host[i] = (char)tolower((int)host[i]);
}
@@ -1226,14 +1225,13 @@ static long asHagAddHost(HAG *phag,const char *host)
errlogPrintf("ACF: Unable to resolve host '%s'\n", host);
- phagname = asCalloc(1, sizeof(HAGNAME) + sizeof(unresolved)+strlen(host));
+ phagname = asCalloc(1, sizeof(HAGNAME) + sizeof(unresolved)-1+strlen(host));
strcpy(phagname->host, unresolved);
strcat(phagname->host, host);
} else {
ip = ntohl(addr.sin_addr.s_addr);
phagname = asCalloc(1, sizeof(HAGNAME) + 24);
- phagname->host = (char *)(phagname + 1);
epicsSnprintf(phagname->host, 24,
"%u.%u.%u.%u",
(ip>>24)&0xff,
From 5cfff383b265ab99448ce44d39b0e865bf9bdd4d Mon Sep 17 00:00:00 2001
From: Andrew Johnson
Date: Fri, 7 Jun 2019 13:16:42 -0500
Subject: [PATCH 038/281] Synchronization hook support for osiClockTime
---
src/libCom/osi/osiClockTime.c | 7 +++++++
src/libCom/osi/osiClockTime.h | 6 ++++++
2 files changed, 13 insertions(+)
diff --git a/src/libCom/osi/osiClockTime.c b/src/libCom/osi/osiClockTime.c
index fb9d1532f..8ae43aed8 100644
--- a/src/libCom/osi/osiClockTime.c
+++ b/src/libCom/osi/osiClockTime.c
@@ -148,6 +148,8 @@ void ClockTime_GetProgramStart(epicsTimeStamp *pDest)
/* Synchronization thread */
#if defined(vxWorks) || defined(__rtems__)
+CLOCKTIME_SYNCHOOK ClockTime_syncHook = NULL;
+
static void ClockTimeSync(void *dummy)
{
taskwdInsert(0, NULL, NULL);
@@ -179,11 +181,16 @@ static void ClockTimeSync(void *dummy)
ClockTimePvt.syncTime = timeNow;
epicsMutexUnlock(ClockTimePvt.lock);
+ if (ClockTime_syncHook)
+ ClockTime_syncHook(1);
+
ClockTimePvt.ClockTimeSyncInterval = ClockTimeSyncInterval_value;
}
}
ClockTimePvt.synchronized = 0;
+ if (ClockTime_syncHook)
+ ClockTime_syncHook(0);
taskwdRemove(0);
}
#endif
diff --git a/src/libCom/osi/osiClockTime.h b/src/libCom/osi/osiClockTime.h
index 17eacab3e..23598886d 100644
--- a/src/libCom/osi/osiClockTime.h
+++ b/src/libCom/osi/osiClockTime.h
@@ -19,6 +19,12 @@ void ClockTime_Init(int synchronize);
void ClockTime_Shutdown(void *dummy);
int ClockTime_Report(int level);
+#if defined(vxWorks) || defined(__rtems__)
+typedef void (* CLOCKTIME_SYNCHOOK)(int synchronized);
+
+extern CLOCKTIME_SYNCHOOK ClockTime_syncHook;
+#endif
+
#ifdef __cplusplus
}
#endif
From 30812c23f017cc20daf15ed72364648af04c2ce9 Mon Sep 17 00:00:00 2001
From: Andrew Johnson
Date: Fri, 7 Jun 2019 13:17:35 -0500
Subject: [PATCH 039/281] Internal cleanup in osiClockTime.c
---
src/libCom/osi/osiClockTime.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/src/libCom/osi/osiClockTime.c b/src/libCom/osi/osiClockTime.c
index 8ae43aed8..01958b20e 100644
--- a/src/libCom/osi/osiClockTime.c
+++ b/src/libCom/osi/osiClockTime.c
@@ -23,7 +23,8 @@
#include "taskwd.h"
#define NSEC_PER_SEC 1000000000
-#define ClockTimeSyncInterval_value 60.0
+#define ClockTimeSyncInterval_initial 1.0
+#define ClockTimeSyncInterval_normal 60.0
static struct {
@@ -79,7 +80,7 @@ static void ClockTime_InitOnce(void *pfirst)
ClockTimePvt.loopEvent = epicsEventMustCreate(epicsEventEmpty);
ClockTimePvt.lock = epicsMutexCreate();
- ClockTimePvt.ClockTimeSyncInterval = 1.0; /* First sync */
+ ClockTimePvt.ClockTimeSyncInterval = ClockTimeSyncInterval_initial;
epicsAtExit(ClockTime_Shutdown, NULL);
@@ -184,7 +185,7 @@ static void ClockTimeSync(void *dummy)
if (ClockTime_syncHook)
ClockTime_syncHook(1);
- ClockTimePvt.ClockTimeSyncInterval = ClockTimeSyncInterval_value;
+ ClockTimePvt.ClockTimeSyncInterval = ClockTimeSyncInterval_normal;
}
}
From b0db6568ea26ab41c4610d21b9bf1a901707f834 Mon Sep 17 00:00:00 2001
From: Andrew Johnson
Date: Fri, 7 Jun 2019 13:24:39 -0500
Subject: [PATCH 040/281] Replace EPICS_TIMEZONE envParam with EPICS_TZ
Adjust rtems_init() to use it.
---
configure/CONFIG_SITE_ENV | 69 ++++++++++++++++++-----------------
src/libCom/RTEMS/rtems_init.c | 24 +++---------
src/libCom/env/envDefs.h | 2 +-
3 files changed, 41 insertions(+), 54 deletions(-)
diff --git a/configure/CONFIG_SITE_ENV b/configure/CONFIG_SITE_ENV
index 49331f9a4..008467933 100644
--- a/configure/CONFIG_SITE_ENV
+++ b/configure/CONFIG_SITE_ENV
@@ -24,40 +24,41 @@
# Site-specific environment settings
-# Time service:
-# EPICS_TIMEZONE
-# Local timezone info for vxWorks and RTEMS. The format is
-# ::::
-# where is only used by strftime() for %Z conversions,
-# and and are mmddhh - that is month,day,hour
-# e.g. for ANL in 2018: EPICS_TIMEZONE=CUS::360:031102:110402
-# The future dates below assume the rules don't get changed;
-# see http://www.timeanddate.com/time/dst/2018.html to check.
-#
-# DST for 2018 US: Mar 11 - Nov 04
-# EU: Mar 25 - Oct 28
-EPICS_TIMEZONE = CUS::360:031102:110402
-#EPICS_TIMEZONE = MET::-60:032502:102803
-#
-# DST for 2019 US: Mar 10 - Nov 03
-# EU: Mar 31 - Oct 27
-#EPICS_TIMEZONE = CUS::360:031002:110302
-#EPICS_TIMEZONE = MET::-60:033102:102703
-#
-# DST for 2020 US: Mar 08 - Nov 01
-# EU: Mar 29 - Oct 25
-#EPICS_TIMEZONE = CUS::360:030802:110102
-#EPICS_TIMEZONE = MET::-60:032902:102503
-#
-# DST for 2021 US: Mar 14 - Nov 07
-# EU: Mar 28 - Oct 31
-#EPICS_TIMEZONE = CUS::360:031402:110702
-#EPICS_TIMEZONE = MET::-60:032802:103103
-#
-# DST for 2022 US: Mar 13 - Nov 06
-# EU: Mar 27 - Oct 30
-#EPICS_TIMEZONE = CUS::360:031302:110602
-#EPICS_TIMEZONE = MET::-60:032702:103003
+## Time service:
+# EPICS_TZ
+# Local timezone rules for vxWorks and RTEMS. The value follows the Posix
+# TZ environment variable's Mm.n.d/h format (see the IBM link below for
+# details). If TZ hasn't already been set when the osdTime timeRegister()
+# C++ static constructor runs, this parameter will be copied into the TZ
+# environment variable. Once the OS clock has been synchronized to NTP the
+# routine tz2timezone() will be run to convert TZ into the TIMEZONE
+# variable format that VxWorks needs.
+# https://developer.ibm.com/articles/au-aix-posix/
+
+# Japan Standard Time, no DST:
+#EPICS_TZ = "JST-9"
+
+# Central European (Summer) Time:
+#EPICS_TZ = "CET-1CEST,M3.5.0/2,M10.5.0/3"
+
+# Greenwich Mean/British Summer Time:
+#EPICS_TZ = "GMT0BST,M3.5.0/1,M10.5.0/2"
+
+# US Eastern Standard/Daylight Time:
+#EPICS_TZ = "EST5EDT,M3.2.0/2,M11.1.0/2"
+
+# US Central Standard/Daylight Time:
+EPICS_TZ = "CST6CDT,M3.2.0/2,M11.1.0/2"
+
+# US Mountain Standard/Daylight Time:
+#EPICS_TZ = "MST7MDT,M3.2.0/2,M11.1.0/2"
+
+# US Pacific Standard/Daylight Time:
+#EPICS_TZ = "PST8PDT,M3.2.0/2,M11.1.0/2"
+
+# US Hawaiian Standard Time, no DST:
+#EPICS_TZ = "HST10"
+
# EPICS_TS_NTP_INET
# NTP time server ip address for VxWorks and RTEMS.
diff --git a/src/libCom/RTEMS/rtems_init.c b/src/libCom/RTEMS/rtems_init.c
index 2b909ab3b..dcb6daaf7 100644
--- a/src/libCom/RTEMS/rtems_init.c
+++ b/src/libCom/RTEMS/rtems_init.c
@@ -581,25 +581,11 @@ Init (rtems_task_argument ignored)
printf ("***** Can't set time: %s\n", rtems_status_text (sc));
}
if (getenv("TZ") == NULL) {
- const char *tzp = envGetConfigParamPtr(&EPICS_TIMEZONE);
- if (tzp == NULL) {
- printf("Warning -- no timezone information available -- times will be displayed as GMT.\n");
- }
- else {
- char tz[10];
- int minWest, toDst = 0, fromDst = 0;
- if(sscanf(tzp, "%9[^:]::%d:%d:%d", tz, &minWest, &toDst, &fromDst) < 2) {
- printf("Warning: EPICS_TIMEZONE (%s) unrecognizable -- times will be displayed as GMT.\n", tzp);
- }
- else {
- char posixTzBuf[40];
- char *p = posixTzBuf;
- p += sprintf(p, "%cST%d:%.2d", tz[0], minWest/60, minWest%60);
- if (toDst != fromDst)
- p += sprintf(p, "%cDT", tz[0]);
- epicsEnvSet("TZ", posixTzBuf);
- }
- }
+ const char *tzp = envGetConfigParamPtr(&EPICS_TZ);
+ if (!tzp || *tzp)
+ printf("Warning: No timezone information, times will be displayed in UTC.\n");
+ else
+ epicsEnvSet("TZ", tzp);
}
tzset();
osdTimeRegister();
diff --git a/src/libCom/env/envDefs.h b/src/libCom/env/envDefs.h
index 20f0eb2ad..588cecdad 100644
--- a/src/libCom/env/envDefs.h
+++ b/src/libCom/env/envDefs.h
@@ -61,7 +61,7 @@ epicsShareExtern const ENV_PARAM EPICS_CAS_BEACON_PORT;
epicsShareExtern const ENV_PARAM EPICS_BUILD_COMPILER_CLASS;
epicsShareExtern const ENV_PARAM EPICS_BUILD_OS_CLASS;
epicsShareExtern const ENV_PARAM EPICS_BUILD_TARGET_ARCH;
-epicsShareExtern const ENV_PARAM EPICS_TIMEZONE;
+epicsShareExtern const ENV_PARAM EPICS_TZ;
epicsShareExtern const ENV_PARAM EPICS_TS_NTP_INET;
epicsShareExtern const ENV_PARAM EPICS_IOC_LOG_PORT;
epicsShareExtern const ENV_PARAM EPICS_IOC_LOG_INET;
From b57f02ece202c71c03d94c9b53990c29ab1a416c Mon Sep 17 00:00:00 2001
From: Andrew Johnson
Date: Fri, 7 Jun 2019 13:26:24 -0500
Subject: [PATCH 041/281] epicsTimeTo[GM]TM(): Allow pNSecDest==NULL
---
src/libCom/osi/epicsTime.cpp | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/src/libCom/osi/epicsTime.cpp b/src/libCom/osi/epicsTime.cpp
index af4fae25d..9f544274f 100644
--- a/src/libCom/osi/epicsTime.cpp
+++ b/src/libCom/osi/epicsTime.cpp
@@ -955,7 +955,8 @@ extern "C" {
try {
local_tm_nano_sec tmns = epicsTime (*pSrc);
*pDest = tmns.ansi_tm;
- *pNSecDest = tmns.nSec;
+ if (pNSecDest)
+ *pNSecDest = tmns.nSec;
}
catch (...) {
return epicsTimeERROR;
@@ -967,7 +968,8 @@ extern "C" {
try {
gm_tm_nano_sec gmtmns = epicsTime (*pSrc);
*pDest = gmtmns.ansi_tm;
- *pNSecDest = gmtmns.nSec;
+ if (pNSecDest)
+ *pNSecDest = gmtmns.nSec;
}
catch (...) {
return epicsTimeERROR;
From 96998f55e01f6472fa41f9db448db768628002bb Mon Sep 17 00:00:00 2001
From: Andrew Johnson
Date: Fri, 7 Jun 2019 14:55:26 -0500
Subject: [PATCH 042/281] Have VxWorks call tz2timezone() once clock is sync'd
---
src/libCom/osi/Makefile | 2 +-
src/libCom/osi/os/vxWorks/osdTime.cpp | 32 ++-
src/libCom/osi/os/vxWorks/tz2timezone.c | 278 ++++++++++++++++++++++++
3 files changed, 303 insertions(+), 9 deletions(-)
create mode 100644 src/libCom/osi/os/vxWorks/tz2timezone.c
diff --git a/src/libCom/osi/Makefile b/src/libCom/osi/Makefile
index c06a862ed..e05aec37d 100644
--- a/src/libCom/osi/Makefile
+++ b/src/libCom/osi/Makefile
@@ -77,7 +77,7 @@ Com_SRCS += epicsGeneralTime.c
# Time providers
Com_SRCS += osiClockTime.c
-Com_SRCS_vxWorks += osiNTPTime.c
+Com_SRCS_vxWorks += osiNTPTime.c tz2timezone.c
Com_SRCS_RTEMS += osiNTPTime.c
ifeq ($(OS_CLASS),vxWorks)
diff --git a/src/libCom/osi/os/vxWorks/osdTime.cpp b/src/libCom/osi/os/vxWorks/osdTime.cpp
index 4db375fbb..eb144c1b6 100644
--- a/src/libCom/osi/os/vxWorks/osdTime.cpp
+++ b/src/libCom/osi/os/vxWorks/osdTime.cpp
@@ -24,22 +24,38 @@
#define NTP_REQUEST_TIMEOUT 4 /* seconds */
+extern "C" {
+ int tz2timezone(void);
+}
+
static char sntp_sync_task[] = "ipsntps";
static char ntp_daemon[] = "ipntpd";
static const char *pserverAddr = NULL;
+static CLOCKTIME_SYNCHOOK prevHook;
+
extern char* sysBootLine;
+static void timeSync(int synchronized) {
+ if (!tz2timezone())
+ ClockTime_syncHook = prevHook; /* Don't call me again */
+}
+
static int timeRegister(void)
{
- /* If TIMEZONE not defined, set it from EPICS_TIMEZONE */
- if (getenv("TIMEZONE") == NULL) {
- const char *timezone = envGetConfigParamPtr(&EPICS_TIMEZONE);
- if (timezone == NULL) {
- printf("timeRegister: No Time Zone Information\n");
- } else {
- epicsEnvSet("TIMEZONE", timezone);
+ /* If TZ not defined, set it from EPICS_TZ */
+ if (getenv("TZ") == NULL) {
+ const char *tz = envGetConfigParamPtr(&EPICS_TZ);
+
+ if (tz && *tz) {
+ epicsEnvSet("TZ", tz);
+
+ /* Call tz2timezone() once we know what year it is */
+ prevHook = ClockTime_syncHook;
+ ClockTime_syncHook = timeSync;
}
+ else if (getenv("TIMEZONE") == NULL)
+ printf("timeRegister: No Time Zone Information available\n");
}
// Define EPICS_TS_FORCE_NTPTIME to force use of NTPTime provider
@@ -57,7 +73,7 @@ static int timeRegister(void)
}
if (useNTP) {
- // Start NTP first so it can be used to sync SysTime
+ // Start NTP first so it can be used to sync ClockTime
NTPTime_Init(100);
ClockTime_Init(CLOCKTIME_SYNC);
} else {
diff --git a/src/libCom/osi/os/vxWorks/tz2timezone.c b/src/libCom/osi/os/vxWorks/tz2timezone.c
new file mode 100644
index 000000000..df8db8514
--- /dev/null
+++ b/src/libCom/osi/os/vxWorks/tz2timezone.c
@@ -0,0 +1,278 @@
+/*************************************************************************\
+* Copyright (c) 2009 Brookhaven Science Associates, as Operator of
+* Brookhaven National Laboratory.
+* Copyright (c) 2019 UChicago Argonne LLC, as Operator of Argonne
+* National Laboratory.
+* EPICS BASE is distributed subject to a Software License Agreement found
+* in file LICENSE that is included with this distribution.
+\*************************************************************************/
+/*
+ * Authors: Larry Hoff, Andrew Johnson
+ */
+
+/*
+ * This file exports a single function "int tz2timezone(void)" which reads
+ * the TZ environment variable (defined by POSIX) and converts it to the
+ * TIMEZONE environment variable defined by ANSI. The latter is used by
+ * VxWorks "time" functions, is largely deprecated in other computing
+ * environments, has limitations, and is difficult to maintain. This holds
+ * out the possibility of "pretending" that VxWorks supports "TZ" - until
+ * such time as it actually does.
+ *
+ * For simplicity, only the "POSIX standard form" of TZ will be supported.
+ * Even that is complicated enough (see following spec). Furthermore,
+ * only the "M" form of DST start and stop dates are supported.
+ *
+ * TZ = zone[-]offset[dst[offset],start[/time],end[/time]]
+ *
+ * zone
+ * A three or more letter name for the timezone in normal (winter) time.
+ *
+ * [-]offset
+ * A signed time giving the offset of the time zone westwards from
+ * Greenwich. The time has the form hh[:mm[:ss]] with a one of two
+ * digit hour, and optional two digit minutes and seconds.
+ *
+ * dst
+ * The name of the time zone when daylight saving is in effect. It may
+ * be followed by an offset giving how big the adjustment is, required
+ * if different than the default of 1 hour.
+ *
+ * start/time,end/time
+ * Specify the start and end of the daylight saving period. The start
+ * and end fields indicate on what day the changeover occurs, and must
+ * be in this format:
+ *
+ * Mm.n.d
+ * This indicates month m, the n-th occurrence of day d, where
+ * 1 <= m <= 12, 1 <= n <= 5, 0 <= d <= 6, 0=Sunday
+ * The 5th occurrence means the last occurrence of that day in a
+ * month. So M4.1.0 is the first Sunday in April, M9.5.0 is the
+ * last Sunday in September.
+ *
+ * The time field indicates what hour the changeover occurs on the given
+ * day (TIMEZONE only supports switching on the hour).
+ *
+ */
+
+#include
+#include /* getenv() */
+#include /* printf() */
+#include /* strchr() */
+#include /* isalpha() */
+
+#include
+
+/* for reference: TZ syntax, example, and TIMEZONE example
+ * std offset dst [offset],start[/time],end[/time]
+ * CST6CDT5,M3.2.0,M11.1.0
+ * EST+5EDT,M4.1.0/2,M10.5.0/2
+ *
+ * std offset start stop
+ * TIMEZONE=EST::300:030802:110102
+ */
+
+static int extractDate(const char *tz, struct tm *current, char *s)
+{
+ static const int startdays[] = {
+ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
+ };
+ static const int molengths[] = {
+ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+ };
+
+ int month, week, weekday, hour=2; /* default=2AM */
+ int jan1wday, wday, mday;
+
+ /* Require 'M' format */
+ if (*++tz != 'M') {
+ printf("tz2timezone: Unsupported date type, need 'M' format\n");
+ return ERROR;
+ }
+ tz++;
+
+ if (sscanf(tz, "%d.%d.%d/%d", &month, &week, &weekday, &hour) < 3)
+ return ERROR; /* something important missing */
+
+ if (month == 0 || month>12 ||
+ week < 1 || week > 5 ||
+ weekday < 0 || weekday > 6 ||
+ hour < 0 || hour > 23)
+ return ERROR;
+
+ /* Now for some brute-force calendar calculations... */
+ /* start month is in "month", and the day is "weekday", but
+ we need to know when that weekday first occurs in that month */
+ /* Let's start with weekday on Jan. 1 */
+ jan1wday = (7 + current->tm_wday - (current->tm_yday % 7)) % 7;
+
+ /* We need to know if it is a leap year (and if it matters) */
+ /* Let's assume that we're working with a date between 1901 and 2099,
+ that way we don't have to think about the "century exception".
+ If this code is still running (unchanged) in 2100, I'll be stunned
+ (and 139 years old) */
+ wday = (jan1wday + startdays[month-1] +
+ ((month > 2 && (current->tm_year % 4 == 0)) ? 1 : 0)) % 7;
+
+ /* Let's see on what day-of-the-month the first target weekday occurs
+ (counting from 1). The result is a number between 1 and 7, inclusive. */
+ mday = 1 + ((7 + weekday - wday) % 7);
+
+ /* Next, we add the week offset. If we overflow the month, we subtract
+ one week */
+ mday += 7 * (week - 1);
+ if (mday > molengths[month-1])
+ mday -= 7;
+
+ /* Should I handle Feb 29? I'm willing to gamble that no one in their right
+ mind would schedule a time change to occur on Feb. 29. If so, we'll be a
+ week early */
+ sprintf(s, "%02d%02d%02d", month, mday, hour);
+
+ return OK;
+}
+
+
+static const char *getTime(const char *s, int *time)
+{
+ /* Time format is [+/-]hh[:mm][:ss] followed by the next zone name */
+
+ *time = 0;
+
+ if (!isdigit((int) s[0]))
+ return s; /* no number here... */
+
+ if (!isdigit((int) s[1])) { /* single digit form */
+ *time = s[0] - '0';
+ return s + 1;
+ }
+
+ if (isdigit((int) s[1])) { /* two digit form */
+ *time = 10 * (s[0] - '0') + (s[1] - '0');
+ return s + 2;
+ }
+
+ return s; /* does not follow supported form */
+}
+
+int tz2timezone(void)
+{
+ const char *tz = getenv("TZ");
+ /* Spec. says that zone names must be at least 3 chars.
+ * I've never seen a longer zone name, but I'll allocate
+ * 40 chars. If you live in a zone with a longer name,
+ * you may want to think about the benefits of relocation.
+ */
+ char zone[40];
+ char start[10], stop[10]; /* only really need 7 bytes now */
+ int hours = 0, minutes = 0, sign = 1;
+ /* This is more than enough, even with a 40-char zone
+ * name, and 4-char offset.
+ */
+ char timezone[100];
+ int i = 0; /* You *always need an "i" :-) */
+ epicsTimeStamp now;
+ struct tm current;
+
+ /* First let's get the current time. We need the year to
+ * compute the start/stop dates for DST.
+ */
+ if (epicsTimeGetCurrent(&now) ||
+ epicsTimeToTM(¤t, NULL, &now))
+ return ERROR;
+
+ /* Make sure TZ exists.
+ * Spec. says that ZONE must be at least 3 chars.
+ */
+ if ((!tz) || (strlen(tz) < 3))
+ return ERROR;
+
+ /* OK, now a bunch of brute-force parsing. My brain hurts if
+ * I try to think of an elegant regular expression for the
+ * string.
+ */
+
+ /* Start extracting zone name, must be alpha */
+ while ((i < sizeof(zone) - 1) && isalpha((int) *tz)) {
+ zone[i++] = *tz++;
+ }
+ if (i < 3)
+ return ERROR; /* Too short, not a real zone name? */
+
+ zone[i] = 0; /* Nil-terminate (for now) */
+
+ /* Now extract offset time. The format is [+/-]hh[:mm[:ss]]
+ * Recall that TIMEZONE doesn't support seconds....
+ */
+ if (*tz == '-') {
+ sign = -1;
+ tz++;
+ }
+ else if (*tz == '+') {
+ tz++;
+ }
+
+ /* Need a digit now */
+ if (!isdigit((int) *tz))
+ return ERROR;
+
+ /* First get the hours */
+ tz = getTime(tz, &hours);
+ if (hours > 24)
+ return ERROR;
+
+ if (*tz == ':') { /* There is a minutes part */
+ /* Need another digit now */
+ if (!isdigit((int) *++tz))
+ return ERROR;
+
+ /* Extract the minutes */
+ tz = getTime(tz, &minutes);
+ if (minutes > 60)
+ return ERROR;
+
+ /* Skip any seconds part */
+ if (*tz == ':') {
+ int seconds;
+ tz = getTime(tz + 1, &seconds);
+ }
+ }
+
+ /* Extract any DST zone name, must be alpha */
+ if (isalpha((int) *tz)) {
+ zone[i++] = '/'; /* Separate the names */
+
+ while ((i < sizeof(zone) - 1) && isalpha((int) *tz)) {
+ zone[i++] = *tz++;
+ }
+ zone[i] = 0; /* Nil-terminate */
+ }
+
+ minutes += hours * 60;
+ minutes *= sign;
+
+ /* Look for start/stop dates - require neither or both */
+ tz = strchr(tz, ',');
+ if (!tz) { /* No daylight savings time here */
+ /* Format the env. variable */
+ sprintf(timezone, "TIMEZONE=%s::%d", zone, minutes);
+ }
+ else {
+ if (extractDate(tz, ¤t, start) != OK)
+ return ERROR;
+
+ tz = strchr(tz + 1, ',');
+ if (!tz)
+ return ERROR;
+ if (extractDate(tz, ¤t, stop) != OK)
+ return ERROR;
+
+ /* Format the env. variable */
+ sprintf(timezone, "TIMEZONE=%s::%d:%s:%s", zone, minutes, start, stop);
+ }
+
+ /* Make it live! */
+ putenv(timezone);
+
+ return OK;
+}
From d3a8a49552ca7930aa9a5ab34ceb1de9cd25ccf4 Mon Sep 17 00:00:00 2001
From: Andrew Johnson
Date: Fri, 7 Jun 2019 15:37:46 -0500
Subject: [PATCH 043/281] Release Notes
---
documentation/RELEASE_NOTES.html | 32 ++++++++++++++++++++++++++++++++
1 file changed, 32 insertions(+)
diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html
index 8ed4e04bf..f6daa49d4 100644
--- a/documentation/RELEASE_NOTES.html
+++ b/documentation/RELEASE_NOTES.html
@@ -16,6 +16,38 @@
+
Replace EPICS_TIMEZONE with EPICS_TZ
+
+
The EPICS_TIMEZONE environment parameter provided time-zone
+information for the IOC's locale in the old ANSI format expected by VxWorks for
+its TIMEZONE environment variable, and can also used by RTEMS to set
+its TZ environment variable. However the TIMEZONE value has to
+be updated every year since it contains the exact dates of the daylight-savings
+time changes. The Posix TZ format that RTEMS uses contains rules that for
+calculating those dates, thus its value would only need updating if the rules
+(or the locale) are changed.
+
+
This release contains changes that replace the EPICS_TIMEZONE
+environment parameter with one called EPICS_TZ and a routine for
+VxWorks that calculates the TIMEZONE environment variable from the
+current TZ value. This routine will be run once at start-up, when the
+EPICS clock has synchronized to its NTP server. The calculations it contains
+were worked out and donated to EPICS by Larry Hoff in 2009; it is unforunate
+that it has taken 10 years for them to be integrated into Base.
+
+
The default value for the EPICS_TZ environment parameter is set in
+the Base configure/CONFIG_SITE_ENV file, which contains example settings for
+most EPICS sites that use VxWorks, and a link to a page describing the Posix TZ
+format for any locations that I missed.
+
+
If a VxWorks IOC runs continuously without being rebooted from December 31st
+to the start of daylight savings time the following year, its TIMEZONE
+value will be wrong as it was calculated for the previous year. This only
+affects times that are converted to a string on the IOC however and is easily
+fixed; just run the command tz2timezone() on the VxWorks shell and the
+calculation will be redone for the current year. IOCs that get rebooted at least
+once before the start of summer time will not need this to be done.
+
Cleaning up with Multiple CA contexts in a Process
Bruno Martins reported a problem with the CA client library at shutdown in a
From e8db975e7f764c9d6e446de01e307ed27d9313aa Mon Sep 17 00:00:00 2001
From: Andrew Johnson
Date: Mon, 17 Jun 2019 14:33:28 -0500
Subject: [PATCH 044/281] Initial implementation for VxWorks 6.9 and later
This almost seems too simple...
---
modules/libcom/src/osi/os/vxWorks/osdThread.c | 9 ++++++++-
modules/libcom/src/osi/os/vxWorks/osdThread.h | 10 ++++++++--
2 files changed, 16 insertions(+), 3 deletions(-)
diff --git a/modules/libcom/src/osi/os/vxWorks/osdThread.c b/modules/libcom/src/osi/os/vxWorks/osdThread.c
index 7a0fe7b9f..8a24add1c 100644
--- a/modules/libcom/src/osi/os/vxWorks/osdThread.c
+++ b/modules/libcom/src/osi/os/vxWorks/osdThread.c
@@ -223,7 +223,14 @@ epicsThreadCreateOpt (
return((epicsThreadId)tid);
}
-void epicsThreadJoin(epicsThreadId id) {}
+void epicsThreadJoin(epicsThreadId id) {
+#if EPICS_THREAD_CAN_JOIN
+ int tid = (int)id;
+
+ if (tid)
+ taskWait(tid, WAIT_FOREVER);
+#endif
+}
void epicsThreadSuspendSelf()
{
diff --git a/modules/libcom/src/osi/os/vxWorks/osdThread.h b/modules/libcom/src/osi/os/vxWorks/osdThread.h
index 09704b667..8fa454ce3 100644
--- a/modules/libcom/src/osi/os/vxWorks/osdThread.h
+++ b/modules/libcom/src/osi/os/vxWorks/osdThread.h
@@ -10,7 +10,13 @@
#ifndef osdThreadh
#define osdThreadh
-/* This target does not support joining threads */
-#define EPICS_THREAD_CAN_JOIN (0)
+/* VxWorks 6.9 and later can support joining threads */
+
+#if (_WRS_VXWORKS_MAJOR > 6) || \
+ (_WRS_VXWORKS_MAJOR == 6 && _WRS_VXWORKS_MINOR >= 9)
+# define EPICS_THREAD_CAN_JOIN (1)
+#else
+# define EPICS_THREAD_CAN_JOIN (0)
+#endif
#endif /* osdThreadh */
From 5efce9ecc056e5de24c7c8a8cbc2fba7d7f4ef67 Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Sun, 23 Jun 2019 07:30:48 -0700
Subject: [PATCH 045/281] epicsThreadJoin() -> epicsThreadMustJoin()
---
modules/libcom/src/osi/epicsThread.cpp | 4 ++--
modules/libcom/src/osi/epicsThread.h | 2 +-
modules/libcom/src/osi/os/RTEMS/osdThread.c | 4 ++--
modules/libcom/src/osi/os/WIN32/osdThread.c | 2 +-
modules/libcom/src/osi/os/posix/osdThread.c | 4 ++--
modules/libcom/src/osi/os/vxWorks/osdThread.c | 2 +-
6 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/modules/libcom/src/osi/epicsThread.cpp b/modules/libcom/src/osi/epicsThread.cpp
index 7092900b1..9a049133f 100644
--- a/modules/libcom/src/osi/epicsThread.cpp
+++ b/modules/libcom/src/osi/epicsThread.cpp
@@ -158,7 +158,7 @@ bool epicsThread::exitWait ( const double delay ) throw ()
epicsGuard < epicsMutex > guard ( this->mutex );
joined = true;
}
- epicsThreadJoin(this->id);
+ epicsThreadMustJoin(this->id);
}
return true;
}
@@ -177,7 +177,7 @@ bool epicsThread::exitWait ( const double delay ) throw ()
joined = true;
epicsGuardRelease < epicsMutex > unguard ( guard );
- epicsThreadJoin(this->id);
+ epicsThreadMustJoin(this->id);
}
}
catch ( std :: exception & except ) {
diff --git a/modules/libcom/src/osi/epicsThread.h b/modules/libcom/src/osi/epicsThread.h
index c43d7fb01..61ab323b1 100644
--- a/modules/libcom/src/osi/epicsThread.h
+++ b/modules/libcom/src/osi/epicsThread.h
@@ -89,7 +89,7 @@ epicsShareFunc epicsThreadId epicsShareAPI epicsThreadCreate (
epicsShareFunc epicsThreadId epicsShareAPI epicsThreadMustCreate (
const char * name, unsigned int priority, unsigned int stackSize,
EPICSTHREADFUNC funptr,void * parm );
-epicsShareFunc void epicsThreadJoin(epicsThreadId id);
+epicsShareFunc void epicsThreadMustJoin(epicsThreadId id);
epicsShareFunc void epicsShareAPI epicsThreadSuspendSelf(void);
epicsShareFunc void epicsShareAPI epicsThreadResume(epicsThreadId id);
epicsShareFunc unsigned int epicsShareAPI epicsThreadGetPriority(
diff --git a/modules/libcom/src/osi/os/RTEMS/osdThread.c b/modules/libcom/src/osi/os/RTEMS/osdThread.c
index a22e2f87f..cae1b0449 100644
--- a/modules/libcom/src/osi/os/RTEMS/osdThread.c
+++ b/modules/libcom/src/osi/os/RTEMS/osdThread.c
@@ -363,7 +363,7 @@ threadMustCreate (const char *name,
return tid;
}
-void epicsThreadJoin(epicsThreadId id)
+void epicsThreadMustJoin(epicsThreadId id)
{
rtems_id target_tid = (rtems_id)id, self_tid;
struct taskVar *v;
@@ -389,7 +389,7 @@ void epicsThreadJoin(epicsThreadId id)
cantProceed("oopsj %s\n", rtems_status_text(sc));
if(sc != RTEMS_SUCCESSFUL) {
- errlogPrintf("epicsThreadJoin('%s') -> %s\n", v->name, rtems_status_text(sc));
+ errlogPrintf("epicsThreadMustJoin('%s') -> %s\n", v->name, rtems_status_text(sc));
}
}
diff --git a/modules/libcom/src/osi/os/WIN32/osdThread.c b/modules/libcom/src/osi/os/WIN32/osdThread.c
index eba41645f..bc41037cf 100644
--- a/modules/libcom/src/osi/os/WIN32/osdThread.c
+++ b/modules/libcom/src/osi/os/WIN32/osdThread.c
@@ -662,7 +662,7 @@ epicsThreadId epicsThreadCreateOpt (
return ( epicsThreadId ) pParmWIN32;
}
-void epicsThreadJoin(epicsThreadId id)
+void epicsThreadMustJoin(epicsThreadId id)
{
win32ThreadParam * pParmWIN32 = id;
diff --git a/modules/libcom/src/osi/os/posix/osdThread.c b/modules/libcom/src/osi/os/posix/osdThread.c
index d34edf694..e2f5f556b 100644
--- a/modules/libcom/src/osi/os/posix/osdThread.c
+++ b/modules/libcom/src/osi/os/posix/osdThread.c
@@ -557,7 +557,7 @@ epicsThreadCreateOpt (
status = pthread_sigmask(SIG_SETMASK,&oldSig,NULL);
checkStatusOnce(status,"pthread_sigmask");
if(pthreadInfo->joinable) {
- /* extra ref for epicsThreadJoin() */
+ /* extra ref for epicsThreadMustJoin() */
epicsAtomicIncrIntT(&pthreadInfo->refcnt);
}
return(pthreadInfo);
@@ -600,7 +600,7 @@ static epicsThreadOSD *createImplicit(void)
return pthreadInfo;
}
-void epicsThreadJoin(epicsThreadId id)
+void epicsThreadMustJoin(epicsThreadId id)
{
void *ret = NULL;
int status;
diff --git a/modules/libcom/src/osi/os/vxWorks/osdThread.c b/modules/libcom/src/osi/os/vxWorks/osdThread.c
index 8a24add1c..6847e9769 100644
--- a/modules/libcom/src/osi/os/vxWorks/osdThread.c
+++ b/modules/libcom/src/osi/os/vxWorks/osdThread.c
@@ -223,7 +223,7 @@ epicsThreadCreateOpt (
return((epicsThreadId)tid);
}
-void epicsThreadJoin(epicsThreadId id) {
+void epicsThreadMustJoin(epicsThreadId id) {
#if EPICS_THREAD_CAN_JOIN
int tid = (int)id;
From 9e5c63fb9158f0262abfc9e6be282dcb542a914c Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Sun, 23 Jun 2019 07:53:23 -0700
Subject: [PATCH 046/281] epicsThreadMustJoin() clear joinable flag
Clear so that repeated calls will error correctly.
Only well defined for self join.
---
modules/libcom/src/osi/os/RTEMS/osdThread.c | 4 +++-
modules/libcom/src/osi/os/WIN32/osdThread.c | 5 ++++-
modules/libcom/src/osi/os/posix/osdThread.c | 4 +++-
3 files changed, 10 insertions(+), 3 deletions(-)
diff --git a/modules/libcom/src/osi/os/RTEMS/osdThread.c b/modules/libcom/src/osi/os/RTEMS/osdThread.c
index cae1b0449..b30cb4156 100644
--- a/modules/libcom/src/osi/os/RTEMS/osdThread.c
+++ b/modules/libcom/src/osi/os/RTEMS/osdThread.c
@@ -380,7 +380,8 @@ void epicsThreadMustJoin(epicsThreadId id)
/* try to error nicely, however in all likelyhood rtems_task_get_note failed,
* or gave us the wrong thread as we are racing thread exit.
*/
- cantProceed("%s join not enabled for thread.\n", v->name);
+ cantProceed("%s thread not joinable.\n", v->name);
+ return;
} else if(target_tid!=self_tid) {
/* wait for target to complete */
@@ -393,6 +394,7 @@ void epicsThreadMustJoin(epicsThreadId id)
}
}
+ v->joinable = 0;
taskUnref(v);
/* target task may be deleted.
* self task is not deleted, even for self join.
diff --git a/modules/libcom/src/osi/os/WIN32/osdThread.c b/modules/libcom/src/osi/os/WIN32/osdThread.c
index bc41037cf..8c3973703 100644
--- a/modules/libcom/src/osi/os/WIN32/osdThread.c
+++ b/modules/libcom/src/osi/os/WIN32/osdThread.c
@@ -673,7 +673,8 @@ void epicsThreadMustJoin(epicsThreadId id)
* 'pParmWIN32' has already crashed us as we are racing thread exit,
* which free's 'pParmWIN32'.
*/
- cantProceed("%s join not enabled for thread.\n", pParmWIN32->pName);
+ cantProceed("%s thread not joinable.\n", pParmWIN32->pName);
+ return;
} else if(epicsThreadGetIdSelf() != id) {
DWORD status = WaitForSingleObject(pParmWIN32->handle, INFINITE);
@@ -681,9 +682,11 @@ void epicsThreadMustJoin(epicsThreadId id)
/* TODO: signal error? */
}
+ pParmWIN32->joinable = 0;
epicsParmCleanupWIN32(pParmWIN32);
} else {
/* join self silently does nothing */
+ pParmWIN32->joinable = 0;
epicsParmCleanupWIN32(pParmWIN32);
}
}
diff --git a/modules/libcom/src/osi/os/posix/osdThread.c b/modules/libcom/src/osi/os/posix/osdThread.c
index e2f5f556b..ed3785205 100644
--- a/modules/libcom/src/osi/os/posix/osdThread.c
+++ b/modules/libcom/src/osi/os/posix/osdThread.c
@@ -612,7 +612,8 @@ void epicsThreadMustJoin(epicsThreadId id)
* 'id' has already caused SIGSEGV as we are racing thread exit,
* which free's 'id'.
*/
- cantProceed("%s join not enabled for thread.\n", id->name);
+ cantProceed("%s thread not joinable.\n", id->name);
+ return;
}
status = pthread_join(id->tid, &ret);
@@ -623,6 +624,7 @@ void epicsThreadMustJoin(epicsThreadId id)
status = pthread_detach(id->tid);
checkStatusOnce(status, "pthread_detach");
} else checkStatusOnce(status, "pthread_join");
+ id->joinable = 0;
free_threadInfo(id);
}
From 86a942872aea66e06cc8aa5478da74f8f5d3fb77 Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Sun, 23 Jun 2019 08:01:59 -0700
Subject: [PATCH 047/281] epicsThreadTest check explicit join
Also remove all the sleeps to improve chances
of catching some kind of race.
---
modules/libcom/test/epicsThreadTest.cpp | 29 ++++++++++++++++---------
1 file changed, 19 insertions(+), 10 deletions(-)
diff --git a/modules/libcom/test/epicsThreadTest.cpp b/modules/libcom/test/epicsThreadTest.cpp
index eb26cc8bf..c50d8c5bd 100644
--- a/modules/libcom/test/epicsThreadTest.cpp
+++ b/modules/libcom/test/epicsThreadTest.cpp
@@ -52,7 +52,7 @@ void myThread::run()
startEvt.signal();
int *pset = argvalue;
privateKey.set(argvalue);
- epicsThreadSleep(2.0);
+
int *pget = privateKey.get();
testOk1(pget == pset);
@@ -63,6 +63,7 @@ void myThread::run()
typedef struct info {
int isOkToBlock;
+ int didSomething;
} info;
extern "C" {
@@ -71,19 +72,19 @@ static void thread(void *arg)
info *pinfo = (info *)arg;
epicsThreadSetOkToBlock(pinfo->isOkToBlock);
- epicsThreadSleep(1.0);
testOk(epicsThreadIsOkToBlock() == pinfo->isOkToBlock,
"%s epicsThreadIsOkToBlock() = %d",
epicsThreadGetNameSelf(), pinfo->isOkToBlock);
- epicsThreadSleep(0.1);
+
+ pinfo->didSomething = 1;
}
}
MAIN(epicsThreadTest)
{
- testPlan(9);
+ testPlan(11);
unsigned int ncpus = epicsThreadGetCPUs();
testDiag("System has %u CPUs", ncpus);
@@ -108,15 +109,23 @@ MAIN(epicsThreadTest)
delete myThreads[i];
}
- unsigned int stackSize = epicsThreadGetStackSize(epicsThreadStackSmall);
+ epicsThreadOpts opts;
+ epicsThreadOptsDefaults(&opts);
+ opts.priority = 50;
+ opts.joinable = 1;
- info infoA = {0};
- epicsThreadCreate("threadA", 50, stackSize, thread, &infoA);
+ info infoA = {0, 0};
+ epicsThreadId threadA = epicsThreadCreateOpt("threadA", thread, &infoA, &opts);
- info infoB = {1};
- epicsThreadCreate("threadB", 50, stackSize, thread, &infoB);
+ info infoB = {1, 0};
+ epicsThreadId threadB = epicsThreadCreateOpt("threadB", thread, &infoB, &opts);
- epicsThreadSleep(2.0);
+ // join B first to better our chance of detecting if it never runs.
+ epicsThreadMustJoin(threadB);
+ testOk1(infoB.didSomething);
+
+ epicsThreadMustJoin(threadA);
+ testOk1(infoA.didSomething);
return testDone();
}
From 4dcd6f37c697b1517b5e36ae1b9d130abfcd7699 Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Sun, 23 Jun 2019 16:18:43 -0700
Subject: [PATCH 048/281] update release notes
---
documentation/RELEASE_NOTES.html | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html
index 376279fc9..1d93d2b3b 100644
--- a/documentation/RELEASE_NOTES.html
+++ b/documentation/RELEASE_NOTES.html
@@ -38,7 +38,7 @@ host, and the IOC would believe them.
look up the IP address of any hostnames listed in its ACF (which will normally
be done using the DNS or the /etc/hosts file). The IOC will then
compare the resulting IP address against the client's actual IP address when
-checking access permissions at connection time. This name resolution gets done
+checking access permissions at connection time. This name resolution is performed
at ACF file load time, which has a few consequences:
@@ -46,14 +46,17 @@ at ACF file load time, which has a few consequences:
If the DNS is slow when the names are resolved this will delay the process
of loading the ACF file.
-
If a host name cannot be resolved the IOC will treat the ACF as invalid,
-which prevents any CA clients from connecting.
+
If a host name cannot be resolved the IOC will proceed, but this host name
+will never be matched.
Any changes in the hostname to IP address mapping will not be picked up by
the IOC unless and until the ACF file gets reloaded.
+
Optionally, IP addresses may be added instead of, or in addition to, host names
+in the ACF file.
+
This feature can be enabled before iocInit with
From 32b3eddb94cf4f47494ff2cc58d6f5e0f3f71d21 Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Sun, 23 Jun 2019 08:56:32 -0700
Subject: [PATCH 049/281] epicsThreadMustJoin() warn only on double self-join
---
modules/libcom/src/osi/os/RTEMS/osdThread.c | 14 ++-
modules/libcom/src/osi/os/WIN32/osdThread.c | 15 ++-
modules/libcom/src/osi/os/posix/osdThread.c | 15 ++-
modules/libcom/test/epicsThreadTest.cpp | 106 ++++++++++++++------
4 files changed, 108 insertions(+), 42 deletions(-)
diff --git a/modules/libcom/src/osi/os/RTEMS/osdThread.c b/modules/libcom/src/osi/os/RTEMS/osdThread.c
index b30cb4156..b23f66b8f 100644
--- a/modules/libcom/src/osi/os/RTEMS/osdThread.c
+++ b/modules/libcom/src/osi/os/RTEMS/osdThread.c
@@ -377,10 +377,16 @@ void epicsThreadMustJoin(epicsThreadId id)
}
if(!v->joinable) {
- /* try to error nicely, however in all likelyhood rtems_task_get_note failed,
- * or gave us the wrong thread as we are racing thread exit.
- */
- cantProceed("%s thread not joinable.\n", v->name);
+ if(epicsThreadGetIdSelf()==id) {
+ errlogPrintf("Warning: %s thread self-join of unjoinable\n", v->name);
+
+ } else {
+ /* try to error nicely, however in all likelyhood de-ref of
+ * 'id' has already caused SIGSEGV as we are racing thread exit,
+ * which free's 'id'.
+ */
+ cantProceed("Error: %s thread not joinable.\n", v->name);
+ }
return;
} else if(target_tid!=self_tid) {
diff --git a/modules/libcom/src/osi/os/WIN32/osdThread.c b/modules/libcom/src/osi/os/WIN32/osdThread.c
index 8c3973703..6d43e769f 100644
--- a/modules/libcom/src/osi/os/WIN32/osdThread.c
+++ b/modules/libcom/src/osi/os/WIN32/osdThread.c
@@ -669,11 +669,16 @@ void epicsThreadMustJoin(epicsThreadId id)
if(!id) {
/* no-op */
} else if(!pParmWIN32->joinable) {
- /* try to error nicely, however in all likelyhood de-ref of
- * 'pParmWIN32' has already crashed us as we are racing thread exit,
- * which free's 'pParmWIN32'.
- */
- cantProceed("%s thread not joinable.\n", pParmWIN32->pName);
+ if(epicsThreadGetIdSelf()==id) {
+ fprintf(stderr, "Warning: %s thread self-join of unjoinable\n", pParmWIN32->pName);
+
+ } else {
+ /* try to error nicely, however in all likelyhood de-ref of
+ * 'id' has already caused SIGSEGV as we are racing thread exit,
+ * which free's 'id'.
+ */
+ cantProceed("Error: %s thread not joinable.\n", pParmWIN32->pName);
+ }
return;
} else if(epicsThreadGetIdSelf() != id) {
diff --git a/modules/libcom/src/osi/os/posix/osdThread.c b/modules/libcom/src/osi/os/posix/osdThread.c
index ed3785205..5ed2cbc56 100644
--- a/modules/libcom/src/osi/os/posix/osdThread.c
+++ b/modules/libcom/src/osi/os/posix/osdThread.c
@@ -608,11 +608,16 @@ void epicsThreadMustJoin(epicsThreadId id)
if(!id) {
return;
} else if(!id->joinable) {
- /* try to error nicely, however in all likelyhood de-ref of
- * 'id' has already caused SIGSEGV as we are racing thread exit,
- * which free's 'id'.
- */
- cantProceed("%s thread not joinable.\n", id->name);
+ if(epicsThreadGetIdSelf()==id) {
+ errlogPrintf("Warning: %s thread self-join of unjoinable\n", id->name);
+
+ } else {
+ /* try to error nicely, however in all likelyhood de-ref of
+ * 'id' has already caused SIGSEGV as we are racing thread exit,
+ * which free's 'id'.
+ */
+ cantProceed("Error: %s thread not joinable.\n", id->name);
+ }
return;
}
diff --git a/modules/libcom/test/epicsThreadTest.cpp b/modules/libcom/test/epicsThreadTest.cpp
index c50d8c5bd..08d7529fa 100644
--- a/modules/libcom/test/epicsThreadTest.cpp
+++ b/modules/libcom/test/epicsThreadTest.cpp
@@ -18,11 +18,14 @@
#include
#include "epicsThread.h"
+#include "epicsEvent.h"
#include "epicsTime.h"
#include "errlog.h"
#include "epicsUnitTest.h"
#include "testMain.h"
+namespace {
+
static epicsThreadPrivate privateKey;
class myThread: public epicsThreadRunable {
@@ -60,35 +63,8 @@ void myThread::run()
testOk1(thread.getPriority() == epicsThreadGetPriority(self));
}
-
-typedef struct info {
- int isOkToBlock;
- int didSomething;
-} info;
-
-extern "C" {
-static void thread(void *arg)
+void testMyThread()
{
- info *pinfo = (info *)arg;
-
- epicsThreadSetOkToBlock(pinfo->isOkToBlock);
-
- testOk(epicsThreadIsOkToBlock() == pinfo->isOkToBlock,
- "%s epicsThreadIsOkToBlock() = %d",
- epicsThreadGetNameSelf(), pinfo->isOkToBlock);
-
- pinfo->didSomething = 1;
-}
-}
-
-
-MAIN(epicsThreadTest)
-{
- testPlan(11);
-
- unsigned int ncpus = epicsThreadGetCPUs();
- testDiag("System has %u CPUs", ncpus);
- testOk1(ncpus > 0);
const int ntasks = 3;
myThread *myThreads[ntasks];
@@ -108,6 +84,65 @@ MAIN(epicsThreadTest)
myThreads[i]->thread.exitWait();
delete myThreads[i];
}
+}
+
+struct selfJoiner {
+ epicsEvent finished;
+};
+
+void joiner(void *arg) {
+ epicsEvent *finished = (epicsEvent*)arg;
+
+ // This is a no-op
+ epicsThreadMustJoin(epicsThreadGetIdSelf());
+
+ // This is a no-op as well, except for a warning.
+ eltc(0);
+ epicsThreadMustJoin(epicsThreadGetIdSelf());
+ eltc(1);
+
+ testPass("Check double self-join");
+ finished->signal();
+}
+
+typedef struct info {
+ int isOkToBlock;
+ int didSomething;
+} info;
+
+void testSelfJoin()
+{
+ epicsEvent finished;
+ epicsThreadOpts opts;
+ epicsThreadOptsDefaults(&opts);
+ opts.priority = 50;
+ opts.joinable = 1;
+
+ (void)epicsThreadCreateOpt("selfjoin", &joiner, &finished, &opts);
+
+ // as this thread "joins" itself, we can't.
+ finished.wait();
+}
+
+} // namespace
+
+extern "C" {
+static void thread(void *arg)
+{
+ info *pinfo = (info *)arg;
+
+ epicsThreadSetOkToBlock(pinfo->isOkToBlock);
+
+ testOk(epicsThreadIsOkToBlock() == pinfo->isOkToBlock,
+ "%s epicsThreadIsOkToBlock() = %d",
+ epicsThreadGetNameSelf(), pinfo->isOkToBlock);
+
+ pinfo->didSomething = 1;
+}
+}
+
+static void testOkToBlock()
+{
epicsThreadOpts opts;
epicsThreadOptsDefaults(&opts);
@@ -127,5 +162,20 @@ MAIN(epicsThreadTest)
epicsThreadMustJoin(threadA);
testOk1(infoA.didSomething);
+}
+
+
+MAIN(epicsThreadTest)
+{
+ testPlan(12);
+
+ unsigned int ncpus = epicsThreadGetCPUs();
+ testDiag("System has %u CPUs", ncpus);
+ testOk1(ncpus > 0);
+
+ testMyThread();
+ testSelfJoin();
+ testOkToBlock();
+
return testDone();
}
From afc31f2f064974e97ef61a9dc6cc58692a1b0a5f Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Sun, 23 Jun 2019 09:29:04 -0700
Subject: [PATCH 050/281] asCaStop() join worker thread
---
modules/database/src/ioc/as/asCa.c | 21 +++++++++++++--------
1 file changed, 13 insertions(+), 8 deletions(-)
diff --git a/modules/database/src/ioc/as/asCa.c b/modules/database/src/ioc/as/asCa.c
index d0180448b..e8c5403d9 100644
--- a/modules/database/src/ioc/as/asCa.c
+++ b/modules/database/src/ioc/as/asCa.c
@@ -229,20 +229,23 @@ static void asCaTask(void)
void asCaStart(void)
{
+ epicsThreadOpts opts;
+ epicsThreadOptsDefaults(&opts);
+ opts.stackSize = epicsThreadGetStackSize(epicsThreadStackBig);
+ opts.priority = epicsThreadPriorityScanLow - 3;
+ opts.joinable = 1;
+
if(asCaDebug) printf("asCaStart called\n");
if(firstTime) {
- firstTime = FALSE;
+ firstTime = FALSE;
asCaTaskLock=epicsMutexMustCreate();
asCaTaskWait=epicsEventMustCreate(epicsEventEmpty);
asCaTaskAddChannels=epicsEventMustCreate(epicsEventEmpty);
asCaTaskClearChannels=epicsEventMustCreate(epicsEventEmpty);
- threadid = epicsThreadCreate("asCaTask",
- (epicsThreadPriorityScanLow - 3),
- epicsThreadGetStackSize(epicsThreadStackBig),
- (EPICSTHREADFUNC)asCaTask,0);
- if(threadid==0) {
- errMessage(0,"asCaStart: taskSpawn Failure\n");
- }
+ threadid = epicsThreadCreateOpt("asCaTask", (EPICSTHREADFUNC)asCaTask, 0, &opts);
+ if(threadid==0) {
+ errMessage(0,"asCaStart: taskSpawn Failure\n");
+ }
}
epicsMutexMustLock(asCaTaskLock);
epicsEventSignal(asCaTaskAddChannels);
@@ -260,6 +263,8 @@ void asCaStop(void)
epicsEventMustWait(asCaTaskWait);
if(asCaDebug) printf("asCaStop done\n");
epicsMutexUnlock(asCaTaskLock);
+ epicsThreadMustJoin(threadid);
+ threadid = 0;
}
int ascar(int level) { return ascarFP(stdout,level);}
From 01fa58f37b4ab74e355fb1bc74b1b1fcaaa96d1a Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Sun, 23 Jun 2019 09:33:08 -0700
Subject: [PATCH 051/281] dbCa join worker
---
modules/database/src/ioc/db/dbCa.c | 14 +++++++++++---
1 file changed, 11 insertions(+), 3 deletions(-)
diff --git a/modules/database/src/ioc/db/dbCa.c b/modules/database/src/ioc/db/dbCa.c
index 65a8327cf..935c397d4 100644
--- a/modules/database/src/ioc/db/dbCa.c
+++ b/modules/database/src/ioc/db/dbCa.c
@@ -68,6 +68,7 @@ static volatile enum dbCaCtl_t {
ctlInit, ctlRun, ctlPause, ctlExit
} dbCaCtl;
static epicsEventId startStopEvent;
+static epicsThreadId dbCaWorker;
struct ca_client_context * dbCaClientContext;
@@ -258,10 +259,18 @@ void dbCaShutdown(void)
dbCaCtl = ctlExit;
epicsEventSignal(workListEvent);
epicsEventMustWait(startStopEvent);
+ if(dbCaWorker)
+ epicsThreadMustJoin(dbCaWorker);
}
static void dbCaLinkInitImpl(int isolate)
{
+ epicsThreadOpts opts;
+ epicsThreadOptsDefaults(&opts);
+ opts.stackSize = epicsThreadGetStackSize(epicsThreadStackBig);
+ opts.priority = epicsThreadPriorityMedium;
+ opts.joinable = 1;
+
dbServiceIsolate = isolate;
dbServiceIOInit();
@@ -274,9 +283,8 @@ static void dbCaLinkInitImpl(int isolate)
startStopEvent = epicsEventMustCreate(epicsEventEmpty);
dbCaCtl = ctlPause;
- epicsThreadCreate("dbCaLink", epicsThreadPriorityMedium,
- epicsThreadGetStackSize(epicsThreadStackBig),
- dbCaTask, NULL);
+ dbCaWorker = epicsThreadCreateOpt("dbCaLink", dbCaTask, NULL, &opts);
+ /* wait for worker to startup and initialize dbCaClientContext */
epicsEventMustWait(startStopEvent);
}
From 37a76b433a9e7d5a8d26a13fd21ad62f20a0c1c1 Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Sun, 23 Jun 2019 10:36:52 -0700
Subject: [PATCH 052/281] dbEvent simplify db_close_events() with join
---
modules/database/src/ioc/db/dbEvent.c | 71 ++++++++-------------------
1 file changed, 20 insertions(+), 51 deletions(-)
diff --git a/modules/database/src/ioc/db/dbEvent.c b/modules/database/src/ioc/db/dbEvent.c
index 9304f99b2..48dc9012b 100644
--- a/modules/database/src/ioc/db/dbEvent.c
+++ b/modules/database/src/ioc/db/dbEvent.c
@@ -78,7 +78,6 @@ 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 */
@@ -117,8 +116,6 @@ 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,10 +257,6 @@ dbEventCtx db_init_events (void)
{
struct event_user * evUser;
- if (!stopSync) {
- stopSync = epicsMutexMustCreate();
- }
-
if (!dbevEventUserFreeList) {
freeListInitPvt(&dbevEventUserFreeList,
sizeof(struct event_user),8);
@@ -287,9 +280,6 @@ 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)
@@ -304,9 +294,6 @@ 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;
@@ -321,8 +308,6 @@ fail:
epicsEventDestroy (evUser->ppendsem);
if(evUser->pflush_sem)
epicsEventDestroy (evUser->pflush_sem);
- if(evUser->pexitsem)
- epicsEventDestroy (evUser->pexitsem);
freeListFree(dbevEventUserFreeList,evUser);
return NULL;
}
@@ -343,7 +328,6 @@ epicsShareFunc void db_cleanup_events(void)
dbevFieldLogFreeList = NULL;
}
- /* intentionally leak stopSync to avoid possible shutdown races */
/*
* DB_CLOSE_EVENTS()
*
@@ -365,30 +349,15 @@ void db_close_events (dbEventCtx ctx)
* hazardous to the system's health.
*/
epicsMutexMustLock ( evUser->lock );
- 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 );
- }
-
+ evUser->pendexit = TRUE;
epicsMutexUnlock ( evUser->lock );
- epicsMutexMustLock (stopSync);
+ /* notify the waiting task */
+ epicsEventSignal(evUser->ppendsem);
- epicsEventDestroy(evUser->pexitsem);
- epicsEventDestroy(evUser->ppendsem);
- epicsEventDestroy(evUser->pflush_sem);
- epicsMutexDestroy(evUser->lock);
-
- epicsMutexUnlock (stopSync);
-
- freeListFree(dbevEventUserFreeList, evUser);
+ if(evUser->taskid)
+ epicsThreadMustJoin(evUser->taskid);
+ /* evUser has been deleted by the worker */
}
/*
@@ -1068,17 +1037,14 @@ static void event_task (void *pParm)
}
}
+ epicsEventDestroy(evUser->ppendsem);
+ epicsEventDestroy(evUser->pflush_sem);
+ epicsMutexDestroy(evUser->lock);
+
+ freeListFree(dbevEventUserFreeList, evUser);
+
taskwdRemove(epicsThreadGetIdSelf());
- /* use stopSync to ensure pexitsem is not destroy'd
- * until epicsEventSignal() has returned.
- */
- epicsMutexMustLock (stopSync);
-
- epicsEventSignal(evUser->pexitsem);
-
- epicsMutexUnlock(stopSync);
-
return;
}
@@ -1090,6 +1056,12 @@ int db_start_events (
void *init_func_arg, unsigned osiPriority )
{
struct event_user * const evUser = (struct event_user *) ctx;
+ epicsThreadOpts opts;
+
+ epicsThreadOptsDefaults(&opts);
+ opts.stackSize = epicsThreadGetStackSize(epicsThreadStackMedium);
+ opts.priority = osiPriority;
+ opts.joinable = 1;
epicsMutexMustLock ( evUser->lock );
@@ -1107,15 +1079,12 @@ 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;
}
- evUser->pendexit = FALSE;
epicsMutexUnlock ( evUser->lock );
return DB_EVENT_OK;
}
From 38999a971f78ebacdd2bad69d3886d9ee4c16ec7 Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Sun, 23 Jun 2019 15:21:56 -0700
Subject: [PATCH 053/281] document epicsThread.h
---
modules/libcom/src/osi/epicsThread.h | 128 ++++++++++++++++++++++++++-
1 file changed, 124 insertions(+), 4 deletions(-)
diff --git a/modules/libcom/src/osi/epicsThread.h b/modules/libcom/src/osi/epicsThread.h
index 61ab323b1..694ac94c7 100644
--- a/modules/libcom/src/osi/epicsThread.h
+++ b/modules/libcom/src/osi/epicsThread.h
@@ -45,6 +45,7 @@ typedef enum {
epicsThreadBooleanStatusFail, epicsThreadBooleanStatusSuccess
} epicsThreadBooleanStatus;
+/** Lookup target specific default stack size */
epicsShareFunc unsigned int epicsShareAPI epicsThreadGetStackSize(
epicsThreadStackSizeClass size);
@@ -54,6 +55,20 @@ typedef struct epicsThreadOSD *epicsThreadId;
typedef epicsThreadId epicsThreadOnceId;
#define EPICS_THREAD_ONCE_INIT 0
+/** Perform one-time initialization.
+ *
+ * Run the provided function if it has not run, and is not running.
+ *
+ * @post The provided function has been run.
+ *
+ * @code
+ * static epicsThreadOnceId onceId = EPICS_THREAD_ONCE_INIT;
+ * static void myInitFunc(void *arg) { ... }
+ * static void some Function(void) {
+ * epicsThreadOnce(&onceId, &myInitFunc, NULL);
+ * }
+ * @endcode
+ */
epicsShareFunc void epicsShareAPI epicsThreadOnce(
epicsThreadOnceId *id, EPICSTHREADFUNC, void *arg);
@@ -65,6 +80,7 @@ epicsShareFunc void epicsThreadRealtimeLock(void);
epicsShareFunc void epicsShareAPI epicsThreadExitMain(void);
+/** For use with epicsThreadCreateOpt() */
typedef struct epicsThreadOpts {
/** Thread priority in OSI range (cf. epicsThreadPriority*) */
unsigned int priority;
@@ -73,57 +89,123 @@ typedef struct epicsThreadOpts {
* @warning Do not pass enum epicsThreadStackSizeClass directly!
*/
unsigned int stackSize;
- /** Should thread be joinable? (default (0) is not joinable). */
+ /** Should thread be joinable? (default (0) is not joinable).
+ * If joinable=1, then epicsThreadMustJoin() must be called for cleanup thread resources.
+ */
unsigned int joinable;
} epicsThreadOpts;
+/** Fill in target specific default values. */
epicsShareFunc void epicsThreadOptsDefaults(epicsThreadOpts *opts);
+/** @brief Allocate and start a new OS thread.
+ * @param name A name describing this thread. Appears in various log and error message.
+ * @param funptr The thread main function.
+ * @param parm Passed to thread main function.
+ * @param opts Modifiers for the new thread, or NULL to use target specific defaults.
+ * @return NULL on error
+ */
epicsShareFunc epicsThreadId epicsThreadCreateOpt (
const char * name,
EPICSTHREADFUNC funptr, void * parm,
const epicsThreadOpts *opts );
+/** Short-hand for epicsThreadCreateOpt() to create an un-joinable thread. */
epicsShareFunc epicsThreadId epicsShareAPI epicsThreadCreate (
const char * name, unsigned int priority, unsigned int stackSize,
EPICSTHREADFUNC funptr,void * parm );
+/** Short-hand for epicsThreadCreateOpt() to create an un-joinable thread.
+ * On error calls cantProceed()
+ */
epicsShareFunc epicsThreadId epicsShareAPI epicsThreadMustCreate (
const char * name, unsigned int priority, unsigned int stackSize,
EPICSTHREADFUNC funptr,void * parm );
+/** Wait for a joinable thread to exit (return from its main function */
epicsShareFunc void epicsThreadMustJoin(epicsThreadId id);
+/** Block the current thread until epicsThreadResume(). */
epicsShareFunc void epicsShareAPI epicsThreadSuspendSelf(void);
+/** Resume a thread suspended with epicsThreadSuspendSelf() */
epicsShareFunc void epicsShareAPI epicsThreadResume(epicsThreadId id);
+/** Return thread OSI priority */
epicsShareFunc unsigned int epicsShareAPI epicsThreadGetPriority(
epicsThreadId id);
+/** Return thread OSI priority */
epicsShareFunc unsigned int epicsShareAPI epicsThreadGetPrioritySelf(void);
+/** Change OSI priority of target thread. */
epicsShareFunc void epicsShareAPI epicsThreadSetPriority(
epicsThreadId id,unsigned int priority);
+/** Lookup the next usage OSI priority such that priority > *pPriorityJustBelow
+ * if this is possible with the current target configuration and privlages.
+ */
epicsShareFunc epicsThreadBooleanStatus epicsShareAPI
epicsThreadHighestPriorityLevelBelow (
unsigned int priority, unsigned *pPriorityJustBelow);
+/** Lookup the next usage OSI priority such that priority < *pPriorityJustBelow
+ * if this is possible with the current target configuration and privlages.
+ */
epicsShareFunc epicsThreadBooleanStatus epicsShareAPI
epicsThreadLowestPriorityLevelAbove (
unsigned int priority, unsigned *pPriorityJustAbove);
+/** Test if two thread IDs actually refer to the same OS thread */
epicsShareFunc int epicsShareAPI epicsThreadIsEqual(
epicsThreadId id1, epicsThreadId id2);
+/** Test if thread has been suspended with epicsThreadSuspendSelf() */
epicsShareFunc int epicsShareAPI epicsThreadIsSuspended(epicsThreadId id);
+/** @brief Block the calling thread for at least the specified time.
+ * @param seconds Time to wait in seconds. Values <=0 blocks for the shortest possible time.
+ */
epicsShareFunc void epicsShareAPI epicsThreadSleep(double seconds);
+/** @brief Query a value approximating the OS timer/scheduler resolution.
+ * @return A value in seconds >=0
+ *
+ * @warning On targets other than vxWorks and RTEMS, the quantum value often isn't
+ * meaningful. Use of this function is discouraged in portable code.
+ */
epicsShareFunc double epicsShareAPI epicsThreadSleepQuantum(void);
+/** Find an epicsThreadId associated with the current thread.
+ * For non-EPICS threads, a new epicsThreadId may be allocated.
+ */
epicsShareFunc epicsThreadId epicsShareAPI epicsThreadGetIdSelf(void);
+/** Attempt to find the first instance of a thread by name.
+ * @return An epicsThreadId, or NULL if no such thread is currently running.
+ * Note that a non-NULL ID may still be invalid if this call races
+ * with thread exit.
+ *
+ * @warning Safe use of this function requires external knowledge that this
+ * thread will not return.
+ */
epicsShareFunc epicsThreadId epicsShareAPI epicsThreadGetId(const char *name);
+/** Return a value approximating the number of threads which this target
+ * can run in parallel. This value is advisory.
+ * @return >=1
+ */
epicsShareFunc int epicsThreadGetCPUs(void);
+/** Return the name of the current thread.
+ *
+ * @return Never NULL. Storage lifetime tied to epicsThreadId.
+ *
+ * This is either a copy of the string passed to epicsThread*Create*(),
+ * or an arbitrary unique string for non-EPICS threads.
+ */
epicsShareFunc const char * epicsShareAPI epicsThreadGetNameSelf(void);
-/* For epicsThreadGetName name is guaranteed to be null terminated */
-/* size is size of buffer to hold name (including terminator) */
-/* Failure results in an empty string stored in name */
+/** Copy out the thread name into the provided buffer.
+ *
+ * Guaranteed to be null terminated.
+ * size is number of bytes in buffer to hold name (including terminator).
+ * Failure results in an empty string stored in name.
+ */
epicsShareFunc void epicsShareAPI epicsThreadGetName(
epicsThreadId id, char *name, size_t size);
epicsShareFunc int epicsShareAPI epicsThreadIsOkToBlock(void);
epicsShareFunc void epicsShareAPI epicsThreadSetOkToBlock(int isOkToBlock);
+/** Print to stdout information about all running EPICS threads.
+ * @param level 0 prints minimal output. Higher values print more details.
+ */
epicsShareFunc void epicsShareAPI epicsThreadShowAll(unsigned int level);
+/** Print info about a single EPICS thread. */
epicsShareFunc void epicsShareAPI epicsThreadShow(
epicsThreadId id,unsigned int level);
@@ -134,10 +216,17 @@ epicsShareFunc int epicsThreadHookDelete(EPICS_THREAD_HOOK_ROUTINE hook);
epicsShareFunc void epicsThreadHooksShow(void);
epicsShareFunc void epicsThreadMap(EPICS_THREAD_HOOK_ROUTINE func);
+/** Thread local storage */
typedef struct epicsThreadPrivateOSD * epicsThreadPrivateId;
+/** Allocate a new thread local variable.
+ * This variable will initially hold NULL for each thread.
+ */
epicsShareFunc epicsThreadPrivateId epicsShareAPI epicsThreadPrivateCreate(void);
+/** Free a thread local variable */
epicsShareFunc void epicsShareAPI epicsThreadPrivateDelete(epicsThreadPrivateId id);
+/** Update thread local variable */
epicsShareFunc void epicsShareAPI epicsThreadPrivateSet(epicsThreadPrivateId,void *);
+/** Fetch the current value of a thread local variable */
epicsShareFunc void * epicsShareAPI epicsThreadPrivateGet(epicsThreadPrivateId);
#ifdef __cplusplus
@@ -149,33 +238,64 @@ epicsShareFunc void * epicsShareAPI epicsThreadPrivateGet(epicsThreadPrivateId);
#include "epicsEvent.h"
#include "epicsMutex.h"
+//! Interface used with class epicsThread
class epicsShareClass epicsThreadRunable {
public:
virtual ~epicsThreadRunable () = 0;
+ //! Thread main function.
+ //! C++ exceptions which propagate from this method will be caught and a warning printed.
+ //! No other action is taken.
virtual void run () = 0;
+ //! Optional. Called via epicsThread::show()
virtual void show ( unsigned int level ) const;
};
extern "C" void epicsThreadCallEntryPoint ( void * );
+/** @brief An OS thread
+ *
+ * A wrapper around the epicsThread* C API.
+ *
+ * @note Threads must be start() ed.
+ */
class epicsShareClass epicsThread {
public:
+ /** Create a new thread with the provided information.
+ *
+ * cf. epicsThreadOpts
+ * @note Threads must be start() ed.
+ * @throws epicsThread::unableToCreateThread on error.
+ */
epicsThread ( epicsThreadRunable &,const char *name, unsigned int stackSize,
unsigned int priority=epicsThreadPriorityLow );
~epicsThread () throw ();
+ //! Actually start the thread.
void start () throw ();
+ //! Wait for the thread epicsRunnable::run() to return.
void exitWait () throw ();
+ //! Wait for the thread epicsRunnable::run() to return.
+ //! @param delay Wait up to this many seconds.
+ //! @returns true if run() returned. false on timeout.
bool exitWait ( const double delay ) throw ();
+ //! @throws A special exitException which will be caught and ignored.
+ //! @note This exitException doesn't not derive from std::exception
static void exit ();
+ //! cf. epicsThreadResume()
void resume () throw ();
+ //! cf. epicsThreadGetName();
void getName ( char * name, size_t size ) const throw ();
+ //! cf. epicsThreadGetIdSelf()()
epicsThreadId getId () const throw ();
+ //! cf. epicsThreadGetPriority()
unsigned int getPriority () const throw ();
+ //! cf. epicsThreadSetPriority()
void setPriority ( unsigned int ) throw ();
bool priorityIsEqual ( const epicsThread & ) const throw ();
bool isSuspended () const throw ();
+ //! @return true if call through this thread's epicsRunnable::run()
bool isCurrentThread () const throw ();
bool operator == ( const epicsThread & ) const throw ();
+ //! Say something interesting about this thread to stdout.
void show ( unsigned level ) const throw ();
/* these operate on the current thread */
From 57d2b143e9ab2a547da14b74fbb4c85ec347da15 Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Sun, 23 Jun 2019 15:31:35 -0700
Subject: [PATCH 054/281] update release notes
---
documentation/RELEASE_NOTES.html | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html
index 63f8880a7..fa9727aff 100644
--- a/documentation/RELEASE_NOTES.html
+++ b/documentation/RELEASE_NOTES.html
@@ -26,6 +26,28 @@ release.
-->
+
Add epicsThreadCreateOpt() and epicsThreadMustJoin()
+
+
epicsThreadCreateOpt() is an alternative to epicsThreadCreate() which
+passes some arguments via a structure (struct epicsThreadOpts).
+This epicsThreadOpts* may be NULL to use target specific
+defaults. Caller wishing to provide thread options should first call
+epicsThreadOptsDefaults() to fill in the defaults.
If the new epicsThreadOpts::joinable option flag is set (not the default),
+then epicsThreadMustJoin() needs to be called to free up thread resources.
+This function will block until the thread main function has returned.
+
Launchpad Bugs
The list of tracked bugs fixed in this release can be found on the
From 93a96e33c09e6377e112bc3f124d06476f75bde9 Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Sun, 23 Jun 2019 16:49:41 -0700
Subject: [PATCH 055/281] dbChArrTest use dbUnitTest.h
---
modules/database/test/ioc/db/dbChArrTest.cpp | 41 ++++++--------------
1 file changed, 12 insertions(+), 29 deletions(-)
diff --git a/modules/database/test/ioc/db/dbChArrTest.cpp b/modules/database/test/ioc/db/dbChArrTest.cpp
index 8a788bed6..8255fdc39 100644
--- a/modules/database/test/ioc/db/dbChArrTest.cpp
+++ b/modules/database/test/ioc/db/dbChArrTest.cpp
@@ -36,7 +36,7 @@
#include "iocInit.h"
#include "iocsh.h"
#include "dbChannel.h"
-#include "epicsUnitTest.h"
+#include "dbUnitTest.h"
#include "testMain.h"
#include "osiFileName.h"
@@ -197,50 +197,33 @@ static void check(short dbr_type) {
dbChannelDelete(pch);
}
-static dbEventCtx evtctx;
-
-extern "C" {
-static void dbChArrTestCleanup(void* junk)
-{
- dbFreeBase(pdbbase);
- registryFree();
- pdbbase=0;
-
- db_close_events(evtctx);
-
- dbmfFreeChunks();
-}
-}
-
MAIN(dbChArrTest)
{
testPlan(102);
/* Prepare the IOC */
+ testdbPrepare();
epicsEnvSet("EPICS_CA_SERVER_PORT", server_port);
- if (dbReadDatabase(&pdbbase, "dbChArrTest.dbd",
- "." OSI_PATH_LIST_SEPARATOR ".." OSI_PATH_LIST_SEPARATOR
- "../O.Common" OSI_PATH_LIST_SEPARATOR "O.Common", NULL))
- testAbort("Database description not loaded");
+ testdbReadDatabase("dbChArrTest.dbd",
+ "." OSI_PATH_LIST_SEPARATOR ".." OSI_PATH_LIST_SEPARATOR
+ "../O.Common" OSI_PATH_LIST_SEPARATOR "O.Common", NULL);
dbChArrTest_registerRecordDeviceDriver(pdbbase);
- if (dbReadDatabase(&pdbbase, "dbChArrTest.db",
- "." OSI_PATH_LIST_SEPARATOR "..", NULL))
- testAbort("Test database not loaded");
+ testdbReadDatabase("dbChArrTest.db",
+ "." OSI_PATH_LIST_SEPARATOR "..", NULL);
- epicsAtExit(&dbChArrTestCleanup,NULL);
-
- /* Start the IOC */
-
- iocInit();
- evtctx = db_init_events();
+ testIocInitOk();
check(DBR_LONG);
check(DBR_DOUBLE);
check(DBR_STRING);
+ testIocShutdownOk();
+
+ testdbCleanup();
+
return testDone();
}
From 14440b2d9d30a25addac00c4e3d227b63def6ecb Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Sun, 23 Jun 2019 19:28:03 -0700
Subject: [PATCH 056/281] more RTEMS join
---
modules/libcom/src/osi/os/RTEMS/osdThread.c | 7 ++++---
modules/libcom/test/epicsThreadTest.cpp | 7 +++++++
2 files changed, 11 insertions(+), 3 deletions(-)
diff --git a/modules/libcom/src/osi/os/RTEMS/osdThread.c b/modules/libcom/src/osi/os/RTEMS/osdThread.c
index b23f66b8f..fdc31759f 100644
--- a/modules/libcom/src/osi/os/RTEMS/osdThread.c
+++ b/modules/libcom/src/osi/os/RTEMS/osdThread.c
@@ -366,7 +366,7 @@ threadMustCreate (const char *name,
void epicsThreadMustJoin(epicsThreadId id)
{
rtems_id target_tid = (rtems_id)id, self_tid;
- struct taskVar *v;
+ struct taskVar *v = 0;
rtems_task_ident (RTEMS_SELF, 0, &self_tid);
@@ -375,10 +375,11 @@ void epicsThreadMustJoin(epicsThreadId id)
rtems_task_get_note (target_tid, RTEMS_NOTEPAD_TASKVAR, ¬e);
v = (void *)note;
}
+ /* 'v' may be NULL if 'id' represents a non-EPICS thread other than _main_. */
- if(!v->joinable) {
+ if(!v || !v->joinable) {
if(epicsThreadGetIdSelf()==id) {
- errlogPrintf("Warning: %s thread self-join of unjoinable\n", v->name);
+ errlogPrintf("Warning: %s thread self-join of unjoinable\n", v ? v->name : "non-EPICS thread");
} else {
/* try to error nicely, however in all likelyhood de-ref of
diff --git a/modules/libcom/test/epicsThreadTest.cpp b/modules/libcom/test/epicsThreadTest.cpp
index 08d7529fa..edaada914 100644
--- a/modules/libcom/test/epicsThreadTest.cpp
+++ b/modules/libcom/test/epicsThreadTest.cpp
@@ -172,10 +172,17 @@ MAIN(epicsThreadTest)
unsigned int ncpus = epicsThreadGetCPUs();
testDiag("System has %u CPUs", ncpus);
testOk1(ncpus > 0);
+ testDiag("main() thread %p", epicsThreadGetIdSelf());
testMyThread();
testSelfJoin();
testOkToBlock();
+ // attempt to self-join from a non-EPICS thread
+ // to make sure it does nothing as expected
+ eltc(0);
+ epicsThreadMustJoin(epicsThreadGetIdSelf());
+ eltc(1);
+
return testDone();
}
From a81e261e23ac744c297a6ebc882b473a5e46cdf0 Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Sun, 23 Jun 2019 19:43:34 -0700
Subject: [PATCH 057/281] iocsh trap arg. parsing errors
---
modules/libcom/src/iocsh/iocsh.cpp | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/modules/libcom/src/iocsh/iocsh.cpp b/modules/libcom/src/iocsh/iocsh.cpp
index 26ecb5b01..7392bca42 100644
--- a/modules/libcom/src/iocsh/iocsh.cpp
+++ b/modules/libcom/src/iocsh/iocsh.cpp
@@ -868,6 +868,9 @@ iocshBody (const char *pathname, const char *commandLine, const char *macros)
* Set up redirection
*/
if ((openRedirect(filename, lineno, redirects) == 0) && (argc > 0)) {
+ // error unless a function is actually called.
+ // handles command not-found and arg parsing errors.
+ scope.errored = true;
/*
* Look up command
*/
@@ -924,7 +927,6 @@ iocshBody (const char *pathname, const char *commandLine, const char *macros)
}
else {
showError(filename, lineno, "Command %s not found.", argv[0]);
- scope.errored = true;
}
}
stopRedirect(filename, lineno, redirects);
From 87d5ca1619ec99055c5fdd0ecfcf7c8fc9edca57 Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Mon, 24 Jun 2019 10:35:25 -0700
Subject: [PATCH 058/281] rpath $ORIGIN doc
---
configure/CONFIG_SITE | 2 +-
src/tools/makeRPath.py | 12 ++++++++++--
2 files changed, 11 insertions(+), 3 deletions(-)
diff --git a/configure/CONFIG_SITE b/configure/CONFIG_SITE
index 49f82c7a8..a39911783 100644
--- a/configure/CONFIG_SITE
+++ b/configure/CONFIG_SITE
@@ -172,7 +172,7 @@ GCC_PIPE = NO
# Must be either YES, NO, or ORIGIN. If you set this to NO you must also provide a
# way for Base executables to find their shared libraries when they are
# run at build-time, e.g. set the LD_LIBRARY_PATH environment variable.
-# ORIGIN is Linux specific.
+# ORIGIN is a feature of the ELF executable format used by Linux, freebsd, and solaris.
LINKER_USE_RPATH = YES
# Only used when LINKER_USE_RPATH=ORIGIN
diff --git a/src/tools/makeRPath.py b/src/tools/makeRPath.py
index 5d151a41a..eeda8ab55 100644
--- a/src/tools/makeRPath.py
+++ b/src/tools/makeRPath.py
@@ -10,9 +10,17 @@ from argparse import ArgumentParser
if os.environ.get('EPICS_DEBUG_RPATH','')=='YES':
sys.stderr.write('%s'%sys.argv)
-P = ArgumentParser()
+P = ArgumentParser(description='''Compute and output -rpath entries for each of the given paths.
+ Paths under --root will be computed as relative to --final .''',
+epilog='''
+eg. A library to be placed in /build/lib and linked against libraries in
+'/build/lib', '/build/module/lib', and '/other/lib' would pass:
+
+ "makeRPath.py -F /build/lib -R /build /build/lib /build/module/lib /other/lib"
+which prints "-Wl,-rpath,$ORIGIN/. -Wl,-rpath,$ORIGIN/../module/lib -Wl,-rpath,/other/lib"
+''')
P.add_argument('-F','--final',default=os.getcwd(), help='Final install location for ELF file')
-P.add_argument('-R','--root',default='/')
+P.add_argument('-R','--root',default='/', help='Root of relocatable tree.')
P.add_argument('-O', '--origin', default='$ORIGIN')
P.add_argument('path', nargs='*')
args = P.parse_args()
From ea1b208c33295f0b81f369bcb638a73c5a83c439 Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Mon, 24 Jun 2019 13:23:28 -0700
Subject: [PATCH 059/281] redo softIoc to be more c++y
---
modules/database/src/std/softIoc/softMain.cpp | 419 ++++++++----------
1 file changed, 197 insertions(+), 222 deletions(-)
diff --git a/modules/database/src/std/softIoc/softMain.cpp b/modules/database/src/std/softIoc/softMain.cpp
index 01ef19b2f..bc945c80e 100644
--- a/modules/database/src/std/softIoc/softMain.cpp
+++ b/modules/database/src/std/softIoc/softMain.cpp
@@ -9,55 +9,12 @@
/* Author: Andrew Johnson Date: 2003-04-08 */
-/* Usage:
- * softIoc [-D softIoc.dbd] [-h] [-S] [-s] [-a ascf]
- * [-m macro=value,macro2=value2] [-d file.db]
- * [-x prefix] [st.cmd]
- *
- * If used the -D option must come first, and specify the
- * path to the softIoc.dbd file. The compile-time install
- * location is saved in the binary as a default.
- *
- * Usage information will be printed if -h is given, then
- * the program will exit normally.
- *
- * The -S option prevents an interactive shell being started
- * after all arguments have been processed.
- *
- * Previous versions accepted a -s option to cause a shell
- * to be started; this option is still accepted but ignored
- * since a command shell is now started by default.
- *
- * Access Security can be enabled with the -a option giving
- * the name of the configuration file; if any macros were
- * set with -m before the -a option was given, they will be
- * used as access security substitution macros.
- *
- * Any number of -m and -d arguments can be interspersed;
- * the macros are applied to the following .db files. Each
- * later -m option causes earlier macros to be discarded.
- *
- * The -x option loads the softIocExit.db with the macro
- * IOC set to the string provided. This database contains
- * a subroutine record named $(IOC):exit which has its field
- * SNAM set to "exit". When this record is processed, the
- * subroutine that runs will call epicsExit() with the value
- * of the field A determining whether the exit status is
- * EXIT_SUCCESS if (A == 0.0) or EXIT_FAILURE (A != 0.0).
- *
- * A st.cmd file is optional. If any databases were loaded
- * the st.cmd file will be run *after* iocInit. To perform
- * iocsh commands before iocInit, all database loading must
- * be performed by the script itself, or by the user from
- * the interactive IOC shell.
- */
-
-#include
-#include
-#include
-#include
-#include
+#include
+#include
+#include
+#include
+#include
#include "registryFunction.h"
#include "epicsThread.h"
#include "epicsExit.h"
@@ -74,6 +31,12 @@
extern "C" int softIoc_registerRecordDeviceDriver(struct dbBase *pdbbase);
+#ifndef EPICS_BASE
+// so IDEs knows EPICS_BASE is a string constant
+# define EPICS_BASE "/"
+# error -DEPICS_BASE required
+#endif
+
#define DBD_BASE "dbd/softIoc.dbd"
#define EXIT_BASE "db/softIocExit.db"
#define DBD_FILE_REL "../../" DBD_BASE
@@ -81,190 +44,202 @@ extern "C" int softIoc_registerRecordDeviceDriver(struct dbBase *pdbbase);
#define DBD_FILE EPICS_BASE "/" DBD_BASE
#define EXIT_FILE EPICS_BASE "/" EXIT_BASE
-const char *arg0;
-const char *base_dbd = DBD_FILE;
-const char *exit_db = EXIT_FILE;
-
-static void preparePath(void)
-{
- FILE *fp;
- char *prefix = epicsGetExecDir();
- char *dbd, *exit;
- if(!prefix) return;
-
- dbd = (char*)malloc(strlen(prefix) + strlen(DBD_FILE_REL) + 1);
- if(dbd) {
- dbd[0] = '\0';
- strcat(dbd, prefix);
- strcat(dbd, DBD_FILE_REL);
- printf("Testing '%s'\n", dbd);
- if((fp = fopen(dbd, "rb"))!=NULL) {
- fclose(fp);
- base_dbd = dbd;
- }
- }
-
- exit = (char*)malloc(strlen(prefix) + strlen(EXIT_FILE_REL) + 1);
- if(exit) {
- exit[0] = '\0';
- strcat(exit, prefix);
- strcat(exit, EXIT_FILE_REL);
- if((fp = fopen(exit, "rb"))!=NULL) {
- fclose(fp);
- exit_db = exit;
- }
- }
-}
+namespace {
static void exitSubroutine(subRecord *precord) {
epicsExitLater((precord->a == 0.0) ? EXIT_SUCCESS : EXIT_FAILURE);
}
-static void usage(int status) {
- printf("Usage: %s [-D softIoc.dbd] [-h] [-S] [-a ascf]\n", arg0);
- puts("\t[-m macro=value,macro2=value2] [-d file.db]");
- puts("\t[-x prefix] [st.cmd]");
- puts("Compiled-in path to softIoc.dbd is:");
- printf("\t%s\n", base_dbd);
- epicsExit(status);
+void usage(const char *arg0, const std::string& base_dbd) {
+ std::cout<<"Usage: "< If used, must come first. Specify the path to the softIoc.dbdfile."
+ " The compile-time install location is saved in the binary as a default.\n"
+ "\n"
+ " -h Print this mesage and exit.\n"
+ "\n"
+ " -S Prevents an interactive shell being started.\n"
+ "\n"
+ " -s Previously caused a shell to be started. Now accepted and ignored.\n"
+ "\n"
+ " -a Access Security configuration file. Macro substitution is\n"
+ " performed.\n"
+ "\n"
+ " -m =,... Set/replace macro definitions used by subsequent -d and\n"
+ " -a.\n"
+ "\n"
+ " -d Load records from file (dbLoadRecords). Macro substitution is\n"
+ " performed.\n"
+ "\n"
+ " -x Load softIocExit.db. Provides a record \":exit\".\n"
+ " Put 0 to exit with success, or non-zero to exit with an error.\n"
+ "\n"
+ "Any number of -m and -d arguments can be interspersed; the macros are applied\n"
+ "to the following .db files. Each later -m option causes earlier macros to be\n"
+ "discarded.\n"
+ "\n"
+ "A st.cmd file is optional. If any databases were loaded the st.cmd file will\n"
+ "be run *after* iocInit. To perform iocsh commands before iocInit, all database\n"
+ "loading must be performed by the script itself, or by the user from the\n"
+ "interactive IOC shell.\n"
+ "\n"
+ "Compiled-in path to softIoc.dbd is:\n"
+ "\t"<(base_dbd);
- char *macros = NULL;
- char xmacro[PVNAME_STRINGSZ + 4];
- int startIocsh = 1; /* default = start shell */
- int loadedDb = 0;
-
- arg0 = strrchr(*argv, '/');
- if (!arg0) {
- arg0 = *argv;
- } else {
- ++arg0; /* skip the '/' */
- }
-
- --argc, ++argv;
-
- /* Do this here in case the dbd file not available */
- if (argc>0 && **argv=='-' && (*argv)[1]=='h') {
- usage(EXIT_SUCCESS);
- }
-
- if (argc>1 && **argv=='-' && (*argv)[1]=='D') {
- dbd_file = *++argv;
- argc -= 2;
- ++argv;
- }
-
- if (dbLoadDatabase(dbd_file, NULL, NULL)) {
- epicsExit(EXIT_FAILURE);
- }
-
- softIoc_registerRecordDeviceDriver(pdbbase);
- registryFunctionAdd("exit", (REGISTRYFUNCTION) exitSubroutine);
+ try {
+ std::string dbd_file(DBD_FILE),
+ exit_file(EXIT_FILE),
+ macros, // scratch space for macros (may be given more than once)
+ xmacro;
+ bool interactive = true;
+ bool loadedDb = false;
- while (argc>1 && **argv == '-') {
- switch ((*argv)[1]) {
- case 'a':
- if (macros) asSetSubstitutions(macros);
- asSetFilename(*++argv);
- --argc;
- break;
-
- case 'd':
- if (dbLoadRecords(*++argv, macros)) {
- epicsExit(EXIT_FAILURE);
- }
- loadedDb = 1;
- --argc;
- break;
-
- case 'h':
- usage(EXIT_SUCCESS);
-
- case 'm':
- macros = *++argv;
- --argc;
- break;
-
- case 'S':
- startIocsh = 0;
- break;
-
- case 's':
- break;
-
- case 'x':
- epicsSnprintf(xmacro, sizeof xmacro, "IOC=%s", *++argv);
- if (dbLoadRecords(exit_db, xmacro)) {
- epicsExit(EXIT_FAILURE);
- }
- loadedDb = 1;
- --argc;
- break;
-
- default:
- printf("%s: option '%s' not recognized\n", arg0, *argv);
- usage(EXIT_FAILURE);
- }
- --argc;
- ++argv;
+ // attempt to compute relative paths
+ {
+ std::string prefix;
+ char *cprefix = epicsGetExecDir();
+ if(cprefix) {
+ try {
+ prefix = cprefix;
+ free(cprefix);
+ } catch(...) {
+ free(cprefix);
+ throw;
+ }
+ }
+
+ dbd_file = prefix + DBD_FILE_REL;
+ exit_file = prefix + EXIT_FILE_REL;
+ }
+
+ int opt;
+
+ while ((opt = getopt(argc, argv, "ha:d:m:Ssx:")) != -1) {
+ switch (opt) {
+ case 'h': /* Print usage */
+ usage(argv[0], dbd_file);
+ epicsExit(0);
+ return 0;
+ default:
+ usage(argv[0], dbd_file);
+ std::cerr<<"Unknown argument: -"<0 && **argv=='-') {
- switch((*argv)[1]) {
- case 'a':
- case 'd':
- case 'm':
- case 'x':
- printf("%s: missing argument to option '%s'\n", arg0, *argv);
- usage(EXIT_FAILURE);
-
- case 'h':
- usage(EXIT_SUCCESS);
-
- case 'S':
- startIocsh = 0;
- break;
-
- case 's':
- break;
-
- default:
- printf("%s: option '%s' not recognized\n", arg0, *argv);
- usage(EXIT_FAILURE);
- }
- --argc;
- ++argv;
- }
-
- if (loadedDb) {
- iocInit();
- epicsThreadSleep(0.2);
- }
-
- /* run user's startup script */
- if (argc>0) {
- if (iocsh(*argv)) epicsExit(EXIT_FAILURE);
- epicsThreadSleep(0.2);
- loadedDb = 1; /* Give it the benefit of the doubt... */
- }
-
- /* start an interactive shell if it was requested */
- if (startIocsh) {
- iocsh(NULL);
- } else {
- if (loadedDb) {
- epicsThreadExitMain();
- } else {
- printf("%s: Nothing to do!\n", arg0);
- usage(EXIT_FAILURE);
- }
- }
- epicsExit(EXIT_SUCCESS);
- /*Note that the following statement will never be executed*/
- return 0;
}
From 784d619bdee4653f8412f664b7338fe42f67e047 Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Mon, 24 Jun 2019 15:41:48 -0700
Subject: [PATCH 060/281] makeRPath allow multiple root directories
Allows handling of complex situations like a package
build where some libraries are in a staging area,
but will be copied to the same final location.
eg.
LINKER_ORIGIN_ROOT=/usr/lib/epics:/build/mymodule
Where build TOP is /build/mymodule
---
configure/CONFIG_SITE | 5 ++--
src/tools/makeRPath.py | 59 +++++++++++++++++++++++++++++++++---------
2 files changed, 50 insertions(+), 14 deletions(-)
diff --git a/configure/CONFIG_SITE b/configure/CONFIG_SITE
index a39911783..c46703f84 100644
--- a/configure/CONFIG_SITE
+++ b/configure/CONFIG_SITE
@@ -176,9 +176,10 @@ GCC_PIPE = NO
LINKER_USE_RPATH = YES
# Only used when LINKER_USE_RPATH=ORIGIN
-# The build time root of the relocatable tree.
-# Linking to libraries under this root directory will be relative.
+# The build time root(s) of the relocatable tree (separate multiple w/ ':').
+# Linking to libraries under any root directory will be relative.
# Linking to libraries outside of this root will be absolute.
+# All root directories are considered to be the same.
LINKER_ORIGIN_ROOT = $(INSTALL_LOCATION)
# Overrides for the settings above may appear in a CONFIG_SITE.local file
diff --git a/src/tools/makeRPath.py b/src/tools/makeRPath.py
index eeda8ab55..000b8b450 100644
--- a/src/tools/makeRPath.py
+++ b/src/tools/makeRPath.py
@@ -4,6 +4,7 @@ from __future__ import print_function
import sys
import os
+from collections import OrderedDict # used as OrderedSet
from argparse import ArgumentParser
@@ -20,26 +21,60 @@ eg. A library to be placed in /build/lib and linked against libraries in
which prints "-Wl,-rpath,$ORIGIN/. -Wl,-rpath,$ORIGIN/../module/lib -Wl,-rpath,/other/lib"
''')
P.add_argument('-F','--final',default=os.getcwd(), help='Final install location for ELF file')
-P.add_argument('-R','--root',default='/', help='Root of relocatable tree.')
+P.add_argument('-R','--root',default='', help='Root(s) of relocatable tree. Separate with :')
P.add_argument('-O', '--origin', default='$ORIGIN')
P.add_argument('path', nargs='*')
args = P.parse_args()
-fdir = os.path.abspath(args.final)
+# eg.
+# target to be installed as: /build/bin/blah
+#
+# post-install will copy as: /install/bin/blah
+#
+# Need to link against:
+# /install/lib/libA.so
+# /build/lib/libB.so
+# /other/lib/libC.so
+#
+# Want final result to be:
+# -rpath $ORIGIN/../lib -rpath /other/lib \
+# -rpath-link /build/lib -rpath-link /install/lib
-output = []
+fdir = os.path.abspath(args.final)
+roots = [os.path.abspath(root) for root in args.root.split(':') if len(root)]
+
+# find the root which contains the final location
+froot = None
+for root in roots:
+ frel = os.path.relpath(fdir, root)
+ if not frel.startswith('..'):
+ # final dir is under this root
+ froot = root
+ break
+
+if froot is None:
+ sys.stderr.write("makeRPath: Final location %s\nNot under any of: %s\n"%(fdir, roots))
+ sys.exit(1)
+
+output = OrderedDict()
for path in args.path:
path = os.path.abspath(path)
- if args.root and os.path.relpath(path, args.root).startswith('../'):
- pass # absolute rpath
- else:
- # some older binutils don't seem to handle $ORIGIN correctly
- # when locating dependencies of libraries. So also provide
- # the absolute path for internal use by 'ld' only.
- output.append('-Wl,-rpath-link,'+path)
- path = os.path.relpath(path, fdir)
+ for root in roots:
+ rrel = os.path.relpath(path, root)
+ if not rrel.startswith('..'):
+ # path is under this root
- output.append('-Wl,-rpath,'+os.path.join(args.origin, path))
+ # some older binutils don't seem to handle $ORIGIN correctly
+ # when locating dependencies of libraries. So also provide
+ # the absolute path for internal use by 'ld' only.
+ output['-Wl,-rpath-link,'+path] = True
+
+ # frel is final location relative to enclosing root
+ # rrel is target location relative to enclosing root
+ path = os.path.relpath(rrel, frel)
+ break
+
+ output['-Wl,-rpath,'+os.path.join(args.origin, path)] = True
print(' '.join(output))
From 398fdee33ebf2f20bef733653839cbf2118d22fe Mon Sep 17 00:00:00 2001
From: Andrew Johnson
Date: Wed, 26 Jun 2019 23:28:51 -0500
Subject: [PATCH 061/281] Added db_available_logs() for filter test code to use
Returns the number of items available on the db_field_log free-list.
---
src/ioc/db/dbEvent.c | 5 +++++
src/ioc/db/dbEvent.h | 2 +-
2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/src/ioc/db/dbEvent.c b/src/ioc/db/dbEvent.c
index fb1f3a168..d1f954834 100644
--- a/src/ioc/db/dbEvent.c
+++ b/src/ioc/db/dbEvent.c
@@ -1165,3 +1165,8 @@ void db_delete_field_log (db_field_log *pfl)
freeListFree(dbevFieldLogFreeList, pfl);
}
}
+
+int db_available_logs(void)
+{
+ return (int) freeListItemsAvail(dbevFieldLogFreeList);
+}
diff --git a/src/ioc/db/dbEvent.h b/src/ioc/db/dbEvent.h
index fe0e52f90..2e496a726 100644
--- a/src/ioc/db/dbEvent.h
+++ b/src/ioc/db/dbEvent.h
@@ -78,6 +78,7 @@ epicsShareFunc void db_event_disable (dbEventSubscription es);
epicsShareFunc struct db_field_log* db_create_event_log (struct evSubscrip *pevent);
epicsShareFunc struct db_field_log* db_create_read_log (struct dbChannel *chan);
epicsShareFunc void db_delete_field_log (struct db_field_log *pfl);
+epicsShareFunc int db_available_logs(void);
#define DB_EVENT_OK 0
#define DB_EVENT_ERROR (-1)
@@ -87,4 +88,3 @@ epicsShareFunc void db_delete_field_log (struct db_field_log *pfl);
#endif
#endif /*INCLdbEventh*/
-
From e03c7edfe5ac87f8573d0ad420167eec8d26a7bd Mon Sep 17 00:00:00 2001
From: Andrew Johnson
Date: Wed, 26 Jun 2019 23:32:52 -0500
Subject: [PATCH 062/281] Check free-list size to ensure field-logs freed
properly
Moves where the field-logs get freed into the mustPass/mustDrop routines,
where it only happens if the filter didn't free them itself.
Filters that save field-logs can't use this code as-is.
---
src/std/filters/test/decTest.c | 28 +++++++++++++++-------------
1 file changed, 15 insertions(+), 13 deletions(-)
diff --git a/src/std/filters/test/decTest.c b/src/std/filters/test/decTest.c
index cc9317eb4..b6b2276c6 100644
--- a/src/std/filters/test/decTest.c
+++ b/src/std/filters/test/decTest.c
@@ -64,15 +64,29 @@ static void testHead (char* title) {
}
static void mustDrop(dbChannel *pch, db_field_log *pfl, char* m) {
+ int oldFree = db_available_logs();
db_field_log *pfl2 = dbChannelRunPreChain(pch, pfl);
+ int newFree = db_available_logs();
testOk(NULL == pfl2, "filter drops field_log (%s)", m);
+ testOk(newFree == oldFree + 1, "field_log was freed - %d+1 => %d",
+ oldFree, newFree);
+
+ if (newFree == oldFree)
+ db_delete_field_log(pfl);
}
static void mustPass(dbChannel *pch, db_field_log *pfl, char* m) {
+ int oldFree = db_available_logs();
db_field_log *pfl2 = dbChannelRunPreChain(pch, pfl);
+ int newFree = db_available_logs();
testOk(pfl == pfl2, "filter passes field_log (%s)", m);
+ testOk(newFree == oldFree, "field_log was not freed - %d => %d",
+ oldFree, newFree);
+
+ if (newFree == oldFree)
+ db_delete_field_log(pfl);
}
static void checkAndOpenChannel(dbChannel *pch, const chFilterPlugin *plug) {
@@ -114,7 +128,7 @@ MAIN(decTest)
int i;
dbEventCtx evtctx;
- testPlan(68);
+ testPlan(103);
testdbPrepare();
@@ -165,9 +179,6 @@ MAIN(decTest)
mustPass(pch, pfl[3], "i=3");
mustPass(pch, pfl[4], "i=4");
- for (i = 0; i < 5; i++)
- db_delete_field_log(pfl[i]);
-
dbChannelDelete(pch);
/* Decimation (N=2) */
@@ -196,9 +207,6 @@ MAIN(decTest)
mustPass(pch, pfl[8], "i=8");
mustDrop(pch, pfl[9], "i=9");
- for (i = 0; i < 10; i++)
- db_delete_field_log(pfl[i]);
-
dbChannelDelete(pch);
/* Decimation (N=3) */
@@ -227,9 +235,6 @@ MAIN(decTest)
mustDrop(pch, pfl[8], "i=8");
mustPass(pch, pfl[9], "i=9");
- for (i = 0; i < 10; i++)
- db_delete_field_log(pfl[i]);
-
dbChannelDelete(pch);
/* Decimation (N=4) */
@@ -258,9 +263,6 @@ MAIN(decTest)
mustPass(pch, pfl[8], "i=8");
mustDrop(pch, pfl[9], "i=9");
- for (i = 0; i < 10; i++)
- db_delete_field_log(pfl[i]);
-
dbChannelDelete(pch);
db_close_events(evtctx);
From f79c69f0a0ed7b323af8c16751afe9e25e4914fc Mon Sep 17 00:00:00 2001
From: Andrew Johnson
Date: Wed, 26 Jun 2019 23:33:35 -0500
Subject: [PATCH 063/281] Fix the decimate filter, free field-logs when
dropping them
---
src/std/filters/decimate.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/std/filters/decimate.c b/src/std/filters/decimate.c
index d34884ebe..502422f55 100644
--- a/src/std/filters/decimate.c
+++ b/src/std/filters/decimate.c
@@ -63,6 +63,8 @@ static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) {
if (i++ == 0)
passfl = pfl;
+ else
+ db_delete_field_log(pfl);
if (i >= my->n)
i = 0;
From ff1462fcc78a326548198afff43d3dff9acebb20 Mon Sep 17 00:00:00 2001
From: Andrew Johnson
Date: Fri, 28 Jun 2019 12:28:41 -0500
Subject: [PATCH 064/281] Working VxWorks implementation of
epicsThreadMustJoin()
---
modules/libcom/src/osi/os/vxWorks/osdThread.c | 153 +++++++++++++++---
1 file changed, 133 insertions(+), 20 deletions(-)
diff --git a/modules/libcom/src/osi/os/vxWorks/osdThread.c b/modules/libcom/src/osi/os/vxWorks/osdThread.c
index 6847e9769..43770eab3 100644
--- a/modules/libcom/src/osi/os/vxWorks/osdThread.c
+++ b/modules/libcom/src/osi/os/vxWorks/osdThread.c
@@ -34,6 +34,22 @@
#include "vxLib.h"
#include "epicsExit.h"
+#if EPICS_THREAD_CAN_JOIN
+ /* The implementation of epicsThreadMustJoin() here uses 2 features
+ * of VxWorks that were first introduced in VxWorks 6.9: taskWait(),
+ * and the taskSpareFieldGet/Set routines in taskUtilLib.
+ */
+ #include
+
+ #define JOIN_WARNING_TIMEOUT (60 * sysClkRateGet())
+
+ static SPARE_NUM joinField;
+ #define ALLOT_JOIN(tid) taskSpareNumAllot(tid, &joinField)
+#else
+ #define ALLOT_JOIN(tid)
+#endif
+
+
epicsShareFunc void osdThreadHooksRun(epicsThreadId id);
#if CPU_FAMILY == MC680X0
@@ -109,6 +125,7 @@ static void epicsThreadInit(void)
assert(taskIdList);
taskIdListSize = ID_LIST_CHUNK;
atRebootRegister();
+ ALLOT_JOIN(0);
done = 1;
}
lock = 0;
@@ -177,19 +194,58 @@ void epicsThreadOnce(epicsThreadOnceId *id, void (*func)(void *), void *arg)
}
semGive(epicsThreadOnceMutex);
}
-
+
+#if EPICS_THREAD_CAN_JOIN
+
+/* This routine is not static so it appears in the back-trace
+ * of a thread that is waiting to be joined.
+ */
+void epicsThreadAwaitingJoin(int tid)
+{
+ SEM_ID joinSem = (SEM_ID) taskSpareFieldGet(tid, joinField);
+ STATUS status;
+
+ if (!joinSem || (int) joinSem == ERROR)
+ return;
+
+ /* Wait for our supervisor */
+ status = semTake(joinSem, JOIN_WARNING_TIMEOUT);
+ if (status && errno == S_objLib_OBJ_TIMEOUT) {
+ errlogPrintf("Warning: epicsThread '%s' still awaiting join\n",
+ epicsThreadGetNameSelf());
+ status = semTake(joinSem, WAIT_FOREVER);
+ }
+ if (status)
+ perror("epicsThreadAwaitingJoin");
+
+ semDelete(joinSem);
+ taskSpareFieldSet(tid, joinField, 0);
+}
+ #define PREPARE_JOIN(tid, joinable) \
+ taskSpareFieldSet(tid, joinField, \
+ joinable ? (int) semBCreate(SEM_Q_FIFO, SEM_EMPTY) : 0)
+ #define AWAIT_JOIN(tid) epicsThreadAwaitingJoin(tid)
+#else
+ #define PREPARE_JOIN(tid, joinable)
+ #define AWAIT_JOIN(tid)
+#endif
+
static void createFunction(EPICSTHREADFUNC func, void *parm)
{
int tid = taskIdSelf();
taskVarAdd(tid,(int *)(char *)&papTSD);
- /*Make sure that papTSD is still 0 after that call to taskVarAdd*/
- papTSD = 0;
+ papTSD = NULL; /* Initialize for this thread */
+
osdThreadHooksRun((epicsThreadId)tid);
+
(*func)(parm);
+
epicsExitCallAtThreadExits ();
free(papTSD);
taskVarDelete(tid,(int *)(char *)&papTSD);
+
+ AWAIT_JOIN(tid);
}
#ifdef ALTIVEC
@@ -197,38 +253,95 @@ static void createFunction(EPICSTHREADFUNC func, void *parm)
#else
#define TASK_FLAGS (VX_FP_TASK)
#endif
-epicsThreadId
-epicsThreadCreateOpt (
- const char * name,
+epicsThreadId epicsThreadCreateOpt(const char * name,
EPICSTHREADFUNC funptr, void * parm, const epicsThreadOpts *opts )
{
int tid;
- if(!opts) opts = &opts_default;
+ if (!opts)
+ opts = &opts_default;
epicsThreadInit();
- if(opts->stackSize<100) {
- errlogPrintf("epicsThreadCreate %s illegal stackSize %d\n",name,opts->stackSize);
- return(0);
+ if (opts->stackSize < 100) {
+ errlogPrintf("epicsThreadCreate %s illegal stackSize %d\n",
+ name, opts->stackSize);
+ return 0;
}
- tid = taskSpawn((char *)name,getOssPriorityValue(opts->priority),
+
+ tid = taskCreate((char *)name,getOssPriorityValue(opts->priority),
TASK_FLAGS, opts->stackSize,
- (FUNCPTR)createFunction,(int)funptr,(int)parm,
+ (FUNCPTR)createFunction, (int)funptr, (int)parm,
0,0,0,0,0,0,0,0);
- if(tid==ERROR) {
+ if (tid == ERROR) {
errlogPrintf("epicsThreadCreate %s failure %s\n",
- name,strerror(errno));
- return(0);
+ name, strerror(errno));
+ return 0;
}
- return((epicsThreadId)tid);
+
+ PREPARE_JOIN(tid, opts->joinable);
+ taskActivate(tid);
+
+ return (epicsThreadId)tid;
}
-void epicsThreadMustJoin(epicsThreadId id) {
+void epicsThreadMustJoin(epicsThreadId id)
+{
+ const char *fn = "epicsThreadMustJoin";
#if EPICS_THREAD_CAN_JOIN
- int tid = (int)id;
+ int tid = (int) id;
+ SEM_ID joinSem;
+ STATUS status;
- if (tid)
- taskWait(tid, WAIT_FOREVER);
+ if (!tid)
+ return;
+
+ joinSem = (SEM_ID) taskSpareFieldGet(tid, joinField);
+ if ((int) joinSem == ERROR) {
+ errlogPrintf("%s: Thread '%s' no longer exists.\n",
+ fn, taskName(tid));
+ return;
+ }
+
+ if (tid == taskIdSelf()) {
+ if (joinSem) {
+ semDelete(joinSem);
+ taskSpareFieldSet(tid, joinField, 0);
+ }
+ else {
+ errlogPrintf("%s: Self-join of unjoinable thread '%s'\n",
+ fn, taskName(tid));
+ }
+ return;
+ }
+
+ if (!joinSem) {
+ cantProceed("%s: Thread '%s' is not joinable.\n",
+ fn, taskName(tid));
+ return;
+ }
+
+ semGive(joinSem); /* Rendezvous with thread */
+
+ status = taskWait(tid, JOIN_WARNING_TIMEOUT);
+ if (status && errno == S_objLib_OBJ_TIMEOUT) {
+ errlogPrintf("Warning: %s still waiting for thread '%s'\n",
+ fn, taskName(tid));
+ status = taskWait(tid, WAIT_FOREVER);
+ }
+ if (status) {
+ if (errno == S_taskLib_ILLEGAL_OPERATION) {
+ errlogPrintf("%s: This shouldn't happen!\n", fn);
+ }
+ else if (errno == S_objLib_OBJ_ID_ERROR) {
+ errlogPrintf("%s: %x is not a known thread\n", fn, tid);
+ }
+ else {
+ perror(fn);
+ }
+ cantProceed(fn);
+ }
+#else
+ cantProceed("%s called when EPICS_THREAD_CAN_JOIN is 0\n", fn);
#endif
}
From 8a072d3c043c2ba51214a7e56d550ff4c9dd95e4 Mon Sep 17 00:00:00 2001
From: Andrew Johnson
Date: Fri, 28 Jun 2019 12:40:07 -0500
Subject: [PATCH 065/281] More basic tests of epicsThreadMustJoin()
---
modules/libcom/test/epicsThreadTest.cpp | 67 ++++++++++++++++++-------
1 file changed, 48 insertions(+), 19 deletions(-)
diff --git a/modules/libcom/test/epicsThreadTest.cpp b/modules/libcom/test/epicsThreadTest.cpp
index edaada914..6785f32ba 100644
--- a/modules/libcom/test/epicsThreadTest.cpp
+++ b/modules/libcom/test/epicsThreadTest.cpp
@@ -86,46 +86,75 @@ void testMyThread()
}
}
-struct selfJoiner {
- epicsEvent finished;
+struct joinStuff {
+ epicsThreadOpts *opts;
+ epicsEvent *trigger;
+ epicsEvent *finished;
};
-void joiner(void *arg) {
- epicsEvent *finished = (epicsEvent*)arg;
+void donothing(void *arg)
+{}
+
+void dowait(void *arg)
+{
+ epicsEvent *trigger = (epicsEvent *) arg;
+ trigger->wait();
+ epicsThreadSleep(0.1);
+}
+
+void joinTests(void *arg)
+{
+ struct joinStuff *stuff = (struct joinStuff *) arg;
+
+ // Task finishes before parent joins
+ epicsThreadId tid = epicsThreadCreateOpt("nothing",
+ &donothing, 0, stuff->opts);
+ epicsThreadSleep(0.1);
+ epicsThreadMustJoin(tid);
+
+ // Parent joins before task finishes
+ tid = epicsThreadCreateOpt("await",
+ &dowait, stuff->trigger, stuff->opts);
+ stuff->trigger->signal();
+ epicsThreadMustJoin(tid);
// This is a no-op
- epicsThreadMustJoin(epicsThreadGetIdSelf());
+ epicsThreadId self = epicsThreadGetIdSelf();
+ epicsThreadMustJoin(self);
// This is a no-op as well, except for a warning.
eltc(0);
- epicsThreadMustJoin(epicsThreadGetIdSelf());
+ epicsThreadMustJoin(self);
eltc(1);
- testPass("Check double self-join");
- finished->signal();
+ stuff->finished->signal();
}
-typedef struct info {
- int isOkToBlock;
- int didSomething;
-} info;
-
-void testSelfJoin()
+void testJoining()
{
- epicsEvent finished;
epicsThreadOpts opts;
epicsThreadOptsDefaults(&opts);
opts.priority = 50;
opts.joinable = 1;
- (void)epicsThreadCreateOpt("selfjoin", &joiner, &finished, &opts);
+ epicsEvent finished, trigger;
- // as this thread "joins" itself, we can't.
- finished.wait();
+ struct joinStuff stuff = {
+ &opts, &trigger, &finished
+ };
+ epicsThreadCreateOpt("parent", &joinTests, &stuff, &opts);
+
+ // as selfjoin joins itself, we can't.
+ testOk(finished.wait(10.0), "Join tests completed");
}
} // namespace
+typedef struct info {
+ int isOkToBlock;
+ int didSomething;
+} info;
+
extern "C" {
static void thread(void *arg)
{
@@ -174,8 +203,8 @@ MAIN(epicsThreadTest)
testOk1(ncpus > 0);
testDiag("main() thread %p", epicsThreadGetIdSelf());
+ testJoining(); // Do this first, ~epicsThread() uses it...
testMyThread();
- testSelfJoin();
testOkToBlock();
// attempt to self-join from a non-EPICS thread
From 4b77d5e1c98f29a70129cf72e02145f68cf220d6 Mon Sep 17 00:00:00 2001
From: Andrew Johnson
Date: Fri, 28 Jun 2019 13:24:29 -0500
Subject: [PATCH 066/281] Release Note updates
VxWorks minimum version, and more about joinable threads.
---
documentation/RELEASE_NOTES.html | 19 +++++++++++++++++--
1 file changed, 17 insertions(+), 2 deletions(-)
diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html
index fa9727aff..b3e266031 100644
--- a/documentation/RELEASE_NOTES.html
+++ b/documentation/RELEASE_NOTES.html
@@ -26,6 +26,13 @@ release.
-->
+
VxWorks Minimum Version Requirements
+
+
The implementation of the epicsThreadMustJoin() functionality
+described below requires facilities that were added to the OS in VxWorks 6.9, so
+that is now the oldest version which this release of EPICS can be built
+against.
+
Add epicsThreadCreateOpt() and epicsThreadMustJoin()
epicsThreadCreateOpt() is an alternative to epicsThreadCreate() which
@@ -45,8 +52,16 @@ void startitup(void) {
If the new epicsThreadOpts::joinable option flag is set (not the default),
-then epicsThreadMustJoin() needs to be called to free up thread resources.
-This function will block until the thread main function has returned.
+then epicsThreadMustJoin()must be called with that
+thread's epicsThreadId when/after the thread exits, to free up thread resources.
+This function will block until the thread's main function has returned, after
+which the epicsThreadId will no longer be valid.
+
+
A thread which was created with the joinable flag set may itself call
+epicsThreadMustJoin() passing its own epicsThreadId. This marks the
+thread as no longer being joinable, so it will then free the thread resources
+itself when its main function returns. The epicsThreadId for a thread that is
+not joinable gets invalidated as soon as its main function returns.
Launchpad Bugs
From fbf62189cbabdc96b9e431ed60b0557e0c6079ea Mon Sep 17 00:00:00 2001
From: Andrew Johnson
Date: Tue, 2 Jul 2019 17:27:27 -0500
Subject: [PATCH 067/281] Replace epicsThreadOptsDefaults() with
EPICS_THREAD_OPTS_INIT
The epicsThreadCreate() routines now interpose calls to
epicsThreadGetStackSize() if an enum value is passed.
---
modules/database/src/ioc/as/asCa.c | 4 +-
modules/database/src/ioc/db/dbCa.c | 4 +-
modules/database/src/ioc/db/dbEvent.c | 3 +-
modules/libcom/src/osi/epicsThread.cpp | 9 ++-
modules/libcom/src/osi/epicsThread.h | 15 +++--
modules/libcom/src/osi/os/RTEMS/osdThread.c | 20 +++---
modules/libcom/src/osi/os/WIN32/osdThread.c | 18 ++---
modules/libcom/src/osi/os/posix/osdThread.c | 66 +++++++++++--------
modules/libcom/src/osi/os/vxWorks/osdThread.c | 26 ++++----
modules/libcom/test/epicsThreadTest.cpp | 16 ++---
10 files changed, 95 insertions(+), 86 deletions(-)
diff --git a/modules/database/src/ioc/as/asCa.c b/modules/database/src/ioc/as/asCa.c
index e8c5403d9..21bb47f5f 100644
--- a/modules/database/src/ioc/as/asCa.c
+++ b/modules/database/src/ioc/as/asCa.c
@@ -229,8 +229,8 @@ static void asCaTask(void)
void asCaStart(void)
{
- epicsThreadOpts opts;
- epicsThreadOptsDefaults(&opts);
+ epicsThreadOpts opts = EPICS_THREAD_OPTS_INIT;
+
opts.stackSize = epicsThreadGetStackSize(epicsThreadStackBig);
opts.priority = epicsThreadPriorityScanLow - 3;
opts.joinable = 1;
diff --git a/modules/database/src/ioc/db/dbCa.c b/modules/database/src/ioc/db/dbCa.c
index 935c397d4..ea0fad0d3 100644
--- a/modules/database/src/ioc/db/dbCa.c
+++ b/modules/database/src/ioc/db/dbCa.c
@@ -265,8 +265,8 @@ void dbCaShutdown(void)
static void dbCaLinkInitImpl(int isolate)
{
- epicsThreadOpts opts;
- epicsThreadOptsDefaults(&opts);
+ epicsThreadOpts opts = EPICS_THREAD_OPTS_INIT;
+
opts.stackSize = epicsThreadGetStackSize(epicsThreadStackBig);
opts.priority = epicsThreadPriorityMedium;
opts.joinable = 1;
diff --git a/modules/database/src/ioc/db/dbEvent.c b/modules/database/src/ioc/db/dbEvent.c
index 48dc9012b..febe62634 100644
--- a/modules/database/src/ioc/db/dbEvent.c
+++ b/modules/database/src/ioc/db/dbEvent.c
@@ -1056,9 +1056,8 @@ int db_start_events (
void *init_func_arg, unsigned osiPriority )
{
struct event_user * const evUser = (struct event_user *) ctx;
- epicsThreadOpts opts;
+ epicsThreadOpts opts = EPICS_THREAD_OPTS_INIT;
- epicsThreadOptsDefaults(&opts);
opts.stackSize = epicsThreadGetStackSize(epicsThreadStackMedium);
opts.priority = osiPriority;
opts.joinable = 1;
diff --git a/modules/libcom/src/osi/epicsThread.cpp b/modules/libcom/src/osi/epicsThread.cpp
index 9a049133f..92f833847 100644
--- a/modules/libcom/src/osi/epicsThread.cpp
+++ b/modules/libcom/src/osi/epicsThread.cpp
@@ -35,7 +35,7 @@ epicsThreadId epicsShareAPI epicsThreadCreate (
const char * name, unsigned int priority, unsigned int stackSize,
EPICSTHREADFUNC funptr,void * parm )
{
- epicsThreadOpts opts;
+ epicsThreadOpts opts = EPICS_THREAD_OPTS_INIT;
opts.priority = priority;
opts.stackSize = stackSize;
opts.joinable = 0;
@@ -202,11 +202,10 @@ epicsThread::epicsThread (
epicsThreadRunable & runableIn, const char * pName,
unsigned stackSize, unsigned priority ) :
runable ( runableIn ), id ( 0 ), pThreadDestroyed ( 0 ),
- begin ( false ), cancel ( false ), terminated ( false )
- , joined(false)
+ begin ( false ), cancel ( false ), terminated ( false ),
+ joined ( false )
{
- epicsThreadOpts opts;
- epicsThreadOptsDefaults(&opts);
+ epicsThreadOpts opts = EPICS_THREAD_OPTS_INIT;
opts.stackSize = stackSize;
opts.priority = priority;
opts.joinable = 1;
diff --git a/modules/libcom/src/osi/epicsThread.h b/modules/libcom/src/osi/epicsThread.h
index 694ac94c7..82fa04b5b 100644
--- a/modules/libcom/src/osi/epicsThread.h
+++ b/modules/libcom/src/osi/epicsThread.h
@@ -84,9 +84,8 @@ epicsShareFunc void epicsShareAPI epicsThreadExitMain(void);
typedef struct epicsThreadOpts {
/** Thread priority in OSI range (cf. epicsThreadPriority*) */
unsigned int priority;
- /** Thread stack size, as returned by epicsThreadGetStackSize().
- *
- * @warning Do not pass enum epicsThreadStackSizeClass directly!
+ /** Thread stack size, either in bytes for this architecture or
+ * an enum epicsThreadStackSizeClass value.
*/
unsigned int stackSize;
/** Should thread be joinable? (default (0) is not joinable).
@@ -95,8 +94,14 @@ typedef struct epicsThreadOpts {
unsigned int joinable;
} epicsThreadOpts;
-/** Fill in target specific default values. */
-epicsShareFunc void epicsThreadOptsDefaults(epicsThreadOpts *opts);
+/** Default initial values for epicsThreadOpts
+ * Applications should always use this macro to initialize an epicsThreadOpts
+ * structure. Additional fields may be added in the future, and the order of
+ * the fields might also change, thus code that assumes the above definition
+ * might break if these rules are not followed.
+ */
+#define EPICS_THREAD_OPTS_INIT { \
+ epicsThreadPriorityLow, epicsThreadStackMedium, 0}
/** @brief Allocate and start a new OS thread.
* @param name A name describing this thread. Appears in various log and error message.
diff --git a/modules/libcom/src/osi/os/RTEMS/osdThread.c b/modules/libcom/src/osi/os/RTEMS/osdThread.c
index fdc31759f..bdcd8c17e 100644
--- a/modules/libcom/src/osi/os/RTEMS/osdThread.c
+++ b/modules/libcom/src/osi/os/RTEMS/osdThread.c
@@ -152,13 +152,6 @@ epicsThreadGetStackSize (epicsThreadStackSizeClass size)
return stackSize;
}
-static const epicsThreadOpts opts_default = {epicsThreadPriorityLow, 5000, 0};
-
-void epicsThreadOptsDefaults(epicsThreadOpts *opts)
-{
- *opts = opts_default;
-}
-
/*
* Ensure integrity of task variable list
*/
@@ -315,15 +308,22 @@ epicsThreadCreateOpt (
const char * name,
EPICSTHREADFUNC funptr, void * parm, const epicsThreadOpts *opts )
{
+ unsigned int stackSize;
rtems_id tid;
rtems_status_code sc;
char c[4];
- unsigned stackSize;
- if(!opts) opts = &opts_default;
+ if (!initialized)
+ epicsThreadInit();
+
+ if (!opts) {
+ static const epicsThreadOpts opts_default = EPICS_THREAD_OPTS_INIT;
+ opts = &opts_default;
+ }
stackSize = opts->stackSize;
+ if (stackSize <= epicsThreadStackBig)
+ stackSize = epicsThreadGetStackSize(stackSize);
- if (!initialized) epicsThreadInit();
if (stackSize < RTEMS_MINIMUM_STACK_SIZE) {
errlogPrintf ("Warning: epicsThreadCreate %s illegal stackSize %d\n",
name, stackSize);
diff --git a/modules/libcom/src/osi/os/WIN32/osdThread.c b/modules/libcom/src/osi/os/WIN32/osdThread.c
index 6d43e769f..1cfa1ec5c 100644
--- a/modules/libcom/src/osi/os/WIN32/osdThread.c
+++ b/modules/libcom/src/osi/os/WIN32/osdThread.c
@@ -469,13 +469,6 @@ epicsShareFunc unsigned int epicsShareAPI
return stackSizeTable[stackSizeClass];
}
-static const epicsThreadOpts opts_default = {epicsThreadPriorityLow, STACK_SIZE(1), 0};
-
-void epicsThreadOptsDefaults(epicsThreadOpts *opts)
-{
- *opts = opts_default;
-}
-
void epicsThreadCleanupWIN32 ()
{
win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal ();
@@ -599,6 +592,7 @@ epicsThreadId epicsThreadCreateOpt (
{
win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal ();
win32ThreadParam * pParmWIN32;
+ unsigned int stackSize;
int osdPriority;
DWORD wstat;
BOOL bstat;
@@ -607,7 +601,13 @@ epicsThreadId epicsThreadCreateOpt (
return NULL;
}
- if(!opts) opts = &opts_default;
+ if (!opts) {
+ static const epicsThreadOpts opts_default = EPICS_THREAD_OPTS_INIT;
+ opts = &opts_default;
+ }
+ stackSize = opts->stackSize;
+ if (stackSize <= epicsThreadStackBig)
+ stackSize = epicsThreadGetStackSize(stackSize);
pParmWIN32 = epicsThreadParmCreate ( pName );
if ( pParmWIN32 == 0 ) {
@@ -620,7 +620,7 @@ epicsThreadId epicsThreadCreateOpt (
{
unsigned threadId;
pParmWIN32->handle = (HANDLE) _beginthreadex (
- 0, opts->stackSize, epicsWin32ThreadEntry,
+ 0, stackSize, epicsWin32ThreadEntry,
pParmWIN32,
CREATE_SUSPENDED | STACK_SIZE_PARAM_IS_A_RESERVATION,
& threadId );
diff --git a/modules/libcom/src/osi/os/posix/osdThread.c b/modules/libcom/src/osi/os/posix/osdThread.c
index 5ed2cbc56..ea0d2b265 100644
--- a/modules/libcom/src/osi/os/posix/osdThread.c
+++ b/modules/libcom/src/osi/os/posix/osdThread.c
@@ -469,13 +469,6 @@ epicsShareFunc unsigned int epicsShareAPI epicsThreadGetStackSize (epicsThreadSt
return 0;
#endif /*_POSIX_THREAD_ATTR_STACKSIZE*/
}
-
-static const epicsThreadOpts opts_default = {epicsThreadPriorityLow, STACK_SIZE(1), 0};
-
-void epicsThreadOptsDefaults(epicsThreadOpts *opts)
-{
- *opts = opts_default;
-}
epicsShareFunc void epicsShareAPI epicsThreadOnce(epicsThreadOnceId *id, void (*func)(void *), void *arg)
{
@@ -519,48 +512,65 @@ epicsShareFunc void epicsShareAPI epicsThreadOnce(epicsThreadOnceId *id, void (*
}
epicsThreadId
-epicsThreadCreateOpt (
- const char * name,
+epicsThreadCreateOpt(const char * name,
EPICSTHREADFUNC funptr, void * parm, const epicsThreadOpts *opts )
{
+ unsigned int stackSize;
epicsThreadOSD *pthreadInfo;
int status;
sigset_t blockAllSig, oldSig;
- if(!opts) opts = &opts_default;
-
epicsThreadInit();
assert(pcommonAttr);
+
+ if (!opts) {
+ static const epicsThreadOpts opts_default = EPICS_THREAD_OPTS_INIT;
+ opts = &opts_default;
+ }
+ stackSize = opts->stackSize;
+ if (stackSize <= epicsThreadStackBig)
+ stackSize = epicsThreadGetStackSize(stackSize);
+
sigfillset(&blockAllSig);
- pthread_sigmask(SIG_SETMASK,&blockAllSig,&oldSig);
- pthreadInfo = init_threadInfo(name,opts->priority,opts->stackSize,funptr,parm,opts->joinable);
- if(pthreadInfo==0) return 0;
+ pthread_sigmask(SIG_SETMASK, &blockAllSig, &oldSig);
+
+ pthreadInfo = init_threadInfo(name, opts->priority, stackSize, funptr,
+ parm, opts->joinable);
+ if (pthreadInfo==0)
+ return 0;
+
pthreadInfo->isEpicsThread = 1;
- setSchedulingPolicy(pthreadInfo,SCHED_FIFO);
+ setSchedulingPolicy(pthreadInfo, SCHED_FIFO);
pthreadInfo->isRealTimeScheduled = 1;
- status = pthread_create(&pthreadInfo->tid,&pthreadInfo->attr,
- start_routine,pthreadInfo);
- if(status==EPERM){
+
+ status = pthread_create(&pthreadInfo->tid, &pthreadInfo->attr,
+ start_routine, pthreadInfo);
+ if (status==EPERM) {
/* Try again without SCHED_FIFO*/
free_threadInfo(pthreadInfo);
- pthreadInfo = init_threadInfo(name,opts->priority,opts->stackSize,funptr,parm,opts->joinable);
- if(pthreadInfo==0) return 0;
+
+ pthreadInfo = init_threadInfo(name, opts->priority, stackSize,
+ funptr, parm, opts->joinable);
+ if (pthreadInfo==0)
+ return 0;
+
pthreadInfo->isEpicsThread = 1;
- status = pthread_create(&pthreadInfo->tid,&pthreadInfo->attr,
- start_routine,pthreadInfo);
+ status = pthread_create(&pthreadInfo->tid, &pthreadInfo->attr,
+ start_routine, pthreadInfo);
}
- checkStatusOnce(status,"pthread_create");
- if(status) {
+ checkStatusOnce(status, "pthread_create");
+ if (status) {
free_threadInfo(pthreadInfo);
return 0;
}
- status = pthread_sigmask(SIG_SETMASK,&oldSig,NULL);
- checkStatusOnce(status,"pthread_sigmask");
- if(pthreadInfo->joinable) {
+
+ status = pthread_sigmask(SIG_SETMASK, &oldSig, NULL);
+ checkStatusOnce(status, "pthread_sigmask");
+ if (pthreadInfo->joinable) {
/* extra ref for epicsThreadMustJoin() */
epicsAtomicIncrIntT(&pthreadInfo->refcnt);
}
- return(pthreadInfo);
+ return pthreadInfo;
}
/*
diff --git a/modules/libcom/src/osi/os/vxWorks/osdThread.c b/modules/libcom/src/osi/os/vxWorks/osdThread.c
index 43770eab3..d5b859ade 100644
--- a/modules/libcom/src/osi/os/vxWorks/osdThread.c
+++ b/modules/libcom/src/osi/os/vxWorks/osdThread.c
@@ -150,13 +150,6 @@ unsigned int epicsThreadGetStackSize (epicsThreadStackSizeClass stackSizeClass)
return stackSizeTable[stackSizeClass];
}
-static const epicsThreadOpts opts_default = {epicsThreadPriorityLow, 4000*ARCH_STACK_FACTOR, 0};
-
-void epicsThreadOptsDefaults(epicsThreadOpts *opts)
-{
- *opts = opts_default;
-}
-
struct epicsThreadOSD {};
/* Strictly speaking this should be a WIND_TCB, but we only need it to
* be able to create an epicsThreadId that is guaranteed never to be
@@ -256,20 +249,27 @@ static void createFunction(EPICSTHREADFUNC func, void *parm)
epicsThreadId epicsThreadCreateOpt(const char * name,
EPICSTHREADFUNC funptr, void * parm, const epicsThreadOpts *opts )
{
+ unsigned int stackSize;
int tid;
- if (!opts)
- opts = &opts_default;
-
epicsThreadInit();
- if (opts->stackSize < 100) {
+
+ if (!opts) {
+ static const epicsThreadOpts opts_default = EPICS_THREAD_OPTS_INIT;
+ opts = &opts_default;
+ }
+ stackSize = opts->stackSize;
+ if (stackSize <= epicsThreadStackBig)
+ stackSize = epicsThreadGetStackSize(stackSize);
+
+ if (stackSize < 100) {
errlogPrintf("epicsThreadCreate %s illegal stackSize %d\n",
- name, opts->stackSize);
+ name, stackSize);
return 0;
}
tid = taskCreate((char *)name,getOssPriorityValue(opts->priority),
- TASK_FLAGS, opts->stackSize,
+ TASK_FLAGS, stackSize,
(FUNCPTR)createFunction, (int)funptr, (int)parm,
0,0,0,0,0,0,0,0);
if (tid == ERROR) {
diff --git a/modules/libcom/test/epicsThreadTest.cpp b/modules/libcom/test/epicsThreadTest.cpp
index 6785f32ba..d07552d15 100644
--- a/modules/libcom/test/epicsThreadTest.cpp
+++ b/modules/libcom/test/epicsThreadTest.cpp
@@ -40,7 +40,7 @@ private:
};
myThread::myThread(int arg,const char *name) :
- thread(*this,name,epicsThreadGetStackSize(epicsThreadStackSmall),50+arg),
+ thread(*this,name,epicsThreadStackSmall,50+arg),
argvalue(0)
{
argvalue = new int;
@@ -132,16 +132,14 @@ void joinTests(void *arg)
void testJoining()
{
- epicsThreadOpts opts;
- epicsThreadOptsDefaults(&opts);
- opts.priority = 50;
- opts.joinable = 1;
-
+ epicsThreadOpts opts = EPICS_THREAD_OPTS_INIT;
epicsEvent finished, trigger;
-
struct joinStuff stuff = {
&opts, &trigger, &finished
};
+
+ opts.priority = 50;
+ opts.joinable = 1;
epicsThreadCreateOpt("parent", &joinTests, &stuff, &opts);
// as selfjoin joins itself, we can't.
@@ -172,9 +170,8 @@ static void thread(void *arg)
static void testOkToBlock()
{
+ epicsThreadOpts opts = EPICS_THREAD_OPTS_INIT;
- epicsThreadOpts opts;
- epicsThreadOptsDefaults(&opts);
opts.priority = 50;
opts.joinable = 1;
@@ -190,7 +187,6 @@ static void testOkToBlock()
epicsThreadMustJoin(threadA);
testOk1(infoA.didSomething);
-
}
From 99be9a86a0bb2a16eac334aa8e1d509e5558cb6c Mon Sep 17 00:00:00 2001
From: Andrew Johnson
Date: Tue, 2 Jul 2019 17:31:37 -0500
Subject: [PATCH 068/281] Rework EPICS_THREAD_CAN_JOIN
RTEMS osdThread.h was missing an extern "C" wrapper.
---
modules/libcom/src/osi/epicsThread.h | 5 ++++-
modules/libcom/src/osi/os/Linux/osdThread.h | 3 ---
modules/libcom/src/osi/os/RTEMS/osdThread.h | 18 +++++++++++++-----
modules/libcom/src/osi/os/WIN32/osdThread.h | 8 ++------
modules/libcom/src/osi/os/posix/osdThread.h | 8 ++------
modules/libcom/src/osi/os/vxWorks/osdThread.c | 8 +++-----
modules/libcom/src/osi/os/vxWorks/osdThread.h | 13 +++++--------
7 files changed, 29 insertions(+), 34 deletions(-)
diff --git a/modules/libcom/src/osi/epicsThread.h b/modules/libcom/src/osi/epicsThread.h
index 82fa04b5b..da16a0b25 100644
--- a/modules/libcom/src/osi/epicsThread.h
+++ b/modules/libcom/src/osi/epicsThread.h
@@ -124,7 +124,10 @@ epicsShareFunc epicsThreadId epicsShareAPI epicsThreadCreate (
epicsShareFunc epicsThreadId epicsShareAPI epicsThreadMustCreate (
const char * name, unsigned int priority, unsigned int stackSize,
EPICSTHREADFUNC funptr,void * parm );
-/** Wait for a joinable thread to exit (return from its main function */
+
+/* This gets undefined in osdThread.h on VxWorks < 6.9 */
+#define EPICS_THREAD_CAN_JOIN
+/** Wait for a joinable thread to exit (return from its main function) */
epicsShareFunc void epicsThreadMustJoin(epicsThreadId id);
/** Block the current thread until epicsThreadResume(). */
epicsShareFunc void epicsShareAPI epicsThreadSuspendSelf(void);
diff --git a/modules/libcom/src/osi/os/Linux/osdThread.h b/modules/libcom/src/osi/os/Linux/osdThread.h
index 40a837e9f..bb1fdcb0a 100644
--- a/modules/libcom/src/osi/os/Linux/osdThread.h
+++ b/modules/libcom/src/osi/os/Linux/osdThread.h
@@ -16,9 +16,6 @@
#include "ellLib.h"
#include "epicsEvent.h"
-/* This target supports joining threads */
-#define EPICS_THREAD_CAN_JOIN (1)
-
#ifdef __cplusplus
extern "C" {
#endif
diff --git a/modules/libcom/src/osi/os/RTEMS/osdThread.h b/modules/libcom/src/osi/os/RTEMS/osdThread.h
index 84d579c0f..4eef8c01f 100644
--- a/modules/libcom/src/osi/os/RTEMS/osdThread.h
+++ b/modules/libcom/src/osi/os/RTEMS/osdThread.h
@@ -3,13 +3,21 @@
* National Laboratory.
* Copyright (c) 2002 The Regents of the University of California, as
* Operator of Los Alamos National Laboratory.
-* EPICS BASE Versions 3.13.7
-* and higher are distributed subject to a Software License Agreement found
-* in file LICENSE that is included with this distribution.
+* EPICS BASE is distributed subject to a Software License Agreement found
+* in file LICENSE that is included with this distribution.
\*************************************************************************/
-/* This target supports joining threads */
-#define EPICS_THREAD_CAN_JOIN (1)
+#ifndef INC_osdThread_H
+#define INC_osdThread_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
int epicsThreadGetOssPriorityValue(unsigned int osiPriority);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* INC_osdThread_H */
diff --git a/modules/libcom/src/osi/os/WIN32/osdThread.h b/modules/libcom/src/osi/os/WIN32/osdThread.h
index fe60564d1..69bc364f0 100644
--- a/modules/libcom/src/osi/os/WIN32/osdThread.h
+++ b/modules/libcom/src/osi/os/WIN32/osdThread.h
@@ -3,15 +3,11 @@
* National Laboratory.
* Copyright (c) 2002 The Regents of the University of California, as
* Operator of Los Alamos National Laboratory.
-* EPICS BASE Versions 3.13.7
-* and higher are distributed subject to a Software License Agreement found
-* in file LICENSE that is included with this distribution.
+* EPICS BASE is distributed subject to a Software License Agreement found
+* in file LICENSE that is included with this distribution.
\*************************************************************************/
#ifndef osdThreadh
#define osdThreadh
-/* This target supports joining threads */
-#define EPICS_THREAD_CAN_JOIN (1)
-
#endif /* osdThreadh */
diff --git a/modules/libcom/src/osi/os/posix/osdThread.h b/modules/libcom/src/osi/os/posix/osdThread.h
index eee0c825d..8fe8f14eb 100644
--- a/modules/libcom/src/osi/os/posix/osdThread.h
+++ b/modules/libcom/src/osi/os/posix/osdThread.h
@@ -3,9 +3,8 @@
* National Laboratory.
* Copyright (c) 2002 The Regents of the University of California, as
* Operator of Los Alamos National Laboratory.
-* EPICS BASE Versions 3.13.7
-* and higher are distributed subject to a Software License Agreement found
-* in file LICENSE that is included with this distribution.
+* EPICS BASE is distributed subject to a Software License Agreement found
+* in file LICENSE that is included with this distribution.
\*************************************************************************/
#ifndef osdThreadh
#define osdThreadh
@@ -16,9 +15,6 @@
#include "ellLib.h"
#include "epicsEvent.h"
-/* This target supports joining threads */
-#define EPICS_THREAD_CAN_JOIN (1)
-
#ifdef __cplusplus
extern "C" {
#endif
diff --git a/modules/libcom/src/osi/os/vxWorks/osdThread.c b/modules/libcom/src/osi/os/vxWorks/osdThread.c
index d5b859ade..0ed31389f 100644
--- a/modules/libcom/src/osi/os/vxWorks/osdThread.c
+++ b/modules/libcom/src/osi/os/vxWorks/osdThread.c
@@ -34,7 +34,7 @@
#include "vxLib.h"
#include "epicsExit.h"
-#if EPICS_THREAD_CAN_JOIN
+#ifdef EPICS_THREAD_CAN_JOIN
/* The implementation of epicsThreadMustJoin() here uses 2 features
* of VxWorks that were first introduced in VxWorks 6.9: taskWait(),
* and the taskSpareFieldGet/Set routines in taskUtilLib.
@@ -188,7 +188,7 @@ void epicsThreadOnce(epicsThreadOnceId *id, void (*func)(void *), void *arg)
semGive(epicsThreadOnceMutex);
}
-#if EPICS_THREAD_CAN_JOIN
+#ifdef EPICS_THREAD_CAN_JOIN
/* This routine is not static so it appears in the back-trace
* of a thread that is waiting to be joined.
@@ -286,8 +286,8 @@ epicsThreadId epicsThreadCreateOpt(const char * name,
void epicsThreadMustJoin(epicsThreadId id)
{
+#ifdef EPICS_THREAD_CAN_JOIN
const char *fn = "epicsThreadMustJoin";
-#if EPICS_THREAD_CAN_JOIN
int tid = (int) id;
SEM_ID joinSem;
STATUS status;
@@ -340,8 +340,6 @@ void epicsThreadMustJoin(epicsThreadId id)
}
cantProceed(fn);
}
-#else
- cantProceed("%s called when EPICS_THREAD_CAN_JOIN is 0\n", fn);
#endif
}
diff --git a/modules/libcom/src/osi/os/vxWorks/osdThread.h b/modules/libcom/src/osi/os/vxWorks/osdThread.h
index 8fa454ce3..15145663b 100644
--- a/modules/libcom/src/osi/os/vxWorks/osdThread.h
+++ b/modules/libcom/src/osi/os/vxWorks/osdThread.h
@@ -3,20 +3,17 @@
* National Laboratory.
* Copyright (c) 2002 The Regents of the University of California, as
* Operator of Los Alamos National Laboratory.
-* EPICS BASE Versions 3.13.7
-* and higher are distributed subject to a Software License Agreement found
-* in file LICENSE that is included with this distribution.
+* EPICS BASE is distributed subject to a Software License Agreement found
+* in file LICENSE that is included with this distribution.
\*************************************************************************/
+
#ifndef osdThreadh
#define osdThreadh
/* VxWorks 6.9 and later can support joining threads */
-#if (_WRS_VXWORKS_MAJOR > 6) || \
- (_WRS_VXWORKS_MAJOR == 6 && _WRS_VXWORKS_MINOR >= 9)
-# define EPICS_THREAD_CAN_JOIN (1)
-#else
-# define EPICS_THREAD_CAN_JOIN (0)
+#if (_WRS_VXWORKS_MAJOR == 6 && _WRS_VXWORKS_MINOR < 9)
+#undef EPICS_THREAD_CAN_JOIN
#endif
#endif /* osdThreadh */
From e5782ae716ea60f721ed0545d60412dcb68db5e5 Mon Sep 17 00:00:00 2001
From: Andrew Johnson
Date: Tue, 2 Jul 2019 17:38:16 -0500
Subject: [PATCH 069/281] Update release notes again, more detail
---
documentation/RELEASE_NOTES.html | 95 +++++++++++++++++++++++---------
1 file changed, 70 insertions(+), 25 deletions(-)
diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html
index b3e266031..203b7a873 100644
--- a/documentation/RELEASE_NOTES.html
+++ b/documentation/RELEASE_NOTES.html
@@ -26,42 +26,87 @@ release.
-->
-
VxWorks Minimum Version Requirements
+
VxWorks Minimum Version Recommendation
-
The implementation of the epicsThreadMustJoin() functionality
-described below requires facilities that were added to the OS in VxWorks 6.9, so
-that is now the oldest version which this release of EPICS can be built
-against.
+
The implementation of the epicsThreadMustJoin() feature
+described below uses facilities that were added to VxWorks in version 6.9. When
+built against an older version of VxWorks the join functionality will not be
+available and calls to epicsThreadMustJoin() will return
+immediately. In this case the epicsThread.h header will not define the C macro
+EPICS_THREAD_CAN_JOIN to allow alternate code to be provided for
+these targets. The IOC's use of the join feature has been designed to work for
+either situation.
-
Add epicsThreadCreateOpt() and epicsThreadMustJoin()
-
epicsThreadCreateOpt() is an alternative to epicsThreadCreate() which
-passes some arguments via a structure (struct epicsThreadOpts).
-This epicsThreadOpts* may be NULL to use target specific
-defaults. Caller wishing to provide thread options should first call
-epicsThreadOptsDefaults() to fill in the defaults.
+
New and modified epicsThread APIs
+
+
epicsThreadCreateOpt()
+
+
A new routine epicsThreadCreateOpt() is an alternative to
+epicsThreadCreate() which takes some arguments via a structure
+(struct epicsThreadOpts) to allow for future extensions.
The final opts parameter may be NULL to use the
+default values of thread priority (low) and stack size (medium). Callers wishing
+to provide alternative settings for these thread options or to create a joinable
+thread (see below) should create and pass in an epicsThreadOpts
+structure as shown below. Always initialize one of these structures using the
+EPICS_THREAD_OPTS_INIT macro to ensure that any additional fields
+that get added in the future are set to their default values.
If the new epicsThreadOpts::joinable option flag is set (not the default),
-then epicsThreadMustJoin()must be called with that
-thread's epicsThreadId when/after the thread exits, to free up thread resources.
-This function will block until the thread's main function has returned, after
-which the epicsThreadId will no longer be valid.
+
C or C++ Code that also needs to build on earlier versions of Base can use
+#ifdef EPICS_THREAD_OPTS_INIT to determine whether the
+epicsThreadCreateOpt() API is available on this Base version.
+
+
Thread stack sizes
+
+
The stackSize member of the epicsThreadOpts
+structure and the equivalent parameters to the epicsThreadCreate()
+and epicsThreadMustCreate() routines can now be passed either one
+of the epicsThreadStackSizeClass enum values or a value returned
+from the epicsThreadGetStackSize() routine.
+
+
epicsThreadMustJoin()
+
+
If the new joinable flag of an epicsThreadOpts
+structure is non-zero (the default value is zero), the new API routine
+epicsThreadMustJoin()must be called with the thread's
+epicsThreadId when/after the thread exits, to free up thread
+resources. This function will block until the thread's main function has
+returned, allowing the parent to wait for its child thread. The child's
+epicsThreadId will no longer be valid and should not be used after
+the epicsThreadMustJoin() routine returns.
+
+
A thread that was originally created with its joinable flag set may itself
+call epicsThreadMustJoin(), passing in its own epicsThreadId. This
+marks the thread as no longer being joinable, so it will then free the thread
+resources itself when its main function returns. The epicsThreadId
+of a thread that is not joinable gets invalidated as soon as its main function
+returns.
-
A thread which was created with the joinable flag set may itself call
-epicsThreadMustJoin() passing its own epicsThreadId. This marks the
-thread as no longer being joinable, so it will then free the thread resources
-itself when its main function returns. The epicsThreadId for a thread that is
-not joinable gets invalidated as soon as its main function returns.
Launchpad Bugs
From b4ee452c4d39424f9b5a8910d85776eb979e4211 Mon Sep 17 00:00:00 2001
From: Andrew Johnson
Date: Tue, 2 Jul 2019 18:17:47 -0500
Subject: [PATCH 070/281] Test that a join actually delays the parent
---
modules/libcom/test/epicsThreadTest.cpp | 21 ++++++++++++++++++++-
1 file changed, 20 insertions(+), 1 deletion(-)
diff --git a/modules/libcom/test/epicsThreadTest.cpp b/modules/libcom/test/epicsThreadTest.cpp
index d07552d15..4796438a1 100644
--- a/modules/libcom/test/epicsThreadTest.cpp
+++ b/modules/libcom/test/epicsThreadTest.cpp
@@ -102,6 +102,11 @@ void dowait(void *arg)
epicsThreadSleep(0.1);
}
+void dodelay(void *arg)
+{
+ epicsThreadSleep(2.0);
+}
+
void joinTests(void *arg)
{
struct joinStuff *stuff = (struct joinStuff *) arg;
@@ -118,6 +123,20 @@ void joinTests(void *arg)
stuff->trigger->signal();
epicsThreadMustJoin(tid);
+ // Parent gets delayed until task finishes
+ epicsTime start, end;
+ start = epicsTime::getCurrent();
+ tid = epicsThreadCreateOpt("delay",
+ &dodelay, 0, stuff->opts);
+ epicsThreadMustJoin(tid);
+ end = epicsTime::getCurrent();
+ double duration = end - start;
+#ifndef EPICS_THREAD_CAN_JOIN
+ testTodoBegin("Thread join doesn't work");
+#endif
+ testOk(duration > 1.0, "Join delayed parent (%g seconds)", duration);
+ testTodoEnd();
+
// This is a no-op
epicsThreadId self = epicsThreadGetIdSelf();
epicsThreadMustJoin(self);
@@ -192,7 +211,7 @@ static void testOkToBlock()
MAIN(epicsThreadTest)
{
- testPlan(12);
+ testPlan(13);
unsigned int ncpus = epicsThreadGetCPUs();
testDiag("System has %u CPUs", ncpus);
From 44ea66aaaf83c09a5991f52a13d1c436e61ce937 Mon Sep 17 00:00:00 2001
From: Andrew Johnson
Date: Sun, 7 Jul 2019 23:11:21 -0500
Subject: [PATCH 071/281] Add checks and summary of free-list size to decTest.c
---
src/std/filters/test/decTest.c | 24 ++++++++++++++++++------
1 file changed, 18 insertions(+), 6 deletions(-)
diff --git a/src/std/filters/test/decTest.c b/src/std/filters/test/decTest.c
index b6b2276c6..19aedc6dd 100644
--- a/src/std/filters/test/decTest.c
+++ b/src/std/filters/test/decTest.c
@@ -72,8 +72,7 @@ static void mustDrop(dbChannel *pch, db_field_log *pfl, char* m) {
testOk(newFree == oldFree + 1, "field_log was freed - %d+1 => %d",
oldFree, newFree);
- if (newFree == oldFree)
- db_delete_field_log(pfl);
+ db_delete_field_log(pfl2);
}
static void mustPass(dbChannel *pch, db_field_log *pfl, char* m) {
@@ -85,8 +84,7 @@ static void mustPass(dbChannel *pch, db_field_log *pfl, char* m) {
testOk(newFree == oldFree, "field_log was not freed - %d => %d",
oldFree, newFree);
- if (newFree == oldFree)
- db_delete_field_log(pfl);
+ db_delete_field_log(pfl2);
}
static void checkAndOpenChannel(dbChannel *pch, const chFilterPlugin *plug) {
@@ -125,10 +123,10 @@ MAIN(decTest)
const chFilterPlugin *plug;
char myname[] = "dec";
db_field_log *pfl[10];
- int i;
+ int i, logsFree, logsFinal;
dbEventCtx evtctx;
- testPlan(103);
+ testPlan(104);
testdbPrepare();
@@ -164,6 +162,11 @@ MAIN(decTest)
testOk(!!(pch = dbChannelCreate("x.VAL{\"dec\":{\"n\":1}}")),
"dbChannel with plugin dec (n=1) created");
+ /* Start the free-list */
+ db_delete_field_log(db_create_read_log(pch));
+ logsFree = db_available_logs();
+ testDiag("%d field_logs on free-list", logsFree);
+
checkAndOpenChannel(pch, plug);
for (i = 0; i < 5; i++) {
@@ -181,6 +184,8 @@ MAIN(decTest)
dbChannelDelete(pch);
+ testDiag("%d field_logs on free-list", db_available_logs());
+
/* Decimation (N=2) */
testHead("Decimation (n=2)");
@@ -209,6 +214,8 @@ MAIN(decTest)
dbChannelDelete(pch);
+ testDiag("%d field_logs on free-list", db_available_logs());
+
/* Decimation (N=3) */
testHead("Decimation (n=3)");
@@ -237,6 +244,8 @@ MAIN(decTest)
dbChannelDelete(pch);
+ testDiag("%d field_logs on free-list", db_available_logs());
+
/* Decimation (N=4) */
testHead("Decimation (n=4)");
@@ -265,6 +274,9 @@ MAIN(decTest)
dbChannelDelete(pch);
+ logsFinal = db_available_logs();
+ testOk(logsFree == logsFinal, "%d field_logs on free-list", logsFinal);
+
db_close_events(evtctx);
testIocShutdownOk();
From 8ff6ce4821a98c7d0ca22ddbb3155479a535118b Mon Sep 17 00:00:00 2001
From: Andrew Johnson
Date: Sun, 7 Jul 2019 23:30:07 -0500
Subject: [PATCH 072/281] Fix leak in sync filter (while, unless modes)
Always release field logs when we drop them.
Adjust how first and after modes work to make them easier to test.
Change stream checking code, fix leaks and double frees.
Add mustStash(), mustSwap(), streamReset(), drop mustPassOld().
Modify test code to check free-list count and release all of
the field-logs returned by the filter; it must release any of
the field-logs that it decides to drop.
---
src/std/filters/sync.c | 24 ++--
src/std/filters/test/syncTest.c | 189 +++++++++++++++++++++-----------
2 files changed, 144 insertions(+), 69 deletions(-)
diff --git a/src/std/filters/sync.c b/src/std/filters/sync.c
index c32e9afbd..6b841eab4 100644
--- a/src/std/filters/sync.c
+++ b/src/std/filters/sync.c
@@ -109,7 +109,9 @@ static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) {
passfl = pfl;
pfl = NULL;
}
- break;
+ else
+ db_delete_field_log(pfl);
+ goto save_state;
case syncModeLast:
if (!actstate && my->laststate) {
passfl = my->lastfl;
@@ -121,28 +123,34 @@ static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) {
passfl = pfl;
pfl = NULL;
}
- break;
+ else
+ db_delete_field_log(pfl);
+ goto save_state;
case syncModeWhile:
- if (actstate) {
+ if (actstate)
passfl = pfl;
- }
+ else
+ db_delete_field_log(pfl);
goto no_shift;
case syncModeUnless:
- if (!actstate) {
+ if (!actstate)
passfl = pfl;
- }
+ else
+ db_delete_field_log(pfl);
goto no_shift;
}
if (my->lastfl)
db_delete_field_log(my->lastfl);
my->lastfl = pfl;
- my->laststate = actstate;
/* since no copy is made we can't keep a reference to the returned fl */
assert(my->lastfl != passfl);
- no_shift:
+save_state:
+ my->laststate = actstate;
+
+no_shift:
return passfl;
}
diff --git a/src/std/filters/test/syncTest.c b/src/std/filters/test/syncTest.c
index 9af44afd7..47d6e268b 100644
--- a/src/std/filters/test/syncTest.c
+++ b/src/std/filters/test/syncTest.c
@@ -66,31 +66,92 @@ static void testHead (char* title) {
testDiag("--------------------------------------------------------");
}
-static void mustDrop(dbChannel *pch, db_field_log *pfl2, char* m) {
- db_field_log *pfl = dbChannelRunPreChain(pch, pfl2);
- testOk(NULL == pfl, "filter drops field_log (%s)", m);
+/*
+ * Use mustDrop() and mustPass() to test filters with no memory
+ * of previous field_log pointers.
+ */
+static void mustDrop(dbChannel *pch, db_field_log *pfl, char* m) {
+ int oldFree = db_available_logs();
+ db_field_log *pfl2 = dbChannelRunPreChain(pch, pfl);
+ int newFree = db_available_logs();
+
+ testOk(NULL == pfl2, "filter drops field_log (%s)", m);
+ testOk(newFree == oldFree + 1, "a field_log was freed - %d+1 => %d",
+ oldFree, newFree);
+
+ db_delete_field_log(pfl2);
}
-static void mustPassTwice(dbChannel *pch, db_field_log *pfl2, char* m) {
- db_field_log *pfl;
+static void mustPass(dbChannel *pch, db_field_log *pfl, char* m) {
+ int oldFree = db_available_logs();
+ db_field_log *pfl2 = dbChannelRunPreChain(pch, pfl);
+ int newFree = db_available_logs();
+
+ testOk(pfl == pfl2, "filter passes field_log (%s)", m);
+ testOk(newFree == oldFree, "no field_logs were freed - %d => %d",
+ oldFree, newFree);
+
+ db_delete_field_log(pfl2);
+}
+
+/*
+ * Use mustStash() and mustSwap() to test filters that save
+ * field_log pointers and return them later.
+ *
+ * mustStash() expects the filter to save the current pointer
+ * (freeing any previously saved pointer) and return NULL.
+ * mustSwap() expects the filter to return the previously
+ * saved pointer and save the current pointer.
+ */
+static db_field_log *stashed;
+
+static void streamReset(void) {
+ stashed = NULL;
+}
+
+static void mustStash(dbChannel *pch, db_field_log *pfl, char* m) {
+ int oldFree = db_available_logs();
+ db_field_log *pfl2 = dbChannelRunPreChain(pch, pfl);
+ int newFree = db_available_logs();
+
+ testOk(NULL == pfl2, "filter stashes field_log (%s)", m);
+ if (stashed) {
+ testOk(newFree == oldFree + 1, "a field_log was freed - %d+1 => %d",
+ oldFree, newFree);
+ }
+ else {
+ testOk(newFree == oldFree, "no field_logs were freed - %d => %d",
+ oldFree, newFree);
+ }
+ stashed = pfl;
+ db_delete_field_log(pfl2);
+}
+
+static void mustSwap(dbChannel *pch, db_field_log *pfl, char* m) {
+ int oldFree = db_available_logs();
+ db_field_log *pfl2 = dbChannelRunPreChain(pch, pfl);
+ int newFree = db_available_logs();
+
+ testOk(stashed == pfl2, "filter returns stashed field log (%s)", m);
+ testOk(newFree == oldFree, "no field_logs were freed - %d => %d",
+ oldFree, newFree);
+
+ stashed = pfl;
+ db_delete_field_log(pfl2);
+}
+
+static void mustPassTwice(dbChannel *pch, db_field_log *pfl, char* m) {
+ int oldFree = db_available_logs(), newFree;
+ db_field_log *pfl2;
testDiag("%s: filter must pass twice", m);
- pfl = dbChannelRunPreChain(pch, pfl2);
+ pfl2 = dbChannelRunPreChain(pch, pfl);
testOk(pfl2 == pfl, "call 1 does not drop or replace field_log");
- pfl = dbChannelRunPreChain(pch, pfl2);
+ pfl2 = dbChannelRunPreChain(pch, pfl);
testOk(pfl2 == pfl, "call 2 does not drop or replace field_log");
-}
-
-static void mustPassOld(dbChannel *pch, db_field_log *old, db_field_log *cur, char* m) {
- db_field_log *pfl = dbChannelRunPreChain(pch, cur);
-
- testOk(old == pfl, "filter passes previous field log (%s)", m);
-}
-
-static void mustPass(dbChannel *pch, db_field_log *cur, char* m) {
- db_field_log *pfl = dbChannelRunPreChain(pch, cur);
-
- testOk(cur == pfl, "filter passes field_log (%s)", m);
+ newFree = db_available_logs();
+ testOk(newFree == oldFree, "no field_logs were freed - %d => %d",
+ oldFree, newFree);
}
static void checkCtxRead(dbChannel *pch, dbStateId id) {
@@ -138,10 +199,10 @@ MAIN(syncTest)
const chFilterPlugin *plug;
char myname[] = "sync";
db_field_log *pfl[10];
- int i;
+ int i, logsFree, logsFinal;
dbEventCtx evtctx;
- testPlan(139);
+ testPlan(214);
testdbPrepare();
@@ -176,9 +237,14 @@ MAIN(syncTest)
testOk(!!(pch = dbChannelCreate("x.VAL{\"sync\":{\"m\":\"while\",\"s\":\"red\"}}")),
"dbChannel with plugin sync (m='while' s='red') created");
+ /* Start the free-list */
+ db_delete_field_log(db_create_read_log(pch));
+ logsFree = db_available_logs();
+ testDiag("%d field_logs on free-list", logsFree);
+
checkAndOpenChannel(pch, plug);
- for (i = 0; i < 10; i++) {
+ for (i = 0; i < 9; i++) {
pfl[i] = db_create_read_log(pch);
fl_setup(pch, pfl[i], 120 + i);
}
@@ -198,11 +264,10 @@ MAIN(syncTest)
mustDrop(pch, pfl[7], "state=FALSE, log7");
mustDrop(pch, pfl[8], "state=FALSE, log8");
- for (i = 0; i < 10; i++)
- db_delete_field_log(pfl[i]);
-
dbChannelDelete(pch);
+ testDiag("%d field_logs on free-list", db_available_logs());
+
/* mode UNLESS */
testHead("Mode UNLESS (m='unless', s='red')");
@@ -211,7 +276,7 @@ MAIN(syncTest)
checkAndOpenChannel(pch, plug);
- for (i = 0; i < 10; i++) {
+ for (i = 0; i < 9; i++) {
pfl[i] = db_create_read_log(pch);
fl_setup(pch, pfl[i], 120 + i);
}
@@ -231,11 +296,10 @@ MAIN(syncTest)
mustPass(pch, pfl[7], "state=FALSE, log7");
mustPass(pch, pfl[8], "state=FALSE, log8");
- for (i = 0; i < 10; i++)
- db_delete_field_log(pfl[i]);
-
dbChannelDelete(pch);
+ testDiag("%d field_logs on free-list", db_available_logs());
+
/* mode BEFORE */
testHead("Mode BEFORE (m='before', s='red')");
@@ -251,24 +315,25 @@ MAIN(syncTest)
testDiag("Test event stream");
+ streamReset();
dbStateClear(red);
- mustDrop(pch, pfl[0], "state=FALSE, log0");
- mustDrop(pch, pfl[1], "state=FALSE, log1");
- mustDrop(pch, pfl[2], "state=FALSE, log2");
+ mustStash(pch, pfl[0], "state=FALSE, log0");
+ mustStash(pch, pfl[1], "state=FALSE, log1");
+ mustStash(pch, pfl[2], "state=FALSE, log2");
dbStateSet(red);
- mustPassOld(pch, pfl[2], pfl[3], "state=TRUE, log3, pass=log2");
- mustDrop(pch, pfl[4], "state=TRUE, log4");
- mustDrop(pch, pfl[5], "state=TRUE, log5");
- mustDrop(pch, pfl[6], "state=TRUE, log6");
+ mustSwap(pch, pfl[3], "state=TRUE, log3");
+ mustStash(pch, pfl[4], "state=TRUE, log4");
+ mustStash(pch, pfl[5], "state=TRUE, log5");
+ mustStash(pch, pfl[6], "state=TRUE, log6");
dbStateClear(red);
- mustDrop(pch, pfl[7], "state=FALSE, log7");
- mustDrop(pch, pfl[8], "state=FALSE, log8");
- mustDrop(pch, pfl[9], "state=FALSE, log9");
-
- db_delete_field_log(pfl[2]);
+ mustStash(pch, pfl[7], "state=FALSE, log7");
+ mustStash(pch, pfl[8], "state=FALSE, log8");
+ mustStash(pch, pfl[9], "state=FALSE, log9");
dbChannelDelete(pch);
+ testDiag("%d field_logs on free-list", db_available_logs());
+
/* mode FIRST */
testHead("Mode FIRST (m='first', s='red')");
@@ -277,13 +342,14 @@ MAIN(syncTest)
checkAndOpenChannel(pch, plug);
- for (i = 0; i < 10; i++) {
+ for (i = 0; i < 9; i++) {
pfl[i] = db_create_read_log(pch);
fl_setup(pch, pfl[i], 120 + i);
}
testDiag("Test event stream");
+ streamReset();
dbStateClear(red);
mustDrop(pch, pfl[0], "state=FALSE, log0");
mustDrop(pch, pfl[1], "state=FALSE, log1");
@@ -297,11 +363,10 @@ MAIN(syncTest)
mustDrop(pch, pfl[7], "state=FALSE, log7");
mustDrop(pch, pfl[8], "state=FALSE, log8");
- db_delete_field_log(pfl[3]);
- db_delete_field_log(pfl[9]);
-
dbChannelDelete(pch);
+ testDiag("%d field_logs on free-list", db_available_logs());
+
/* mode LAST */
testHead("Mode LAST (m='last', s='red')");
@@ -317,24 +382,25 @@ MAIN(syncTest)
testDiag("Test event stream");
+ streamReset();
dbStateClear(red);
- mustDrop(pch, pfl[0], "state=FALSE, log0");
- mustDrop(pch, pfl[1], "state=FALSE, log1");
- mustDrop(pch, pfl[2], "state=FALSE, log2");
+ mustStash(pch, pfl[0], "state=FALSE, log0");
+ mustStash(pch, pfl[1], "state=FALSE, log1");
+ mustStash(pch, pfl[2], "state=FALSE, log2");
dbStateSet(red);
- mustDrop(pch, pfl[3], "state=TRUE, log3");
- mustDrop(pch, pfl[4], "state=TRUE, log4");
- mustDrop(pch, pfl[5], "state=TRUE, log5");
+ mustStash(pch, pfl[3], "state=TRUE, log3");
+ mustStash(pch, pfl[4], "state=TRUE, log4");
+ mustStash(pch, pfl[5], "state=TRUE, log5");
dbStateClear(red);
- mustPassOld(pch, pfl[5], pfl[6], "state=TRUE, log6, pass=log5");
- mustDrop(pch, pfl[7], "state=FALSE, log7");
- mustDrop(pch, pfl[8], "state=FALSE, log8");
- mustDrop(pch, pfl[9], "state=FALSE, log9");
-
- db_delete_field_log(pfl[5]);
+ mustSwap(pch, pfl[6], "state=TRUE, log6");
+ mustStash(pch, pfl[7], "state=FALSE, log7");
+ mustStash(pch, pfl[8], "state=FALSE, log8");
+ mustStash(pch, pfl[9], "state=FALSE, log9");
dbChannelDelete(pch);
+ testDiag("%d field_logs on free-list", db_available_logs());
+
/* mode AFTER */
testHead("Mode AFTER (m='after', s='red')");
@@ -343,13 +409,14 @@ MAIN(syncTest)
checkAndOpenChannel(pch, plug);
- for (i = 0; i < 10; i++) {
+ for (i = 0; i < 9; i++) {
pfl[i] = db_create_read_log(pch);
fl_setup(pch, pfl[i], 120 + i);
}
testDiag("Test event stream");
+ streamReset();
dbStateClear(red);
mustDrop(pch, pfl[0], "state=FALSE, log0");
mustDrop(pch, pfl[1], "state=FALSE, log1");
@@ -363,11 +430,11 @@ MAIN(syncTest)
mustDrop(pch, pfl[7], "state=FALSE, log7");
mustDrop(pch, pfl[8], "state=FALSE, log8");
- db_delete_field_log(pfl[6]);
- db_delete_field_log(pfl[9]);
-
dbChannelDelete(pch);
+ logsFinal = db_available_logs();
+ testOk(logsFree == logsFinal, "%d field_logs on free-list", logsFinal);
+
db_close_events(evtctx);
testIocShutdownOk();
From cac3e2dc3bb22b9892729a0eb224f9222daf4544 Mon Sep 17 00:00:00 2001
From: Andrew Johnson
Date: Sun, 7 Jul 2019 23:32:12 -0500
Subject: [PATCH 073/281] Add checks of freelist to dbndTest.c
---
src/std/filters/test/dbndTest.c | 29 ++++++++++++++++++++++++++++-
1 file changed, 28 insertions(+), 1 deletion(-)
diff --git a/src/std/filters/test/dbndTest.c b/src/std/filters/test/dbndTest.c
index b35b9a6cc..dc0c3e20e 100644
--- a/src/std/filters/test/dbndTest.c
+++ b/src/std/filters/test/dbndTest.c
@@ -62,6 +62,7 @@ static void changeValue(db_field_log *pfl2, long val) {
}
static void mustPassOnce(dbChannel *pch, db_field_log *pfl2, char* m, double d, long val) {
+ int oldFree = db_available_logs(), newFree;
db_field_log *pfl;
changeValue(pfl2, val);
@@ -71,18 +72,26 @@ static void mustPassOnce(dbChannel *pch, db_field_log *pfl2, char* m, double d,
testOk(fl_equal(pfl, pfl2), "call 1 does not change field_log data");
pfl = dbChannelRunPreChain(pch, pfl2);
testOk(NULL == pfl, "call 2 drops field_log");
+ newFree = db_available_logs();
+ testOk(newFree == oldFree + 1, "field_log was freed - %d+1 => %d",
+ oldFree, newFree);
}
static void mustDrop(dbChannel *pch, db_field_log *pfl2, char* m, double d, long val) {
+ int oldFree = db_available_logs(), newFree;
db_field_log *pfl;
changeValue(pfl2, val);
testDiag("mode=%s delta=%g filter must drop", m, d);
pfl = dbChannelRunPreChain(pch, pfl2);
testOk(NULL == pfl, "call 1 drops field_log");
+ newFree = db_available_logs();
+ testOk(newFree == oldFree + 1, "field_log was freed - %d+1 => %d",
+ oldFree, newFree);
}
static void mustPassTwice(dbChannel *pch, db_field_log *pfl2, char* m, double d, long val) {
+ int oldFree = db_available_logs(), newFree;
db_field_log *pfl;
changeValue(pfl2, val);
@@ -93,6 +102,9 @@ static void mustPassTwice(dbChannel *pch, db_field_log *pfl2, char* m, double d,
pfl = dbChannelRunPreChain(pch, pfl2);
testOk(pfl2 == pfl, "call 2 does not drop or replace field_log");
testOk(fl_equal(pfl, pfl2), "call 2 does not change field_log data");
+ newFree = db_available_logs();
+ testOk(newFree == oldFree, "field_log was not freed - %d => %d",
+ oldFree, newFree);
}
static void testHead (char* title) {
@@ -113,8 +125,9 @@ MAIN(dbndTest)
db_field_log *pfl2;
db_field_log fl1;
dbEventCtx evtctx;
+ int logsFree, logsFinal;
- testPlan(59);
+ testPlan(77);
testdbPrepare();
@@ -135,6 +148,11 @@ MAIN(dbndTest)
testOk(!!(pch = dbChannelCreate("x.VAL{\"dbnd\":{}}")), "dbChannel with plugin dbnd (delta=0) created");
testOk((ellCount(&pch->filters) == 1), "channel has one plugin");
+ /* Start the free-list */
+ db_delete_field_log(db_create_read_log(pch));
+ logsFree = db_available_logs();
+ testDiag("%d field_logs on free-list", logsFree);
+
memset(&fl, PATTERN, sizeof(fl));
fl1 = fl;
node = ellFirst(&pch->filters);
@@ -176,6 +194,8 @@ MAIN(dbndTest)
dbChannelDelete(pch);
+ testDiag("%d field_logs on free-list", db_available_logs());
+
/* Delta = -1: pass any update */
testHead("Delta = -1: pass any update");
@@ -192,6 +212,8 @@ MAIN(dbndTest)
db_delete_field_log(pfl2);
dbChannelDelete(pch);
+ testDiag("%d field_logs on free-list", db_available_logs());
+
/* Delta = absolute */
testHead("Delta = absolute");
@@ -224,6 +246,8 @@ MAIN(dbndTest)
dbChannelDelete(pch);
+ testDiag("%d field_logs on free-list", db_available_logs());
+
/* Delta = relative */
testHead("Delta = relative");
@@ -275,6 +299,9 @@ MAIN(dbndTest)
dbChannelDelete(pch);
+ logsFinal = db_available_logs();
+ testOk(logsFree == logsFinal, "%d field_logs on free-list", logsFinal);
+
db_close_events(evtctx);
testIocShutdownOk();
From 84c86e67e817e17024cc9fff151a8bfbd61cc89e Mon Sep 17 00:00:00 2001
From: Andrew Johnson
Date: Mon, 8 Jul 2019 12:55:21 -0500
Subject: [PATCH 074/281] Fix valgrind warnings in filter tests
---
src/std/filters/test/dbndTest.c | 2 ++
src/std/filters/test/decTest.c | 2 ++
src/std/filters/test/syncTest.c | 2 ++
3 files changed, 6 insertions(+)
diff --git a/src/std/filters/test/dbndTest.c b/src/std/filters/test/dbndTest.c
index dc0c3e20e..4d70f83ad 100644
--- a/src/std/filters/test/dbndTest.c
+++ b/src/std/filters/test/dbndTest.c
@@ -39,12 +39,14 @@ static int fl_equal(const db_field_log *pfl1, const db_field_log *pfl2) {
static void fl_setup(dbChannel *chan, db_field_log *pfl) {
struct dbCommon *prec = dbChannelRecord(chan);
+ memset(pfl, 0, sizeof(db_field_log));
pfl->ctx = dbfl_context_read;
pfl->type = dbfl_type_val;
pfl->stat = prec->stat;
pfl->sevr = prec->sevr;
pfl->time = prec->time;
pfl->field_type = dbChannelFieldType(chan);
+ pfl->field_size = dbChannelFieldSize(chan);
pfl->no_elements = dbChannelElements(chan);
/*
* use memcpy to avoid a bus error on
diff --git a/src/std/filters/test/decTest.c b/src/std/filters/test/decTest.c
index 19aedc6dd..3b6784248 100644
--- a/src/std/filters/test/decTest.c
+++ b/src/std/filters/test/decTest.c
@@ -39,12 +39,14 @@ static int fl_equal(const db_field_log *pfl1, const db_field_log *pfl2) {
static void fl_setup(dbChannel *chan, db_field_log *pfl, long val) {
struct dbCommon *prec = dbChannelRecord(chan);
+ memset(pfl, 0, sizeof(db_field_log));
pfl->ctx = dbfl_context_event;
pfl->type = dbfl_type_val;
pfl->stat = prec->stat;
pfl->sevr = prec->sevr;
pfl->time = prec->time;
pfl->field_type = DBF_LONG;
+ pfl->field_size = sizeof(epicsInt32);
pfl->no_elements = 1;
/*
* use memcpy to avoid a bus error on
diff --git a/src/std/filters/test/syncTest.c b/src/std/filters/test/syncTest.c
index 47d6e268b..9be23b05d 100644
--- a/src/std/filters/test/syncTest.c
+++ b/src/std/filters/test/syncTest.c
@@ -42,12 +42,14 @@ static int fl_equal(const db_field_log *pfl1, const db_field_log *pfl2) {
static void fl_setup(dbChannel *chan, db_field_log *pfl, long val) {
struct dbCommon *prec = dbChannelRecord(chan);
+ memset(pfl, 0, sizeof(db_field_log));
pfl->ctx = dbfl_context_event;
pfl->type = dbfl_type_val;
pfl->stat = prec->stat;
pfl->sevr = prec->sevr;
pfl->time = prec->time;
pfl->field_type = DBF_LONG;
+ pfl->field_size = sizeof(epicsInt32);
pfl->no_elements = 1;
/*
* use memcpy to avoid a bus error on
From 1a94376c19fda2073a0e8c2ce3ea34cf58367f2c Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Tue, 9 Jul 2019 18:42:19 -0700
Subject: [PATCH 075/281] restore ModuleDirs in caPerlApp
---
modules/ca/src/template/top/caPerlApp/caget.pl | 3 ++-
modules/ca/src/template/top/caPerlApp/cainfo.pl | 3 ++-
modules/ca/src/template/top/caPerlApp/camonitor.pl | 3 ++-
modules/ca/src/template/top/caPerlApp/caput.pl | 3 ++-
4 files changed, 8 insertions(+), 4 deletions(-)
diff --git a/modules/ca/src/template/top/caPerlApp/caget.pl b/modules/ca/src/template/top/caPerlApp/caget.pl
index d206d8a36..0d9af37a1 100644
--- a/modules/ca/src/template/top/caPerlApp/caget.pl
+++ b/modules/ca/src/template/top/caPerlApp/caget.pl
@@ -4,8 +4,9 @@ use strict;
# This construct sets @INC to search lib/perl of all RELEASE entries
use FindBin qw($Bin);
-use lib ("$Bin/../../lib/perl");
+use lib ($Bin, "$Bin/../../lib/perl");
use _APPNAME_ModuleDirs;
+no lib $Bin;
use Getopt::Std;
use Scalar::Util qw(looks_like_number);
diff --git a/modules/ca/src/template/top/caPerlApp/cainfo.pl b/modules/ca/src/template/top/caPerlApp/cainfo.pl
index 4d4a1ffbe..3e38e8baf 100644
--- a/modules/ca/src/template/top/caPerlApp/cainfo.pl
+++ b/modules/ca/src/template/top/caPerlApp/cainfo.pl
@@ -4,8 +4,9 @@ use strict;
# This construct sets @INC to search lib/perl of all RELEASE entries
use FindBin qw($Bin);
-use lib ("$Bin/../../lib/perl");
+use lib ($Bin, "$Bin/../../lib/perl");
use _APPNAME_ModuleDirs;
+no lib $Bin;
use Getopt::Std;
use CA;
diff --git a/modules/ca/src/template/top/caPerlApp/camonitor.pl b/modules/ca/src/template/top/caPerlApp/camonitor.pl
index c426fa536..564688d68 100644
--- a/modules/ca/src/template/top/caPerlApp/camonitor.pl
+++ b/modules/ca/src/template/top/caPerlApp/camonitor.pl
@@ -4,8 +4,9 @@ use strict;
# This construct sets @INC to search lib/perl of all RELEASE entries
use FindBin qw($Bin);
-use lib ("$Bin/../../lib/perl");
+use lib ($Bin, "$Bin/../../lib/perl");
use _APPNAME_ModuleDirs;
+no lib $Bin;
use Getopt::Std;
use Scalar::Util qw(looks_like_number);
diff --git a/modules/ca/src/template/top/caPerlApp/caput.pl b/modules/ca/src/template/top/caPerlApp/caput.pl
index 03842d397..64ff9cbda 100644
--- a/modules/ca/src/template/top/caPerlApp/caput.pl
+++ b/modules/ca/src/template/top/caPerlApp/caput.pl
@@ -4,8 +4,9 @@ use strict;
# This construct sets @INC to search lib/perl of all RELEASE entries
use FindBin qw($Bin);
-use lib ("$Bin/../../lib/perl");
+use lib ($Bin, "$Bin/../../lib/perl");
use _APPNAME_ModuleDirs;
+no lib $Bin;
use Getopt::Std;
use CA;
From 29fc49199d3c42e1df9fc2ba0ba7bf40635f19e0 Mon Sep 17 00:00:00 2001
From: Andrew Johnson
Date: Wed, 31 Jul 2019 14:29:18 -0500
Subject: [PATCH 076/281] Update versions after tag, set
DEVELOPMENT_FLAG/DEV_SNAPSHOT
---
configure/CONFIG_BASE_VERSION | 10 +++++-----
modules/ca/configure/CONFIG_CA_VERSION | 12 ++++++++++--
modules/database/configure/CONFIG_DATABASE_VERSION | 12 ++++++++++--
modules/libcom/configure/CONFIG_LIBCOM_VERSION | 12 ++++++++++--
4 files changed, 35 insertions(+), 11 deletions(-)
diff --git a/configure/CONFIG_BASE_VERSION b/configure/CONFIG_BASE_VERSION
index 53753d7e4..4e61e48cd 100644
--- a/configure/CONFIG_BASE_VERSION
+++ b/configure/CONFIG_BASE_VERSION
@@ -52,12 +52,12 @@ EPICS_MODIFICATION = 3
# 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
-# Between official releases, the EPICS_PATCH_LEVEL gets incremented
-# and a -DEV suffix is added (similar to the Maven -SNAPSHOT versions)
-EPICS_DEV_SNAPSHOT=
-#EPICS_DEV_SNAPSHOT=-DEV
+# 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
#EPICS_DEV_SNAPSHOT=-pre1
#EPICS_DEV_SNAPSHOT=-pre1-DEV
#EPICS_DEV_SNAPSHOT=-pre2
diff --git a/modules/ca/configure/CONFIG_CA_VERSION b/modules/ca/configure/CONFIG_CA_VERSION
index 8a6649666..cac0fcbfe 100644
--- a/modules/ca/configure/CONFIG_CA_VERSION
+++ b/modules/ca/configure/CONFIG_CA_VERSION
@@ -1,4 +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 = 4
-EPICS_CA_DEVELOPMENT_FLAG = 0
+EPICS_CA_MAINTENANCE_VERSION = 5
+
+# Development flag, must be zero for official release versions
+
+EPICS_CA_DEVELOPMENT_FLAG = 1
+
+# Immediately after an official release the MAINTENANCE_VERSION
+# will be incremented and the DEVELOPMENT_FLAG set to 1
diff --git a/modules/database/configure/CONFIG_DATABASE_VERSION b/modules/database/configure/CONFIG_DATABASE_VERSION
index ad8d54ab5..a43738cfb 100644
--- a/modules/database/configure/CONFIG_DATABASE_VERSION
+++ b/modules/database/configure/CONFIG_DATABASE_VERSION
@@ -1,4 +1,12 @@
+# Version number for the database APIs and shared library
+
EPICS_DATABASE_MAJOR_VERSION = 3
EPICS_DATABASE_MINOR_VERSION = 17
-EPICS_DATABASE_MAINTENANCE_VERSION = 4
-EPICS_DATABASE_DEVELOPMENT_FLAG = 0
+EPICS_DATABASE_MAINTENANCE_VERSION = 5
+
+# Development flag, must be zero for official release versions
+
+EPICS_DATABASE_DEVELOPMENT_FLAG = 1
+
+# Immediately after an official release the MAINTENANCE_VERSION
+# will be incremented and the DEVELOPMENT_FLAG set to 1
diff --git a/modules/libcom/configure/CONFIG_LIBCOM_VERSION b/modules/libcom/configure/CONFIG_LIBCOM_VERSION
index f5d91037c..92c374f88 100644
--- a/modules/libcom/configure/CONFIG_LIBCOM_VERSION
+++ b/modules/libcom/configure/CONFIG_LIBCOM_VERSION
@@ -1,4 +1,12 @@
+# Version number for the libcom APIs and shared library
+
EPICS_LIBCOM_MAJOR_VERSION = 3
EPICS_LIBCOM_MINOR_VERSION = 17
-EPICS_LIBCOM_MAINTENANCE_VERSION = 5
-EPICS_LIBCOM_DEVELOPMENT_FLAG = 0
+EPICS_LIBCOM_MAINTENANCE_VERSION = 6
+
+# Development flag, must be zero for official release versions
+
+EPICS_LIBCOM_DEVELOPMENT_FLAG = 1
+
+# Immediately after an official release the MAINTENANCE_VERSION
+# will be incremented and the DEVELOPMENT_FLAG set to 1
From 969ffa3598ff36738328afec78882f6e751377ee Mon Sep 17 00:00:00 2001
From: Andrew Johnson
Date: Wed, 31 Jul 2019 14:34:42 -0500
Subject: [PATCH 077/281] Checklist: Update version update instructions
---
documentation/ReleaseChecklist.html | 52 +++++++++++++++++------------
1 file changed, 31 insertions(+), 21 deletions(-)
diff --git a/documentation/ReleaseChecklist.html b/documentation/ReleaseChecklist.html
index 81784e8cc..a29552dc2 100644
--- a/documentation/ReleaseChecklist.html
+++ b/documentation/ReleaseChecklist.html
@@ -289,13 +289,20 @@ starting at Release Approval.
Release Manager
Edit and commit changes to the EPICS Base version number file and
- the embedded module version files:
+ these embedded module version files:
Version numbers should be set according to the level of changes
+ made since the last release. Note that the MAINTENANCE_VERSION or
+ 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.
+
@@ -305,7 +312,26 @@ starting at Release Approval.
cd base-7.0
git tag -m 'ANJ: Tagged for 7.0.3' R7.0.3
- Don't push the new tag to the Launchpad repository yet.
+
Don't push these commits or the new tag to the Launchpad repository
+ yet.
+
+
+
+
Release Manager
+
Edit and commit changes to the EPICS Base version number file and
+ the embedded module version files as follows:
+
Version numbers should be set for the next expected patch/maintenance
+ 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".
Test the tar file by extracting its contents and building it on at
- least one supported platform. When this succeeds the new git tag can be
- pushed to the Launchpad repository:
+ least one supported platform. When this succeeds the commits and new git
+ tag can be pushed to the Launchpad repository:
git push --follow-tags upstream 7.0
-
-
-
Release Manager
-
Edit and commit changes to the EPICS Base version number file and
- the embedded module version files:
-
Follow instructions on
Add a page for a new release to create a new release webpage (not
- required for a patch release though, just edit the existing page).
+ required for a patch release, just edit the existing page). Update the
+ TablePress "Point Releases" table and add the new download, and adjust
+ the XYZ Html Snippet for the series download.
+
From fd48ee9aeced4096a96a8ba310ac19980e688524 Mon Sep 17 00:00:00 2001
From: Andrew Johnson
Date: Tue, 13 Aug 2019 11:21:26 -0500
Subject: [PATCH 079/281] Update submodules
Adjust comments in the embedded module CONFIG_*_VERSION files
---
modules/ca/configure/CONFIG_CA_VERSION | 4 ++--
modules/database/configure/CONFIG_DATABASE_VERSION | 4 ++--
modules/libcom/configure/CONFIG_LIBCOM_VERSION | 4 ++--
modules/normativeTypes | 2 +-
modules/pvAccess | 2 +-
modules/pvData | 2 +-
modules/pvDatabase | 2 +-
modules/pva2pva | 2 +-
modules/pvaClient | 2 +-
9 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/modules/ca/configure/CONFIG_CA_VERSION b/modules/ca/configure/CONFIG_CA_VERSION
index cac0fcbfe..57bcd89ac 100644
--- a/modules/ca/configure/CONFIG_CA_VERSION
+++ b/modules/ca/configure/CONFIG_CA_VERSION
@@ -4,9 +4,9 @@ EPICS_CA_MAJOR_VERSION = 4
EPICS_CA_MINOR_VERSION = 13
EPICS_CA_MAINTENANCE_VERSION = 5
-# Development flag, must be zero for official release versions
+# Development flag, set to zero for release versions
EPICS_CA_DEVELOPMENT_FLAG = 1
-# Immediately after an official release the MAINTENANCE_VERSION
+# Immediately after a release the MAINTENANCE_VERSION
# will be incremented and the DEVELOPMENT_FLAG set to 1
diff --git a/modules/database/configure/CONFIG_DATABASE_VERSION b/modules/database/configure/CONFIG_DATABASE_VERSION
index a43738cfb..ebf04180a 100644
--- a/modules/database/configure/CONFIG_DATABASE_VERSION
+++ b/modules/database/configure/CONFIG_DATABASE_VERSION
@@ -4,9 +4,9 @@ EPICS_DATABASE_MAJOR_VERSION = 3
EPICS_DATABASE_MINOR_VERSION = 17
EPICS_DATABASE_MAINTENANCE_VERSION = 5
-# Development flag, must be zero for official release versions
+# Development flag, set to zero for release versions
EPICS_DATABASE_DEVELOPMENT_FLAG = 1
-# Immediately after an official release the MAINTENANCE_VERSION
+# Immediately after a release the MAINTENANCE_VERSION
# will be incremented and the DEVELOPMENT_FLAG set to 1
diff --git a/modules/libcom/configure/CONFIG_LIBCOM_VERSION b/modules/libcom/configure/CONFIG_LIBCOM_VERSION
index 92c374f88..5953c7175 100644
--- a/modules/libcom/configure/CONFIG_LIBCOM_VERSION
+++ b/modules/libcom/configure/CONFIG_LIBCOM_VERSION
@@ -4,9 +4,9 @@ EPICS_LIBCOM_MAJOR_VERSION = 3
EPICS_LIBCOM_MINOR_VERSION = 17
EPICS_LIBCOM_MAINTENANCE_VERSION = 6
-# Development flag, must be zero for official release versions
+# Development flag, set to zero for release versions
EPICS_LIBCOM_DEVELOPMENT_FLAG = 1
-# Immediately after an official release the MAINTENANCE_VERSION
+# Immediately after a release the MAINTENANCE_VERSION
# will be incremented and the DEVELOPMENT_FLAG set to 1
diff --git a/modules/normativeTypes b/modules/normativeTypes
index c6168a747..ba33c7443 160000
--- a/modules/normativeTypes
+++ b/modules/normativeTypes
@@ -1 +1 @@
-Subproject commit c6168a74772a93f18facc83631c13db381ff15bb
+Subproject commit ba33c7443c52689641c2a7377e39544d5dd326d2
diff --git a/modules/pvAccess b/modules/pvAccess
index 936f5d35d..c8c3cf4fd 160000
--- a/modules/pvAccess
+++ b/modules/pvAccess
@@ -1 +1 @@
-Subproject commit 936f5d35d845a3c47a55176876a0f607a582cf09
+Subproject commit c8c3cf4fd8b3490221c1e231c354e73735ee3813
diff --git a/modules/pvData b/modules/pvData
index 6ceaa6adb..b903df5d0 160000
--- a/modules/pvData
+++ b/modules/pvData
@@ -1 +1 @@
-Subproject commit 6ceaa6adb0b39dc3e37f064ca7e7bb5eea459d96
+Subproject commit b903df5d0daf08592368be2978efb2d828617a8c
diff --git a/modules/pvDatabase b/modules/pvDatabase
index 70ee85778..d7bd5628d 160000
--- a/modules/pvDatabase
+++ b/modules/pvDatabase
@@ -1 +1 @@
-Subproject commit 70ee85778268bb47397d4cf8aa07e019ccdd8392
+Subproject commit d7bd5628d47f82b2ad2cab2ea97dade98c2ee128
diff --git a/modules/pva2pva b/modules/pva2pva
index ce39c9320..d70a2ff8c 160000
--- a/modules/pva2pva
+++ b/modules/pva2pva
@@ -1 +1 @@
-Subproject commit ce39c932013e3af1d4750394ee69ba20bd13fa74
+Subproject commit d70a2ff8c31bc042477b4997d733b77e6029e8f9
diff --git a/modules/pvaClient b/modules/pvaClient
index aba40922e..246cceae3 160000
--- a/modules/pvaClient
+++ b/modules/pvaClient
@@ -1 +1 @@
-Subproject commit aba40922e6a96519c214c7644bfdf49a8bd0d7f6
+Subproject commit 246cceae3e682ac99413673195fec07dffa77267
From 05cd95a8212551aa0d6205093ea1e2ac9fcdc128 Mon Sep 17 00:00:00 2001
From: Zimoch Dirk
Date: Tue, 13 Aug 2019 20:35:05 -0700
Subject: [PATCH 080/281] astac argument checks
---
modules/database/src/ioc/as/asDbLib.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/modules/database/src/ioc/as/asDbLib.c b/modules/database/src/ioc/as/asDbLib.c
index c0fe192b0..49be480bc 100644
--- a/modules/database/src/ioc/as/asDbLib.c
+++ b/modules/database/src/ioc/as/asDbLib.c
@@ -240,6 +240,10 @@ int astac(const char *pname,const char *user,const char *location)
char *puser;
char *plocation;
+ if (!pname || !user || !location){
+ printf("Usage: astac \"record name\", \"user\", \"host\"\n");
+ return(1);
+ }
paddr = dbCalloc(1,sizeof(DBADDR) + sizeof(ASCLIENTPVT));
pasclientpvt = (ASCLIENTPVT *)(paddr + 1);
status=dbNameToAddr(pname,paddr);
From 2fb41d7a36748fe3e3b8c76019f696044ef5e46b Mon Sep 17 00:00:00 2001
From: Andrew Johnson
Date: Fri, 23 Aug 2019 15:54:32 -0500
Subject: [PATCH 081/281] VxWorks: Measure CPU time-base frequency if necessary
The BSP routine sysTimeBaseFreq() is optional. If not present or
non-functional we measure the frequency of the CPU's time-base
counter (against the OS system clock) once at initialization-time.
---
.../libcom/src/osi/os/vxWorks/osdMonotonic.c | 52 ++++++++++++++-----
1 file changed, 38 insertions(+), 14 deletions(-)
diff --git a/modules/libcom/src/osi/os/vxWorks/osdMonotonic.c b/modules/libcom/src/osi/os/vxWorks/osdMonotonic.c
index e0c6b2278..1e6b0a8ad 100644
--- a/modules/libcom/src/osi/os/vxWorks/osdMonotonic.c
+++ b/modules/libcom/src/osi/os/vxWorks/osdMonotonic.c
@@ -9,10 +9,13 @@
#include
#include
+#include
+#include
#define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE
#include "epicsTypes.h"
#include "epicsTime.h"
+#include "cantProceed.h"
#define NS_PER_SEC 1000000000
@@ -23,6 +26,7 @@ union timebase {
UINT64 u64; /* epicsMonotonicGet() */
};
+static void measureTickRate(void);
#if CPU_FAMILY == PPC
#include
@@ -46,12 +50,15 @@ void osdMonotonicInit(void)
if (sysTimeBaseFreq) {
ticksPerSec = sysTimeBaseFreq();
- if (!ticksPerSec)
- printf("Warning: Failed to set up monotonic time source.\n");
- /* Warn here only if the BSP routine exists but returned 0 */
+ if (ticksPerSec)
+ return;
+
+ /* This should never happen */
+ printf("Warning: sysTimeBaseFreq() present but returned zero.\n");
}
- else
- ticksPerSec = 0; /* Warn on first use */
+
+ /* Fall back to measuring */
+ measureTickRate();
}
@@ -73,8 +80,14 @@ void osdMonotonicInit(void)
{
ticksPerSec = vxCpuIdGetFreq();
- if (!ticksPerSec)
- printf("Warning: Failed to set up monotonic time source.\n");
+ if (ticksPerSec)
+ return;
+
+ /* This should never happen */
+ printf("Warning: vxCpuIdGetFreq() returned zero.\n");
+
+ /* Fall back to measuring */
+ measureTickRate();
}
@@ -96,13 +109,7 @@ epicsUInt64 epicsMonotonicGet(void)
union timebase tbNow;
if (!ticksPerSec) {
- static int warned = 0;
-
- if (!warned) {
- printf("Warning: Monotonic time source is not available.\n");
- warned = 1;
- }
- return 0;
+ cantProceed("Monotonic time source not available.\n");
}
TIMEBASEGET(tbNow);
@@ -111,3 +118,20 @@ epicsUInt64 epicsMonotonicGet(void)
*/
return ((long double) tbNow.u64) * NS_PER_SEC / ticksPerSec;
}
+
+static void measureTickRate(void)
+{
+ union timebase start, end;
+ int sysTicks = sysClkRateGet(); /* 1 second */
+
+ printf("osdMonotonicInit: Measuring CPU time-base frequency ...");
+ fflush(stdout);
+
+ taskDelay(1);
+ TIMEBASEGET(start);
+ taskDelay(sysTicks);
+ TIMEBASEGET(end);
+ ticksPerSec = end.u64 - start.u64;
+
+ printf(" %llu ticks/sec.\n", (unsigned long long) ticksPerSec);
+}
From a625acbb182f5c90810983aed74ecd4df4359d2e Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Sat, 24 Aug 2019 20:30:47 -0700
Subject: [PATCH 082/281] iocsh handle redirect and similar early errors
---
modules/libcom/src/iocsh/iocsh.cpp | 73 +++++++++++++++++-------------
1 file changed, 41 insertions(+), 32 deletions(-)
diff --git a/modules/libcom/src/iocsh/iocsh.cpp b/modules/libcom/src/iocsh/iocsh.cpp
index 7392bca42..b3fe936cb 100644
--- a/modules/libcom/src/iocsh/iocsh.cpp
+++ b/modules/libcom/src/iocsh/iocsh.cpp
@@ -623,9 +623,7 @@ iocshBody (const char *pathname, const char *commandLine, const char *macros)
}
}
- /*
- * Check for existing macro context or construct a new one.
- */
+ // Check for existing context or construct a new one.
context = (iocshContext *) epicsThreadPrivateGet(iocshContextId);
if (!context) {
@@ -654,6 +652,29 @@ iocshBody (const char *pathname, const char *commandLine, const char *macros)
* Read commands till EOF or exit
*/
for (;;) {
+ if(!scope.interactive && scope.errored) {
+ if(scope.onerr==Continue) {
+ /* do nothing */
+
+ } else if(scope.onerr==Break) {
+ ret = -1;
+ fprintf(epicsGetStderr(), "iocsh Error: Break\n" );
+ break;
+
+ } else if(scope.onerr==Halt) {
+ ret = -1;
+ if(scope.timeout<=0.0 || isinf(scope.timeout)) {
+ fprintf(epicsGetStderr(), "iocsh Error: Halt\n" );
+ epicsThreadSuspendSelf();
+ break;
+
+ } else {
+ fprintf(epicsGetStderr(), "iocsh Error: Waiting %.1f sec ...\n", scope.timeout);
+ epicsThreadSleep(scope.timeout);
+ }
+ }
+ }
+
/*
* Read a line
*/
@@ -692,8 +713,10 @@ iocshBody (const char *pathname, const char *commandLine, const char *macros)
* Expand macros
*/
free(line);
- if ((line = macDefExpand(raw, handle)) == NULL)
+ if ((line = macDefExpand(raw, handle)) == NULL) {
+ scope.errored = true;
continue;
+ }
/*
* Skip leading white-space coming from a macro
@@ -706,9 +729,11 @@ iocshBody (const char *pathname, const char *commandLine, const char *macros)
* Echo non-empty lines read from a script.
* Comments delineated with '#-' aren't echoed.
*/
- if ((prompt == NULL) && *line && (commandLine == NULL))
- if ((c != '#') || (line[icin + 1] != '-'))
+ if ((prompt == NULL) && *line && (commandLine == NULL)) {
+ if ((c != '#') || (line[icin + 1] != '-')) {
puts(line);
+ }
+ }
/*
* Ignore lines that became a comment or empty after macro expansion
@@ -732,6 +757,7 @@ iocshBody (const char *pathname, const char *commandLine, const char *macros)
if (newv == NULL) {
fprintf (epicsGetStderr(), "Out of memory!\n");
argc = -1;
+ scope.errored = true;
break;
}
argv = newv;
@@ -805,8 +831,9 @@ iocshBody (const char *pathname, const char *commandLine, const char *macros)
}
else {
if (!sep) {
- if (((c == '"') || (c == '\'')) && !backslash)
+ if (((c == '"') || (c == '\'')) && !backslash) {
quote = c;
+ }
if (redirect != NULL) {
if (redirect->name != NULL) {
argc = -1;
@@ -827,16 +854,20 @@ iocshBody (const char *pathname, const char *commandLine, const char *macros)
}
if (redirect != NULL) {
showError(filename, lineno, "Illegal redirection.");
+ scope.errored = true;
continue;
}
- if (argc < 0)
+ if (argc < 0) {
break;
+ }
if (quote != EOF) {
showError(filename, lineno, "Unbalanced quote.");
+ scope.errored = true;
continue;
}
if (backslash) {
showError(filename, lineno, "Trailing backslash.");
+ scope.errored = true;
continue;
}
if (inword)
@@ -853,7 +884,8 @@ iocshBody (const char *pathname, const char *commandLine, const char *macros)
if (openRedirect(filename, lineno, redirects) < 0)
continue;
startRedirect(filename, lineno, redirects);
- iocshBody(commandFile, NULL, macros);
+ if(iocshBody(commandFile, NULL, macros))
+ scope.errored = true;
stopRedirect(filename, lineno, redirects);
continue;
}
@@ -930,29 +962,6 @@ iocshBody (const char *pathname, const char *commandLine, const char *macros)
}
}
stopRedirect(filename, lineno, redirects);
-
- if(!scope.interactive && scope.errored) {
- if(scope.onerr==Continue) {
- /* do nothing */
-
- } else if(scope.onerr==Break) {
- ret = -1;
- fprintf(epicsGetStderr(), "iocsh Error: Break\n" );
- break;
-
- } else if(scope.onerr==Halt) {
- ret = -1;
- if(scope.timeout<=0.0 || isinf(scope.timeout)) {
- fprintf(epicsGetStderr(), "iocsh Error: Halt\n" );
- epicsThreadSuspendSelf();
- break;
-
- } else {
- fprintf(epicsGetStderr(), "iocsh Error: Waiting %f sec ...\n", scope.timeout);
- epicsThreadSleep(scope.timeout);
- }
- }
- }
}
macPopScope(handle);
From 58473e825c76e859d9785a3fc4c7aee404adb95e Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Sun, 25 Aug 2019 16:29:55 -0700
Subject: [PATCH 083/281] iocsh more error handling
sooo many ways to fail...
---
modules/libcom/src/iocsh/iocsh.cpp | 51 +++++++++++++++++++-----------
1 file changed, 33 insertions(+), 18 deletions(-)
diff --git a/modules/libcom/src/iocsh/iocsh.cpp b/modules/libcom/src/iocsh/iocsh.cpp
index b3fe936cb..1ba1de635 100644
--- a/modules/libcom/src/iocsh/iocsh.cpp
+++ b/modules/libcom/src/iocsh/iocsh.cpp
@@ -1156,34 +1156,49 @@ static void onCallFunc(const iocshArgBuf *args)
{
iocshContext *context = (iocshContext *) epicsThreadPrivateGet(iocshContextId);
+#define USAGE() fprintf(epicsGetStderr(), "Usage: on error [continue | break | halt | wait ]\n")
+
if(!context || !context->scope) {
// we are not called through iocshBody()...
} else if(args->aval.ac<3 || strcmp(args->aval.av[1], "error")!=0) {
- fprintf(epicsGetStderr(), "Usage: on error [continue | break | halt | wait ]\n");
+ USAGE();
} else if(context->scope->interactive) {
fprintf(epicsGetStderr(), "Interactive shell ignores on error ...\n");
- } else if(strcmp(args->aval.av[2], "continue")==0) {
- context->scope->onerr = Continue;
-
- } else if(strcmp(args->aval.av[2], "break")==0) {
- context->scope->onerr = Break;
-
- } else if(strcmp(args->aval.av[2], "halt")==0) {
- context->scope->onerr = Halt;
- context->scope->timeout = 0.0;
-
- } else if(strcmp(args->aval.av[2], "wait")==0) {
- context->scope->onerr = Halt;
- if(args->aval.ac==3 || epicsParseDouble(args->aval.av[3], &context->scope->timeout, NULL)) {
- context->scope->timeout = 5.0;
- }
-
} else {
- fprintf(epicsGetStderr(), "Usage: on error [continue | break | halt | wait ]\n");
+ // don't fault on previous, ignored, errors
+ context->scope->errored = false;
+
+ if(strcmp(args->aval.av[2], "continue")==0) {
+ context->scope->onerr = Continue;
+
+ } else if(strcmp(args->aval.av[2], "break")==0) {
+ context->scope->onerr = Break;
+
+ } else if(strcmp(args->aval.av[2], "halt")==0) {
+ context->scope->onerr = Halt;
+ context->scope->timeout = 0.0;
+
+ } else if(strcmp(args->aval.av[2], "wait")==0) {
+ context->scope->onerr = Halt;
+ if(args->aval.ac<=3) {
+ USAGE();
+ } else if(epicsParseDouble(args->aval.av[3], &context->scope->timeout, NULL)) {
+ context->scope->timeout = 5.0;
+ } else {
+ USAGE();
+ fprintf(epicsGetStderr(), "Unable to parse 'on error wait' time %s\n", args->aval.av[3]);
+ }
+
+ } else {
+ fprintf(epicsGetStderr(), "Usage: on error [continue | break | halt | wait ]\n");
+ context->scope->errored = true;
+ }
}
+
+#undef USAGE
}
/*
From 2557d147853c944f4fd3b5e8baa21b78e67d88dc Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Sun, 25 Aug 2019 19:37:47 -0700
Subject: [PATCH 084/281] iocshCmd() imply "on error break"
---
modules/libcom/src/iocsh/iocsh.cpp | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/modules/libcom/src/iocsh/iocsh.cpp b/modules/libcom/src/iocsh/iocsh.cpp
index 1ba1de635..a795c9b70 100644
--- a/modules/libcom/src/iocsh/iocsh.cpp
+++ b/modules/libcom/src/iocsh/iocsh.cpp
@@ -600,6 +600,10 @@ iocshBody (const char *pathname, const char *commandLine, const char *macros)
fclose(fp);
return -1;
}
+
+ } else {
+ // use of iocshCmd() implies "on error break"
+ scope.onerr = Break;
}
/*
From 85517d761d028822ddae546aea20e235a4b3749c Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Sun, 25 Aug 2019 17:40:26 -0700
Subject: [PATCH 085/281] iocshTest start
---
modules/libcom/test/Makefile | 8 ++
modules/libcom/test/iocshTest.cpp | 131 ++++++++++++++++++
modules/libcom/test/iocshTestBadArg.cmd | 4 +
.../libcom/test/iocshTestBadArgIndirect.cmd | 3 +
modules/libcom/test/iocshTestSuccess.cmd | 8 ++
.../libcom/test/iocshTestSuccessIndirect.cmd | 3 +
modules/libcom/test/rtemsTestData.c | 6 -
7 files changed, 157 insertions(+), 6 deletions(-)
create mode 100644 modules/libcom/test/iocshTest.cpp
create mode 100644 modules/libcom/test/iocshTestBadArg.cmd
create mode 100644 modules/libcom/test/iocshTestBadArgIndirect.cmd
create mode 100644 modules/libcom/test/iocshTestSuccess.cmd
create mode 100644 modules/libcom/test/iocshTestSuccessIndirect.cmd
delete mode 100644 modules/libcom/test/rtemsTestData.c
diff --git a/modules/libcom/test/Makefile b/modules/libcom/test/Makefile
index f2e495a90..27577f012 100755
--- a/modules/libcom/test/Makefile
+++ b/modules/libcom/test/Makefile
@@ -241,6 +241,11 @@ TESTS += yajlTest
endif
endif
+TESTPROD_HOST += iocshTest
+iocshTest_SRCS += iocshTest.cpp
+TESTS += iocshTest
+TESTFILES += $(wildcard ../iocshTest*.cmd)
+
# The testHarness runs all the test programs in a known working order.
testHarness_SRCS += epicsRunLibComTests.c
@@ -300,3 +305,6 @@ endif
endif
include $(TOP)/configure/RULES
+
+rtemsTestData.c : $(TESTFILES) $(TOOLS)/epicsMakeMemFs.pl
+ $(PERL) $(TOOLS)/epicsMakeMemFs.pl $@ epicsRtemsFSImage $(TESTFILES)
diff --git a/modules/libcom/test/iocshTest.cpp b/modules/libcom/test/iocshTest.cpp
new file mode 100644
index 000000000..1216ed579
--- /dev/null
+++ b/modules/libcom/test/iocshTest.cpp
@@ -0,0 +1,131 @@
+/*************************************************************************\
+* Copyright (c) 2019 Michael Davidsaver
+* 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
+#include
+#include
+#include
+
+#include
+#include
+
+namespace {
+void findTestData()
+{
+ const char *locations[] = {
+ ".",
+ "..",
+ ".." OSI_PATH_LIST_SEPARATOR "O.Common",
+ "O.Common",
+ };
+
+ for(size_t i=0; i %d", expect ? "ran" : "expected error from", fname, err);
+}
+
+void testCmd(const char *cmd, bool expect=true)
+{
+ testDiag("eval \"%s\"", cmd);
+ int err = iocshCmd(cmd);
+ testOk((err==0) ^ (!expect), "%s \"%s\" -> %d", expect ? "eval'd" : "expected error from", cmd, err);
+}
+
+std::set reached;
+const iocshArg positionArg0 = {"position", iocshArgString};
+const iocshArg * const positionArgs[1] = { &positionArg0 };
+const iocshFuncDef positionFuncDef = {"position",1,positionArgs};
+void positionCallFunc(const iocshArgBuf *args)
+{
+ testDiag("Reaching \"%s\"", args[0].sval);
+ reached.insert(args[0].sval);
+}
+
+void testPosition(const std::string& pos, bool expect=true)
+{
+ testOk((reached.find(pos)!=reached.end()) ^ !expect,
+ "%sreached position %s", expect ? "" : "not ", pos.c_str());
+}
+
+const iocshArg assertArg0 = {"condition", iocshArgInt};
+const iocshArg * const assertArgs[1] = {&assertArg0};
+const iocshFuncDef assertFuncDef = {"assert",1,assertArgs};
+void assertCallFunc(const iocshArgBuf *args)
+{
+ iocshSetError(args[0].ival);
+}
+
+} // namespace
+
+MAIN(iocshTest)
+{
+ testPlan(19);
+ libComRegister();
+ iocshRegister(&positionFuncDef, &positionCallFunc);
+ iocshRegister(&assertFuncDef, &assertCallFunc);
+ findTestData();
+
+ testFile("iocshTestSuccess.cmd");
+ testPosition("success");
+ reached.clear();
+ testPosition("success", false);
+
+ testCmd("
Date: Mon, 26 Aug 2019 09:45:23 -0700
Subject: [PATCH 086/281] deprecate iocshFindCommand()
This function, and struct iocshFuncDef, expose
internal details. Specifically iocshCmdDef::func .
Which prevents changing/extending the iocsh function
signature.
Deprecate in favor of iocshCmd() and iocshRun().
---
modules/libcom/src/iocsh/iocsh.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/modules/libcom/src/iocsh/iocsh.h b/modules/libcom/src/iocsh/iocsh.h
index 2e8dc225e..6781e2404 100644
--- a/modules/libcom/src/iocsh/iocsh.h
+++ b/modules/libcom/src/iocsh/iocsh.h
@@ -76,7 +76,7 @@ epicsShareFunc void epicsShareAPI iocshRegister(
epicsShareFunc void epicsShareAPI iocshRegisterVariable (
const iocshVarDef *piocshVarDef);
epicsShareFunc const iocshCmdDef * epicsShareAPI iocshFindCommand(
- const char* name);
+ const char* name) EPICS_DEPRECATED;
epicsShareFunc const iocshVarDef * epicsShareAPI iocshFindVariable(
const char* name);
From c63a564ad455ae94235b1ce0b65c426acf411a58 Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Sun, 25 Aug 2019 20:30:14 -0700
Subject: [PATCH 087/281] doc
---
documentation/RELEASE_NOTES.html | 21 +++++++++++++++++++++
modules/libcom/src/iocsh/iocsh.h | 17 +++++++++++++++++
2 files changed, 38 insertions(+)
diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html
index df783606d..bdefbe0b8 100644
--- a/documentation/RELEASE_NOTES.html
+++ b/documentation/RELEASE_NOTES.html
@@ -30,6 +30,27 @@ release.
-->
+
Iocsh "on error ..."
+
+
A new statement is added to enable IOC shell commands
+to signal error conditions, and for scripts to respond.
+This first is through the new function
iocshSetError(int)
+A script may be prefixed with eg. "on error break"
+to stop at the failed command.
+
+
diff --git a/modules/libcom/src/iocsh/iocsh.h b/modules/libcom/src/iocsh/iocsh.h
index 6781e2404..a63af64ce 100644
--- a/modules/libcom/src/iocsh/iocsh.h
+++ b/modules/libcom/src/iocsh/iocsh.h
@@ -84,11 +84,28 @@ epicsShareFunc const iocshVarDef * epicsShareAPI iocshFindVariable(
/* This should only be called when iocsh is no longer needed*/
epicsShareFunc void epicsShareAPI iocshFree(void);
+/** shorthand for @code iocshLoad(pathname, NULL) @endcode */
epicsShareFunc int epicsShareAPI iocsh(const char *pathname);
+/** shorthand for @code iocshRun(cmd, NULL) @endcode */
epicsShareFunc int epicsShareAPI iocshCmd(const char *cmd);
+/** Read and evaluate IOC shell commands from the given file.
+ * @param pathname Path to script file
+ * @param macros NULL or a comma seperated list of macro definitions. eg. "VAR1=x,VAR2=y"
+ * @return 0 on success, non-zero on error
+ */
epicsShareFunc int epicsShareAPI iocshLoad(const char *pathname, const char* macros);
+/** Evaluate a single IOC shell command
+ * @param cmd Command string. eg. "echo \"something or other\""
+ * @param macros NULL or a comma seperated list of macro definitions. eg. "VAR1=x,VAR2=y"
+ * @return 0 on success, non-zero on error
+ */
epicsShareFunc int epicsShareAPI iocshRun(const char *cmd, const char* macros);
+/** @brief Signal error from an IOC shell function.
+ *
+ * @param err 0 - success (no op), !=0 - error
+ * @return The err argument value.
+ */
epicsShareFunc int iocshSetError(int err);
/* Makes macros that shadow environment variables work correctly with epicsEnvSet */
From b2938459f8003cd5ed1484603e228d73afbe0d9b Mon Sep 17 00:00:00 2001
From: Andrew Johnson
Date: Wed, 28 Aug 2019 15:28:08 -0500
Subject: [PATCH 088/281] Define pdevLibVME on non-VME RTEMS targets
---
documentation/RELEASE_NOTES.html | 14 +++++++++++++-
modules/libcom/src/osi/os/RTEMS/devLibVMEOSD.c | 7 ++++++-
2 files changed, 19 insertions(+), 2 deletions(-)
diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html
index 103e5a957..10910080d 100644
--- a/documentation/RELEASE_NOTES.html
+++ b/documentation/RELEASE_NOTES.html
@@ -20,7 +20,7 @@ release.
which should also be read to understand what has changed since an earlier
release.
-
EPICS Release 7.0.3
+
EPICS Release 7.0.3.1
+
Non-VME RTEMS targets now define pdevLibVME
+
+
Previously IOC executables that made calls to devLib routines would fail to
+link when built for some non-VME based RTEMS targets, which would have to be
+explicitly filtered out by sites that build Base for those targets.
+This fix makes
+that no longer necessary, all RTEMS targets should now link although the IOC
+won't be able to be used with the VME I/O on those systems (that we don't have
+VMEbus I/O support for in libCom).
+
+
EPICS Release 7.0.3
+
epicsTimeGetCurrent() optimization
Add a fast path to epicsTimeGetCurrent() and related calls
diff --git a/modules/libcom/src/osi/os/RTEMS/devLibVMEOSD.c b/modules/libcom/src/osi/os/RTEMS/devLibVMEOSD.c
index 0a96bad1a..b8f79e706 100644
--- a/modules/libcom/src/osi/os/RTEMS/devLibVMEOSD.c
+++ b/modules/libcom/src/osi/os/RTEMS/devLibVMEOSD.c
@@ -350,7 +350,12 @@ static void unsolicitedHandlerEPICS(int vectorNumber)
);
}
-#endif /* defined(__PPC__) && defined(mpc750) */
+#else /* !defined(__PPC__) && !defined(__mcf528x__) */
+
+/* No known VME interface here, provide a dummy */
+devLibVME *pdevLibVME;
+
+#endif /* defined(__PPC__) || defined(__mcf528x__) */
/*
* Some vxWorks convenience routines
From 62c3b0a585a4abcaae7794e487627bea29712be8 Mon Sep 17 00:00:00 2001
From: Dirk Zimoch
Date: Tue, 27 Aug 2019 16:51:00 +0200
Subject: [PATCH 089/281] don't send errlog on all logClients
---
modules/libcom/src/log/iocLog.c | 14 ++++++++++++++
modules/libcom/src/log/logClient.c | 4 ----
2 files changed, 14 insertions(+), 4 deletions(-)
diff --git a/modules/libcom/src/log/iocLog.c b/modules/libcom/src/log/iocLog.c
index e62da2050..8cb1349a1 100644
--- a/modules/libcom/src/log/iocLog.c
+++ b/modules/libcom/src/log/iocLog.c
@@ -18,8 +18,10 @@
#define epicsExportSharedSymbols
#include "envDefs.h"
+#include "errlog.h"
#include "logClient.h"
#include "iocLog.h"
+#include "epicsExit.h"
int iocLogDisable = 0;
@@ -74,6 +76,14 @@ void epicsShareAPI epicsShareAPI iocLogFlush (void)
}
}
+/*
+ * logClientDestroy()
+ */
+static void iocLogClientDestroy (logClientId id)
+{
+ errlogRemoveListeners (logClientSendMessage, id);
+}
+
/*
* iocLogClientInit()
*/
@@ -89,6 +99,10 @@ static logClientId iocLogClientInit (void)
return NULL;
}
id = logClientCreate (addr, port);
+ if (id != NULL) {
+ errlogAddListener (logClientSendMessage, id);
+ epicsAtExit (iocLogClientDestroy, id);
+ }
return id;
}
diff --git a/modules/libcom/src/log/logClient.c b/modules/libcom/src/log/logClient.c
index 99ee671d9..b076d50cb 100644
--- a/modules/libcom/src/log/logClient.c
+++ b/modules/libcom/src/log/logClient.c
@@ -154,8 +154,6 @@ static void logClientDestroy (logClientId id)
return;
}
- errlogRemoveListeners ( logClientSendMessage, (void *) pClient );
-
logClientClose ( pClient );
epicsMutexDestroy ( pClient->mutex );
@@ -549,8 +547,6 @@ logClientId epicsShareAPI logClientCreate (
pClient->name, LOG_SERVER_CREATE_CONNECT_SYNC_TIMEOUT );
}
- errlogAddListener ( logClientSendMessage, (void *) pClient );
-
return (void *) pClient;
}
From 243287877311748057baa61937ea8d8a530f8d27 Mon Sep 17 00:00:00 2001
From: Dirk Zimoch
Date: Tue, 27 Aug 2019 17:34:01 +0200
Subject: [PATCH 090/281] speed up logRestart thread termination at exit
---
modules/libcom/src/log/logClient.c | 21 ++++++++++++++++-----
1 file changed, 16 insertions(+), 5 deletions(-)
diff --git a/modules/libcom/src/log/logClient.c b/modules/libcom/src/log/logClient.c
index b076d50cb..96382a2eb 100644
--- a/modules/libcom/src/log/logClient.c
+++ b/modules/libcom/src/log/logClient.c
@@ -44,6 +44,7 @@ typedef struct {
SOCKET sock;
epicsThreadId restartThreadId;
epicsEventId stateChangeNotify;
+ epicsEventId shutdownNotify;
unsigned connectCount;
unsigned nextMsgIndex;
unsigned connected;
@@ -113,6 +114,7 @@ static void logClientDestroy (logClientId id)
epicsMutexMustLock ( pClient->mutex );
pClient->shutdown = 1u;
epicsMutexUnlock ( pClient->mutex );
+ epicsEventSignal ( pClient->shutdownNotify );
/* unblock log client thread blocking in send() or connect() */
interruptInfo =
@@ -157,8 +159,8 @@ static void logClientDestroy (logClientId id)
logClientClose ( pClient );
epicsMutexDestroy ( pClient->mutex );
-
epicsEventDestroy ( pClient->stateChangeNotify );
+ epicsEventDestroy ( pClient->shutdownNotify );
free ( pClient );
}
@@ -461,8 +463,8 @@ static void logClientRestart ( logClientId id )
else {
logClientConnect ( pClient );
}
-
- epicsThreadSleep ( LOG_RESTART_DELAY );
+
+ epicsEventWaitWithTimeout ( pClient->shutdownNotify, LOG_RESTART_DELAY);
epicsMutexMustLock ( pClient->mutex );
}
@@ -505,14 +507,22 @@ logClientId epicsShareAPI logClientCreate (
pClient->shutdownConfirm = 0;
epicsAtExit (logClientDestroy, (void*) pClient);
-
+
pClient->stateChangeNotify = epicsEventCreate (epicsEventEmpty);
if ( ! pClient->stateChangeNotify ) {
epicsMutexDestroy ( pClient->mutex );
free ( pClient );
return NULL;
}
-
+
+ pClient->shutdownNotify = epicsEventCreate (epicsEventEmpty);
+ if ( ! pClient->shutdownNotify ) {
+ epicsMutexDestroy ( pClient->mutex );
+ epicsEventDestroy ( pClient->stateChangeNotify );
+ free ( pClient );
+ return NULL;
+ }
+
pClient->restartThreadId = epicsThreadCreate (
"logRestart", epicsThreadPriorityLow,
epicsThreadGetStackSize(epicsThreadStackSmall),
@@ -520,6 +530,7 @@ logClientId epicsShareAPI logClientCreate (
if ( pClient->restartThreadId == NULL ) {
epicsMutexDestroy ( pClient->mutex );
epicsEventDestroy ( pClient->stateChangeNotify );
+ epicsEventDestroy ( pClient->shutdownNotify );
free (pClient);
fprintf(stderr, "log client: unable to start log client connection watch dog thread\n");
return NULL;
From 0a3427c835e367792dd3419bc2a5bc1aea02386a Mon Sep 17 00:00:00 2001
From: Dirk Zimoch
Date: Wed, 28 Aug 2019 09:29:57 +0200
Subject: [PATCH 091/281] do not discard unsent messages when log server has
closed connection, instead try to send them after reconnect
---
modules/libcom/src/log/logClient.c | 2 --
1 file changed, 2 deletions(-)
diff --git a/modules/libcom/src/log/logClient.c b/modules/libcom/src/log/logClient.c
index 96382a2eb..75984404c 100644
--- a/modules/libcom/src/log/logClient.c
+++ b/modules/libcom/src/log/logClient.c
@@ -85,8 +85,6 @@ static void logClientClose ( logClient *pClient )
pClient->sock = INVALID_SOCKET;
}
- pClient->nextMsgIndex = 0u;
- memset ( pClient->msgBuf, '\0', sizeof ( pClient->msgBuf ) );
pClient->connected = 0u;
/*
From 709208ef5cd4db2764875175aacd94fd500e984a Mon Sep 17 00:00:00 2001
From: Dirk Zimoch
Date: Wed, 28 Aug 2019 11:41:12 +0200
Subject: [PATCH 092/281] elimitate duplicate code in logClient
---
modules/libcom/src/log/logClient.c | 62 +++++++-----------------------
1 file changed, 14 insertions(+), 48 deletions(-)
diff --git a/modules/libcom/src/log/logClient.c b/modules/libcom/src/log/logClient.c
index 75984404c..90fde98b5 100644
--- a/modules/libcom/src/log/logClient.c
+++ b/modules/libcom/src/log/logClient.c
@@ -174,57 +174,23 @@ static void sendMessageChunk(logClient * pClient, const char * message) {
unsigned msgBufBytesLeft =
sizeof ( pClient->msgBuf ) - pClient->nextMsgIndex;
- if ( strSize > msgBufBytesLeft ) {
- int status;
-
- if ( ! pClient->connected ) {
- break;
- }
-
- if ( msgBufBytesLeft > 0u ) {
- memcpy ( & pClient->msgBuf[pClient->nextMsgIndex],
- message, msgBufBytesLeft );
- pClient->nextMsgIndex += msgBufBytesLeft;
- strSize -= msgBufBytesLeft;
- message += msgBufBytesLeft;
- }
-
- status = send ( pClient->sock, pClient->msgBuf,
- pClient->nextMsgIndex, 0 );
- if ( status > 0 ) {
- unsigned nSent = (unsigned) status;
- if ( nSent < pClient->nextMsgIndex ) {
- unsigned newNextMsgIndex = pClient->nextMsgIndex - nSent;
- memmove ( pClient->msgBuf, & pClient->msgBuf[nSent],
- newNextMsgIndex );
- pClient->nextMsgIndex = newNextMsgIndex;
- }
- else {
- pClient->nextMsgIndex = 0u;
- }
- }
- else {
- if ( ! pClient->shutdown ) {
- char sockErrBuf[64];
- if ( status ) {
- epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) );
- }
- else {
- strcpy ( sockErrBuf, "server initiated disconnect" );
- }
- fprintf ( stderr, "log client: lost contact with log server at \"%s\" because \"%s\"\n",
- pClient->name, sockErrBuf );
- }
- logClientClose ( pClient );
- break;
- }
+ if ( msgBufBytesLeft < strSize && pClient->nextMsgIndex != 0u && pClient->connected)
+ {
+ /* buffer is full, thus flush it */
+ logClientFlush ( pClient );
+ msgBufBytesLeft = sizeof ( pClient->msgBuf ) - pClient->nextMsgIndex;
}
- else {
- memcpy ( & pClient->msgBuf[pClient->nextMsgIndex],
- message, strSize );
- pClient->nextMsgIndex += strSize;
+ if ( msgBufBytesLeft == 0u ) {
+ fprintf ( stderr, "log client: messages to \"%s\" are lost\n",
+ pClient->name );
break;
}
+ if ( msgBufBytesLeft > strSize) msgBufBytesLeft = strSize;
+ memcpy ( & pClient->msgBuf[pClient->nextMsgIndex],
+ message, msgBufBytesLeft );
+ pClient->nextMsgIndex += msgBufBytesLeft;
+ strSize -= msgBufBytesLeft;
+ message += msgBufBytesLeft;
}
}
From 59aa9cfe746b3fc08d7b94b82f47973f6c5ed92c Mon Sep 17 00:00:00 2001
From: Dirk Zimoch
Date: Wed, 28 Aug 2019 15:15:19 +0200
Subject: [PATCH 093/281] avoid needless memmove calls
---
modules/libcom/src/log/logClient.c | 24 +++++++++++-------------
1 file changed, 11 insertions(+), 13 deletions(-)
diff --git a/modules/libcom/src/log/logClient.c b/modules/libcom/src/log/logClient.c
index 90fde98b5..203f29dd3 100644
--- a/modules/libcom/src/log/logClient.c
+++ b/modules/libcom/src/log/logClient.c
@@ -219,6 +219,8 @@ void epicsShareAPI logClientSend ( logClientId id, const char * message )
void epicsShareAPI logClientFlush ( logClientId id )
{
+ unsigned nSent = 0u;
+
logClient * pClient = ( logClient * ) id;
if ( ! pClient ) {
@@ -227,20 +229,11 @@ void epicsShareAPI logClientFlush ( logClientId id )
epicsMutexMustLock ( pClient->mutex );
- while ( pClient->nextMsgIndex && pClient->connected ) {
- int status = send ( pClient->sock, pClient->msgBuf,
- pClient->nextMsgIndex, 0 );
+ while ( nSent < pClient->nextMsgIndex && pClient->connected ) {
+ int status = send ( pClient->sock, pClient->msgBuf + nSent,
+ pClient->nextMsgIndex - nSent, 0 );
if ( status > 0 ) {
- unsigned nSent = (unsigned) status;
- if ( nSent < pClient->nextMsgIndex ) {
- unsigned newNextMsgIndex = pClient->nextMsgIndex - nSent;
- memmove ( pClient->msgBuf, & pClient->msgBuf[nSent],
- newNextMsgIndex );
- pClient->nextMsgIndex = newNextMsgIndex;
- }
- else {
- pClient->nextMsgIndex = 0u;
- }
+ nSent += (unsigned) status;
}
else {
if ( ! pClient->shutdown ) {
@@ -258,6 +251,11 @@ void epicsShareAPI logClientFlush ( logClientId id )
break;
}
}
+ pClient->nextMsgIndex -= nSent;
+ if ( nSent > 0 && pClient->nextMsgIndex > 0 ) {
+ memmove ( pClient->msgBuf, & pClient->msgBuf[nSent],
+ pClient->nextMsgIndex );
+ }
epicsMutexUnlock ( pClient->mutex );
}
From 9df98c18386f1204fdf21a58f0f6dc1aae04126e Mon Sep 17 00:00:00 2001
From: Dirk Zimoch
Date: Wed, 28 Aug 2019 15:23:00 +0200
Subject: [PATCH 094/281] send pending log messages directly after connecting
---
modules/libcom/src/log/logClient.c | 10 +++-------
1 file changed, 3 insertions(+), 7 deletions(-)
diff --git a/modules/libcom/src/log/logClient.c b/modules/libcom/src/log/logClient.c
index 203f29dd3..743910fb8 100644
--- a/modules/libcom/src/log/logClient.c
+++ b/modules/libcom/src/log/logClient.c
@@ -223,7 +223,7 @@ void epicsShareAPI logClientFlush ( logClientId id )
logClient * pClient = ( logClient * ) id;
- if ( ! pClient ) {
+ if ( ! pClient || ! pClient->connected ) {
return;
}
@@ -419,12 +419,8 @@ static void logClientRestart ( logClientId id )
epicsMutexUnlock ( pClient->mutex );
- if ( isConn ) {
- logClientFlush ( pClient );
- }
- else {
- logClientConnect ( pClient );
- }
+ if ( ! isConn ) logClientConnect ( pClient );
+ logClientFlush ( pClient );
epicsEventWaitWithTimeout ( pClient->shutdownNotify, LOG_RESTART_DELAY);
From 2b0161d9bfd8fc604942996d1812d245bf4a00f7 Mon Sep 17 00:00:00 2001
From: Dirk Zimoch
Date: Wed, 28 Aug 2019 15:29:23 +0200
Subject: [PATCH 095/281] no need to delay startup only because log server is
currently not available
---
modules/libcom/src/log/logClient.c | 25 -------------------------
1 file changed, 25 deletions(-)
diff --git a/modules/libcom/src/log/logClient.c b/modules/libcom/src/log/logClient.c
index 743910fb8..72a2e1364 100644
--- a/modules/libcom/src/log/logClient.c
+++ b/modules/libcom/src/log/logClient.c
@@ -54,7 +54,6 @@ typedef struct {
} logClient;
static const double LOG_RESTART_DELAY = 5.0; /* sec */
-static const double LOG_SERVER_CREATE_CONNECT_SYNC_TIMEOUT = 5.0; /* sec */
static const double LOG_SERVER_SHUTDOWN_TIMEOUT = 30.0; /* sec */
/*
@@ -438,9 +437,7 @@ static void logClientRestart ( logClientId id )
logClientId epicsShareAPI logClientCreate (
struct in_addr server_addr, unsigned short server_port)
{
- epicsTimeStamp begin, current;
logClient *pClient;
- double diff;
pClient = calloc (1, sizeof (*pClient));
if (pClient==NULL) {
@@ -494,28 +491,6 @@ logClientId epicsShareAPI logClientCreate (
return NULL;
}
- /*
- * attempt to synchronize with circuit connect
- */
- epicsTimeGetCurrent ( & begin );
- epicsMutexMustLock ( pClient->mutex );
- do {
- epicsMutexUnlock ( pClient->mutex );
- epicsEventWaitWithTimeout (
- pClient->stateChangeNotify,
- LOG_SERVER_CREATE_CONNECT_SYNC_TIMEOUT / 10.0 );
- epicsTimeGetCurrent ( & current );
- diff = epicsTimeDiffInSeconds ( & current, & begin );
- epicsMutexMustLock ( pClient->mutex );
- }
- while ( ! pClient->connected && diff < LOG_SERVER_CREATE_CONNECT_SYNC_TIMEOUT );
- epicsMutexUnlock ( pClient->mutex );
-
- if ( ! pClient->connected ) {
- fprintf (stderr, "log client create: timed out synchronizing with circuit connect to \"%s\" after %.1f seconds\n",
- pClient->name, LOG_SERVER_CREATE_CONNECT_SYNC_TIMEOUT );
- }
-
return (void *) pClient;
}
From b9bc836d1e446ad110b8c296f45833ffd092fd03 Mon Sep 17 00:00:00 2001
From: Ralph Lange
Date: Mon, 2 Sep 2019 14:47:39 +0200
Subject: [PATCH 096/281] appveyor-ci: disable cygwin x86 builds (install is
broken)
---
appveyor.yml | 3 +++
1 file changed, 3 insertions(+)
diff --git a/appveyor.yml b/appveyor.yml
index b6fb2f2ae..21ec93c86 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -64,6 +64,9 @@ matrix:
# Cygwin static-debug has compiler problems
- configuration: static-debug
TOOLCHAIN: cygwin
+ # Cygwin x86 install is currently (08/2019) broken (libncursesw-devel fails)
+ - platform: x86
+ TOOLCHAIN: cygwin
#---------------------------------#
From 8a39ca74891e327f051f091030aa4bcbf7d126ff Mon Sep 17 00:00:00 2001
From: Joao Paulo Martins
Date: Tue, 3 Sep 2019 14:20:24 +0200
Subject: [PATCH 097/281] Preparing the waveform record DBD-POD file; Creation
of the menuFtype DBD-POD file
---
.../db/{menuFtype.dbd => menuFtype.dbd.pod} | 9 +
src/std/rec/waveformRecord.dbd.pod | 517 +++++++++++++++++-
2 files changed, 499 insertions(+), 27 deletions(-)
rename src/ioc/db/{menuFtype.dbd => menuFtype.dbd.pod} (88%)
diff --git a/src/ioc/db/menuFtype.dbd b/src/ioc/db/menuFtype.dbd.pod
similarity index 88%
rename from src/ioc/db/menuFtype.dbd
rename to src/ioc/db/menuFtype.dbd.pod
index ff20e0ca4..fd643f625 100644
--- a/src/ioc/db/menuFtype.dbd
+++ b/src/ioc/db/menuFtype.dbd.pod
@@ -7,6 +7,15 @@
# and higher are distributed subject to a Software License Agreement found
# in file LICENSE that is included with this distribution.
#*************************************************************************
+
+=head1 Menu menuFtype
+
+This menu is used for the C field of all record types.
+
+=menu menuFtype
+
+=cut
+
menu(menuFtype) {
choice(menuFtypeSTRING,"STRING")
choice(menuFtypeCHAR,"CHAR")
diff --git a/src/std/rec/waveformRecord.dbd.pod b/src/std/rec/waveformRecord.dbd.pod
index db2fa05fb..e40aa2a21 100644
--- a/src/std/rec/waveformRecord.dbd.pod
+++ b/src/std/rec/waveformRecord.dbd.pod
@@ -1,35 +1,13 @@
-#*************************************************************************
-# 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.
-# EPICS BASE is distributed subject to a Software License Agreement found
-# in file LICENSE that is included with this distribution.
-#*************************************************************************
+=pod
=title Waveform Record (waveform)
-...
-
-=head2 Record-specific Menus
-
-=head3 Menu waveformPOST
-
-The MPST and APST fields use this menu to determine when to post new value
-and archive monitors respectively.
-
-=menu waveformPOST
-
-...
-
-=head2 Parameter Fields
-
-The record-specific fields are described below.
+The waveform record type is used to interface waveform digitizers. The record
+stores its data in arrays. The array can contain any of the supported data
+types.
=recordtype waveform
-...
-
=cut
menu(waveformPOST) {
@@ -39,7 +17,492 @@ menu(waveformPOST) {
recordtype(waveform) {
-=fields VAL, FTVL, MPST, APST
+=pod
+
+=head1 Contents
+
+=over
+
+=item * L
+
+=over
+
+=item * L
+
+=item * L
+
+=item * L
+
+=item * L
+
+=back
+
+=item * L
+
+=over
+
+=item * L
+
+=item * L
+
+=back
+
+=item * L
+
+=over
+
+=item * L
+
+=item * L
+
+=item * L
+
+=back
+
+=back
+
+=begin html
+
+
+
+=end html
+
+=head2 Parameter Fields
+
+The waveform's fields fall into the following categories:
+
+=over
+
+=item * L;
+
+=item * L;
+
+=item * L;
+
+=item * L;
+
+=back
+
+=head3 Scan Parameters
+
+The waveform record has the standard fields for specifying under what
+circumstances the record will be processed. These fields are listed in L. In addition, L explains how these fields are
+used. Note that I/O event scanning is only supported for those card types that
+interrupt.
+
+=head3 Read Parameters
+
+These fields are configurable by the user to specify how and from where the
+record reads its data. How the INP field is configured determines where the
+waveform gets its input. It can be a hardware address, a channel access or
+database link, or a constant. Only in records that use soft device support can
+the INP field be a channel access link, a database link, or a constant.
+Otherwise, the INP field must be a hardware address. See L for information on the format of hardware addresses and database
+links.
+
+=head4 Fields related to waveform reading
+
+=fields DTYP, INP, NELM, FTVL, RARM
+
+The DTYP field must contain the name of the appropriate device support module.
+The values retrieved from the input link are placed in an array referenced by
+VAL. (If the INP link is a constant, elements can be placed in the array via
+dbPuts.) NELM specifies the number of elements that the array will hold, while
+FTVL specifies the data type of the elements.
+The RARM field causes the device to re-arm when this field is set to 1.
+
+=head4 Possible data types for FTVL
+
+=begin html
+
+
+
Index
Identifier
Choice String
+
+
0
+
menuFtypeSTRING
+
STRING
+
+
+
1
+
menuFtypeCHAR
+
CHAR
+
+
+
2
+
menuFtypeUCHAR
+
UCHAR
+
+
+
3
+
menuFtypeSHORT
+
SHORT
+
+
+
4
+
menuFtypeUSHORT
+
USHORT
+
+
+
5
+
menuFtypeLONG
+
LONG
+
+
+
6
+
menuFtypeULONG
+
ULONG
+
+
+
7
+
menuFtypeFLOAT
+
FLOAT
+
+
+
8
+
menuFtypeDOUBLE
+
DOUBLE
+
+
+
9
+
menuFtypeENUM
+
ENUM
+
+
+
+
+=end html
+
+=head3 Operator Display Parameters
+
+These parameters are used to present meaningful data to the operator. They
+display the value and other parameters of the waveform either textually or
+graphically.
+
+=head4 Fields related to I
+
+=fields EGU, HOPR, LOPR, PREC, NAME, DESC
+
+EGU is a string of up to 16 characters describing the units that the waveform
+measures. It is retrieved by the C<<< get_units >>> record support routine.
+
+The HOPR and LOPR fields set the upper and lower display limits for array
+elements referenced by the VAL field. Both the C<<< get_graphic_double >>> and
+C<<< get_control_double >>> record support routines retrieve these fields.
+
+The PREC field determines the floating point precision with which to display the
+array values. It is used whenever the C<<< get_precision >>> record support
+routine is called.
+
+See L for more on the record name (NAME) and
+description (DESC) fields.
+
+
+=head3 Alarm Parameters
+
+The waveform record has the alarm parameters common to all record types. L lists other fields related to a alarms that are common to all record
+types.
+
+=head3 Monitor Parameters
+
+These parameters are used to determine when to send monitors placed on the VAL
+field. The APST and MPST fields are a menu with choices "Always" and "On
+Change". The default is "Always", thus monitors will normally be sent every time
+the record processes. Selecting "On Change" causes a 32-bit hash of the VAL
+field buffer to be calculated and compared with the previous hash value every
+time the record processes; the monitor will only be sent if the hash is
+different, indicating that the buffer has changed. Note that there is a small
+chance that two different value buffers might result in the same hash value, so
+for critical systems "Always" may be a better choice, even though it re-sends
+duplicate data.
+
+=head4 Record fields related to I
+
+=fields APST, MPST, HASH
+
+=head4 Menu choices for C and C fields
+
+=menu waveformPOST
+
+=head3 Run-time Parameters
+
+These parameters are used by the run-time code for processing the waveform. They
+are not configured using a configuration tool. Only the VAL field is modifiable
+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.
+
+=fields VAL, BPTR, NORD, BUSY
+
+The following fields are used to operate the waveform in the simulation mode.
+See L for more information on the simulation mode fields.
+
+=fields SIOL, SIML, SIMM, SIMS
+
+=begin html
+
+
+
+
+
+=end html
+
+=head2 Record Support
+
+=head3 Record Support Routines
+
+=head4 init_record
+
+ static long init_record(waveformRecord *prec, int pass)
+
+Using NELM and FTVL space for the array is allocated. The array address is
+stored in the record.
+
+This routine initializes SIMM with the value of SIML if SIML type is CONSTANT
+link or creates a channel access link if SIML type is PV_LINK. VAL is likewise
+initialized if SIOL is CONSTANT or PV_LINK.
+
+This routine next checks to see that device support is available and a device
+support read routine is defined. If either does not exist, an error message is
+issued and processing is terminated
+
+If device support includes C, it is called.
+
+=head4 process
+
+ static long process(waveformRecord *prec)
+
+See L"Record Processing"> section below.
+
+=head4 cvt_dbaddr
+
+ static long cvt_dbaddr(DBADDR *paddr)
+
+This is called by dbNameToAddr. It makes the dbAddr structure refer to the
+actual buffer holding the result.
+
+=head4 get_array_info
+
+ static long get_array_info(DBADDR *paddr, long *no_elements, long *offset)
+
+Obtains values from the array referenced by VAL.
+
+=head4 put_array_info
+
+ static long put_array_info(DBADDR *paddr, long nNew)
+
+Writes values into the array referenced by VAL.
+
+=head4 get_units
+
+ static long get_units(DBADDR *paddr, char *units)
+
+Retrieves EGU.
+
+=head4 get_prec
+
+ static long get_precision(DBADDR *paddr, long *precision)
+
+Retrieves PREC if field is VAL field. Otherwise, calls C<<< recGblGetPrec() >>>.
+
+=head4 get_graphic_double
+
+ static long get_graphic_double(DBADDR *paddr, struct dbr_grDouble *pgd)
+
+Sets the upper display and lower display limits for a field. If the field is VAL
+the limits are set to HOPR and LOPR, else if the field has upper and lower
+limits defined they will be used, else the upper and lower maximum values for
+the field type will be used.
+
+Sets the following values:
+
+ upper_disp_limit = HOPR
+ lower_disp_limit = LOPR
+
+=head4 get_control_double
+
+ static long get_control_double(DBADDR *paddr, struct dbr_ctrlDouble *pcd)
+
+Sets the upper control and the lower control limits for a field. If the field is
+VAL the limits are set to HOPR and LOPR, else if the field has upper and lower
+limits defined they will be used, else the upper and lower maximum values for
+the field type will be used.
+
+Sets the following values
+
+ upper_ctrl_limit = HOPR
+ lower_ctrl_limit = LOPR
+
+=head3 Record Processing
+
+Routine process implements the following algorithm:
+
+=over
+
+=item 1.
+
+Check to see that the appropriate device support module exists. If it doesn't,
+an error message is issued and processing is terminated with the PACT field
+still set to TRUE. This ensures that processes will no longer be called for this
+record. Thus error storms will not occur.
+
+=item 2.
+
+Call device support read routine.
+
+=item 3.
+
+If PACT has been changed to TRUE, the device support read routine has started
+but has not completed writing the new value. In this case, the processing
+routine merely returns, leaving PACT TRUE.
+
+=item 4.
+
+Check to see if monitors should be invoked.
+
+=over
+
+=item *
+
+Alarm monitors are invoked if the alarm status or severity has changed.
+
+=item *
+
+Archive and value change monitors are invoked if APST or MPST are Always or if
+the result of the hash calculation is different.
+
+=item *
+
+NSEV and NSTA are reset to 0.
+
+=back
+
+=item 5.
+
+Scan forward link if necessary, set PACT FALSE, and return.
+
+=back
+
+=begin html
+
+
+
+
+
+=end html
+
+=head2 Device Support
+
+=head3 Fields Of Interest To Device Support
+
+Each waveform record must have an associated set of device support routines. The
+primary responsibility of the device support routines is to obtain a new array
+value whenever read_wf is called. The device support routines are primarily
+interested in the following fields:
+
+=fields PACT, DPVT, NSEV, NSTA, INP, NELM, FTVL, RARM, BPTR, NORD, BUSY
+
+=head3 Device Support Routines
+
+Device support consists of the following routines:
+
+=head4 long report(int level)
+
+This optional routine is called by the IOC command C and is passed the
+report level that was requested by the user.
+It should print a report on the state of the device support to stdout.
+The C parameter may be used to output increasingly more detailed
+information at higher levels, or to select different types of information with
+different levels.
+Level zero should print no more than a small summary.
+
+=head4 long init(int after)
+
+This optional routine is called twice at IOC initialization time.
+The first call happens before any of the C calls are made, with
+the integer parameter C set to 0.
+The second call happens after all of the C calls have been made,
+with C set to 1.
+
+=head4 init_record
+
+ init_record(precord)
+
+This routine is optional. If provided, it is called by the record support
+C routine.
+
+=head4 get_ioint_info
+
+ get_ioint_info(int cmd,struct dbCommon *precord,IOSCANPVT *ppvt)
+
+This routine is called by the ioEventScan system each time the record is added
+or deleted from an I/O event scan list. cmd has the value (0,1) if the
+record is being (added to, deleted from) an I/O event list. It must be
+provided for any device type that can use the ioEvent scanner.
+
+=head4 read_wf
+
+ read_wf(precord)
+
+This routine must provide a new input value. It returns the following values:
+
+=over
+
+=item *
+
+0: Success.
+
+=item *
+
+Other: Error.
+
+=back
+
+=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.
+
+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 PV_LINK, then dbCaAddInlink is called by C.
+
+read_wf calls recGblGetLinkValue which performs the following steps:
+
+=over
+
+=item *
+
+If the INP link type is CONSTANT recGblGetLinkValue does nothing.
+
+=item *
+
+If the INP link type is DB_LINK, then dbGetLink is called to obtain a new input
+value. If dbGetLink returns an error, a LINK_ALARM with a severity of
+INVALID_ALARM is raised.
+
+=item *
+
+If the INP link type is CA_LINK, then dbCaGetLink is called to obtain a new
+input value. If dbCaGetLink returns an error, a LINK_ALARM with a severity of
+INVALID_ALARM is raised.
+
+=item *
+
+NORD is set to the number of values returned and read_wf returns.
+
+=back
=cut
From 67583b4bda38190952a9cb3e76ace2a4eb0b40af Mon Sep 17 00:00:00 2001
From: Saeed Haghtalab
Date: Wed, 4 Sep 2019 10:58:20 +0200
Subject: [PATCH 098/281] Update compressRecord.dbd.pod based on Wiki + Content
update
---
src/std/rec/Makefile | 6 +
src/std/rec/compressRecord.dbd.pod | 386 +++++++++++++++++++++++++++--
src/std/rec/image/compress-1.jpg | Bin 0 -> 16781 bytes
src/std/rec/image/compress-2.gif | Bin 0 -> 4136 bytes
4 files changed, 371 insertions(+), 21 deletions(-)
create mode 100644 src/std/rec/image/compress-1.jpg
create mode 100644 src/std/rec/image/compress-2.gif
diff --git a/src/std/rec/Makefile b/src/std/rec/Makefile
index 7515d769c..307662ebb 100644
--- a/src/std/rec/Makefile
+++ b/src/std/rec/Makefile
@@ -53,3 +53,9 @@ stdRecords_DBD = $(patsubst %,%.dbd,$(stdRecords))
dbRecStd_SRCS += $(patsubst %,%.c,$(stdRecords))
HTMLS += $(patsubst %.dbd.pod,%.html,$(notdir $(wildcard ../rec/*Record.dbd.pod)))
+
+vpath %.jpg $(USR_VPATH) .. $(SRC_DIRS) ../rec/image
+vpath %.gif $(USR_VPATH) .. $(SRC_DIRS) ../rec/image
+
+HTMLS += compress-1.jpg
+HTMLS += compress-2.gif
diff --git a/src/std/rec/compressRecord.dbd.pod b/src/std/rec/compressRecord.dbd.pod
index a67e12d3c..90cacb77d 100644
--- a/src/std/rec/compressRecord.dbd.pod
+++ b/src/std/rec/compressRecord.dbd.pod
@@ -7,43 +7,387 @@
# in file LICENSE that is included with this distribution.
#*************************************************************************
-=title Compress Record (compress)
+=title Compression Record (compress)
-...
+The data compression record is used to collect and compress data from arrays.
+When the INP field references a data array field, it immediately compresses the
+entire array into an element of an array using one of several algorithms,
+overwriting the previous element. If the INP field obtains its value from a
+scalar-value field, the compression record will collect a new sample each time
+the record is processed and add it to the compressed data array as a circular
+buffer.
-=head2 Record-specific Menus
-
-=head3 Menu compressALG
-
-The ALG field which uses this menu controls the compression algorithm used.
-
-...
-
-=menu compressALG
-
-...
-
-=head2 Parameter Fields
-
-The record-specific fields are described below.
+The INP link can also specify a constant; however, if this is the case, the
+compression algorithms are ignored, and the record support routines merely
+return after checking the FLNK field.
=recordtype compress
-...
-
=cut
menu(compressALG) {
choice(compressALG_N_to_1_Low_Value,"N to 1 Low Value")
choice(compressALG_N_to_1_High_Value,"N to 1 High Value")
choice(compressALG_N_to_1_Average,"N to 1 Average")
+ choice(compressALG_N_to_1_Median,"N to 1 Median")
choice(compressALG_Average,"Average")
choice(compressALG_Circular_Buffer,"Circular Buffer")
- choice(compressALG_N_to_1_Median,"N to 1 Median")
}
recordtype(compress) {
-=fields VAL
+=head2 Contents
+
+=over
+
+=item * L
+
+=over
+
+=item * L
+
+=item * L
+
+=item * L
+
+=item * L
+
+=back
+
+=item * L
+
+=over
+
+=item * L
+
+=item * L
+
+=back
+
+=back
+
+=begin html
+
+
+
+
+
+=end html
+
+=head2 Parameter Fields
+
+=head3 Scanning Parameters
+
+The compression record has the standard fields for specifying under what
+circumstances the record will be processed. These fields are listed in
+L. In addition,
+L
+explains how these fields are used. Since the compression record supports no
+direct interfaces to hardware, its SCAN field cannot specify C<<< I/O Intr >>>.
+
+=head3 Algorithms and Related Parameters
+
+The user specifies the algorithm to be used in the ALG field. There are six possible
+algorithms which can be specified as follows:
+
+=head4 Menu compressALG
+
+=menu compressALG
+
+The following fields determine what channel to read and how to compress the data:
+
+=fields ALG, INP, NSAM, N, ILIL, IHIL, OFF, RES
+
+As stated above, the ALG field specifies which algorithm to be performed on the data.
+
+The INP should be a database or channel access link. Though INP can be a constant,
+the data compression algorithms are supported only when INP is a database link. See
+L
+for information on specifying links.
+
+
+IHIL and ILIL can be set to provide an initial value filter on the input array.
+If ILIL E IHIL, the input elements will be skipped until a value is found
+that is in the range of ILIL to IHIL. Note that ILIL and IHIL are used only in
+C<<< N to 1 >>> algorithms.
+
+OFF provides the offset to the current beginning of the array data.
+Note that OFF is used only in C<<< N to 1 >>> algorithms.
+
+The RES field can be accessed at run time to cause the algorithm to reset
+itself before the maximum number of samples are reached.
+
+=head4 Algorithms
+
+B algorithm keeps a circular buffer of length NSAM.
+Each time the record is processed, it gets the data referenced by INP and puts
+it into the circular buffer referenced by VAL. The INP can refer to both scalar or
+array data and VAL is just a time ordered circular buffer of values obtained
+from INP.
+Note that N, ILIL, IHIL and OFF are not used in C<<< Circular Buffer >>> algorithm.
+
+B takes an average of every element of the array obtained from
+INP over time; that is, the entire array referenced by INP is retrieved, and for
+each element, the new average is calculated and placed in the corresponding
+element of the value buffer. The retrieved array is truncated to be of length
+NSAM. N successive arrays are averaged and placed in the buffer. Thus, VAL[0]
+holds the average of the first element of INP over N samples, VAL[1] holds the
+average of the next element of INP over N samples, and so on. The following
+shows the equation:
+
+=begin html
+
+
+
+=end html
+
+B If any of the C<<< N to 1 >>> algorithms are chosen, then VAL is a circular
+buffer of NSAM samples.
+The actual algorithm depends on whether INP references a scalar or an array.
+
+If INP refers to a scalar, then N successive time ordered samples of INP are taken.
+After the Nth sample is obtained, a new value determined by the algorithm
+(Lowest, Highest, or Average), is written to the circular buffer referenced by
+VAL. If C<<< Low Value >>> the lowest value of all the samples is written; if
+C<<< High Value >>> the highest value is written; and if C<<< Average >>>, the
+average of all the samples are written. The C<<< Median >>> setting behaves
+like C<<< Average >>> with scalar input data.
+
+If INP refers to an array, then the following applies:
+
+=over
+
+=item C<<< N to 1 Low Value >>>
+
+Compress N to 1 samples, keeping the lowest value.
+
+=item C<<< N to 1 High Value >>>
+
+Compress N to 1 samples, keeping the highest value.
+
+=item C<<< N to 1 Average >>>
+
+Compress N to 1 samples, taking the average value.
+
+=item C<<< N to 1 Median >>>
+
+Compress N to 1 samples, taking the median value.
+
+=back
+
+The compression record keeps NSAM data samples.
+
+The field N determines the number of elements to compress into each result.
+
+Thus, if NSAM was 3, and N was also equal to 3, then the algorithms would work
+as in the following diagram:
+
+=begin html
+
+
+
+=end html
+
+
+=head3 Operator Display Parameters
+
+These parameters are used to present meaningful data to the operator. They
+display the value and other parameters of the record either textually or
+graphically.
+
+=fields EGU, HOPR, LOPR, PREC, NAME, DESC
+
+The EGU field should be given a string that describes the value of VAL, but is
+used whenever the C<<< get_units >>> record support routine is called.
+
+The HOPR and LOPR fields only specify the upper and lower display limits for
+VAL, HIHI, HIGH, LOLO and LOW fields.
+
+PREC controls the floating-point precision whenever C<<< get_precision >>> is
+called, and the field being referenced is the VAL field (i.e., one of the values
+contained in the circular buffer).
+
+See L
+for more on the record name (NAME) and description (DESC) fields.
+
+
+=head3 Alarm Parameters
+
+The compression record has the alarm parameters common to all record types described in
+L.
+
+=head3 Run-time Parameters
+
+These parameters are used by the run-time code for processing the data
+compression algorithm. They are not configurable by the user, though some are
+accessible at run-time. They can represent the current state of the waveform or
+of the record whose field is referenced by the INP field.
+
+=fields NUSE, OUSE, BPTR, SPTR, WPTR, CVB, INPN, INX
+
+NUSE and OUSE hold the current and previous number of elements stored in VAL.
+
+BPTR is a pointer that refers to the buffer referenced by VAL.
+
+SPTR points to an array that is used for array averages.
+
+WPTR is used by the dbGetlinks routines.
+
+=begin html
+
+
+
+
+
+=end html
+
+=head2 Record Support
+
+=head3 Record Support Routines (compressRecord.c)
+
+=head4 init_record
+
+ static long init_record(compressRecord *prec, int pass)
+
+Space for all necessary arrays is allocated. The addresses are stored in the
+appropriate fields in the record.
+
+=head4 process
+
+ static long process(compressRecord *prec)
+
+See L
+
+=head4 special
+
+ static long special(DBADDR *paddr, int after)
+
+This routine is called when RSET, ALG, or N are set. It performs a reset.
+
+=head4 cvt_dbaddr
+
+ static long cvt_dbaddr(DBADDR *paddr)
+
+This is called by dbNameToAddr. It makes the dbAddr structure refer to the
+actual buffer holding the result.
+
+=head4 get_array_info
+
+ static long get_array_info(DBADDR *paddr, long *no_elements, long *offset)
+
+Obtains values from the circular buffer referenced by VAL.
+
+=head4 put_array_info
+
+ static long put_array_info(DBADDR *paddr, long nNew)
+
+Writes values into the circular buffer referenced by VAL.
+
+=head4 get_units
+
+ static long get_units(DBADDR *paddr, char *units)
+
+Retrieves EGU.
+
+=head4 get_precision
+
+ static long get_precision(DBADDR *paddr, long *precision)
+
+Retrieves PREC.
+
+=head4 get_graphic_double
+
+ static long get_graphic_double(DBADDR *paddr, struct dbr_grDouble *pgd)
+
+Sets the upper display and lower display limits for a field. If the field is
+VAL, the limits are set to HOPR and LOPR, else if the field has upper and lower
+limits defined they will be used, else the upper and lower maximum values for
+the field type will be used.
+
+=head4 get_control_double
+
+ static long get_control_double(DBADDR *paddr, struct dbr_ctrlDouble *pcd)
+
+Sets the upper control and the lower control limits for a field. If the field is
+VAL, the limits are set to HOPR and LOPR, else if the field has upper and lower
+limits defined they will be used, else the upper and lower maximum values for
+the field type will be used.
+
+=head3 Record Processing
+
+Routine process implements the following algorithm:
+
+=over
+
+=item 1.
+
+If INP is not a database link, check monitors and the forward link and return.
+
+=item 2.
+
+Get the current data referenced by INP.
+
+=item 3.
+
+Perform the appropriate algorithm:
+
+=over
+
+=item *
+
+Average: Read N successive instances of INP and perform an element by element
+average. Until N instances have been obtained it just return without checking
+monitors or the forward link. When N instances have been obtained complete the
+algorithm, store the result in the VAL array, check monitors and the forward
+link, and return.
+
+=item *
+
+Circular Buffer: Write the values obtained from INP into the VAL array as a
+circular buffer, check monitors and the forward link, and return.
+
+=item *
+
+N to 1 xxx when INP refers to a scalar: Obtain N successive values from INP and
+apply the N to 1 xxx algorithm to these values. Until N values are obtained
+monitors and forward links are not triggered. When N successive values have been
+obtained, complete the algorithm, check monitors and trigger the forward link,
+and return.
+
+=item *
+
+N to 1 xxx when INP refers to an array: The ILIL and IHIL are honored if ILIL
+E IHIL. The input array is divided into subarrays of length N. The specified
+N to 1 xxx compression algorithm is applied to each sub-array and the result
+stored in the array referenced by VAL. The monitors and forward link are
+checked.
+
+=back
+
+=item 4.
+
+If success, set UDF to FALSE.
+
+=item 5.
+
+Check to see if monitors should be invoked:
+
+=over
+
+=item *
+
+Alarm monitors are invoked if the alarm status or severity has changed.
+
+=item *
+
+NSEV and NSTA are reset to 0.
+
+=back
+
+=item 6.
+
+Scan forward link if necessary, set PACT FALSE, and return.
+
+=back
=cut
diff --git a/src/std/rec/image/compress-1.jpg b/src/std/rec/image/compress-1.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..9ce8f4145887e0361016b4cf3c7543d00d83cbc5
GIT binary patch
literal 16781
zcmeHt2RvL|yYFT&`Vd{TC?VQJ5+VshB!~zig6PqW(Mt@5(M2Z`LJ}euxPfnNyxLf{tyzYzF^z<);s46N<#?S!sbJ39GT
zyI9+h*~7oF1zMM%i2Py;{~gE0FPncM@C$)o2>e3e7Xp8afB|3)*pq*DWY@wqa@rAa
z0({7K7qXDq1KCGG@8sm;<8cuRb@P_BwsW_!m$h|wg(9szpmMUOp@5PK(!<&oVecbk
zWA6xeQx@N-Z4eiN+bN41%j=xh@wj5|1iu#GWp5Cmd)qbuVSB+&Tm{BNuY|maboFqx
z_pug2y1KY|UqmVk|5p1&a{A|Es4z^)%g*8At*aV;lpyDnh5u-apP!$s-&t9AFGr}{
zg$oy;r_VsooRJ}ykn#3+^RY(CxOt2GQNdMvZ(A?8hY#G{P3UKZ);8|GKFY$rzHqyX
z4%QCxHg5FBSeZy8b_d>)%+Ty&IV-`jI&yX&kr$fT^izsHwm-)HJkUFfBbR1DRvc
zb3j-aS-ClQdAK>axsD5npE%Ah#?QqqEGsM~aY{;BidP7F7Akp0TvAH%XC)M1T3UKK
zdUgf|c1b>NKFPm)ks1M3TA+mTI*39Tpk$>0u~Lv)0YS1ajhY<0`x(Cb{9KO-)4(+))IP-vd;v)W=T9U8Z5vw+0J)vY!b}$)y!hD{tbs
z)r%E9YvUD0N6*Q{&BH5pQe5JcLsr^cGMHuSwc*``WZQgs#4ggQVi2f}aPnU2#>Fe}Y|KmHPh?p-`mC
z3ah_2d`|mNg?Wr{2TQQ8GLyNrus+X4w3Hq*G8Z(L5DbMqS`U^QNY}&$mleJrlX*R#
z3Y*bYF?}_CBydSEIzZ!*CwB^$Ug8D){fUSBl{;NIOc2OwTCf=QD6RY@?9i+;?M}C6
zR>yY%NZ@h)vuB|+P6E=I
zpY&Ll%bh9PMyC^99x`e$8$b71@{(#nJAX#Ho^xktLNIz<;Y*d&J(OeWAm|0PUnJ
zJ%?M9AyB+AM!X$*+_c+s2F-vEAYSsLlciIOh>wr10pr@-(ak1@YUrNm91<|qMs8f@
z{_0FYQW;DD+0JA%?vvJie?z9JF-_Q?psBxM<+IFNmG=IHnR9^oswdo+`@t#pITmyY
zfb2)s?nW3&qrka$0~A#X#t>k0d>gK~#pCVRaOn0SlHL9#=h-@E
RzXYX6zlW26=awO`)ZcfJE}U+;cMj6*=Ag9Ca@D1{JEm8wqBpK-
zZm)Z$CWeK`k@pO>Dy(e!u{OGfp|U5R`rj@Q37Y~GuvogeVa+_PS30>STBR+Fys2t0
z7bLhVL@>#8YgXcTwpp46e@udAyPqX^fM8$rKqS`8DD~4DgX~9!HYh%i3w(|PnLC1b
zOG`3M%Y)8B4p-1b%^ZkpUY$0PD?Nb(EXCo&i3emmQkFB|yLJ#q`OeM01Ua1gMFVx#h+IdeaaHbp6j4jG107W0}P3e#5kn%af!N#apLBw!;aMPeAk{o4;hwXhiU{V^2z8(7E=)5oE~^SkqfT3~RJSIG
z5Lp>X06eDYmiHTR+y)7-<>|%`Pj=PX>_47Jdg6X#_%VS4Z>dB#vpXPfe++SuJmq_0
zuC*YAo9H-jB-Kx5dw+0%vJ5oBeV-0*MXdQw?koM_e#0&3nkxx7p~64{hL2|-(O(1(
z&TEi>WTxn&Kut0ikDY-DlYlO5yf+E3zCZ%DwMsYs=kIfvL73s@;wNSecs&AV8TEWE
zU0GiBOeo*}Y9a>Tpklfa%!D$mCNs2Nrd3hXwFjZTESRtSR7J`V&k-WWvUJgkA$bi5
zqwN%~zm3nG4lghs}H9Ly7@BhIo}Jkk~{%
zg)LQD${n6WcY<22cDA^PVTe(JCkYUy`A7opE|X0*DLZzQCUa}AIZOQAqZhX`k-eGc
zB^ue4qWHqsB|uA3tA`e6nh_IfvzHJ~EOix~Be(SjQmrnaPW3qAHac|7W_ic!Ol3X}
zl(|ZUDJ|2EWuyl4aYCkN@;5&7_P0k
z+>M=_6dUK$7c#uO1Wezg8X!-GGyl=rRG*N3q)h@oAQqvcengEj-XI$i&@-O{$#Zvi
z-Cn&QyJ3Il?tS}-T6D}>=ull;n-v#I-yX--LgeoyC`hkWne^MYzc?$3<80wbFq4~h
z+}QddH7eEW`h>^^8Ef`O51Aq@TWsb$DpHab7K!^g5Vx`?FmdwO7)2T!ebzhT-PwMI
z8pWv)bw4*}ubZNmhI1eBGLP0)ZO?v1@zg9)j5Z2{g(%;uH^zTzBn}7&0%2Fv08I5p@iG&r
zWl^=?u9CO?RbesTi_D16QmqQ1Rd*yX@mr~>=G1#Xa5EMLw_WuN2kS2T7hVD*-j^AF
zk-o?m!QJ6v%U=i@Twxfmc`YpqmE{&S!o4l+c%z?6DE_L~SWMZP#D|>FK!S|91^1bY
z&4asnsrPu(V^L;7JcqsCSFW#2SxxkSZLuW4^dnBNrIz2(z+-DlQ?!Mii`L{FBSgIo
z+3};61c(?Xz{=4}Uc(Je2l8dnD0#&5<)*fhgqkRsMVTrM8a;1I(4j2ZD>8`Ddu?^V
z_m*N?XJ_mVe2**DlfON@c>0=Xy2cHo`3cC
z{Ezn{-pMN4nTcboMuP<`CSw|QkGgYyC~GZI12%*!xb-L4z2-{iu~hKGc^TKx{eo0J
ziQRQQYfD3{apOT!tnYrXIDXY<&rjD%H}>+Lrrufp5IzY)kkm8m?z30K9umMdWjQSD
zq$HCM8(I72(Q<(`d?LvR_B6n_zg(S<<;Bgs4nAiNRt?I~M#>PupYA?Oz39pZs(m_Crf8U
zuc3Y~_$<~k3DeEo^?Vg~Nv-vaHYk1SZV0xF$#5;V9%+Eyc0y!BU5}
zO_T2XcDlGd-<*3>Rg&L2q{&IS90So8oG3=SltdjZRRMJz?FwhZF(p1(!WEx4RRhBYV`kZs
ztcd)$koNKtE$kTP_7kJ}zQr_TZbiRKK5LjoBc%~vt*vN?TH;g8
z$scKN@Ws0izcgyO&jiWbVSe8Is6ypLINkszV=+F3-)pSrtIbk$o!qZFo+=e-kzA^w
zg(*c#0^83{j+sH3Ovb)n>(ge+(^y{=dn`niYNbN_+^{K+>|f;~M#}_2nC7?+&||KA
zT@0^A4zDpPYXp$pr8^v3|84>60r#FChFceWx72C9hdWoY{d1{os;59)^x;LvpiD|E
z+N(bd72Z+|ojUh;KTMLblJEUA^WgeHLIm&23~%dubYUweqf`s1iWvpayz>MmY-RHl
z=YwXZmJ_9B8nnZWak(gnK%EaJ4Ku?ASMJH=>FXq
zp$FXO^BXz-Ns#|zIb(N5ZRt4NUg-QwO;em61BwdA6}E$TcBRFNHQ~ju`3>gS3hD3*
zO-!fw^-OnHBtR-x-VVI>x$nC3QODP8ush@4yJmoZ+oV)FRLZmDnLu^@et2l!dVi
zA2P){lh2wfKlmPHE|`uu-QngvR##OILKsi6NTbZkko!AQt6G>;;$>~l`76ocE;ahY
z)d9Pi|Iir*6q&wDDnN|6WCm9{a;&|mnPU9>-V7`vvTE#<14)HR;Y}ft4Qwv*Nzrfg
zz@vA~4)5Pi*Y;d+S@BYlg-W@H|_9SoN%TDZfYL`RD}c<8-_lv*L~_l
zBZB+J;yV_S)O$rD(WCxWo=4Px-v`<7e5AJ?=S7YPPR`so+PgZ%sRKs{wcp7(Md>QL
z%jW5;H?`050m?ckKiu$aKOSS|dR+Z!0!_Q#!&kE_m)o~io>wng86m4%
zkvoweJJ^)t@-CJ6ewOkGoHkBFw@4yIh+*~QgID8y75rDg9~^~tazBRGY%zqPJkZaOhK5+lGH4OKpRd1;(JK|
zcli)T74osvXM>yM^NI=wWtmY9liku5&LE|!keLpLlLe=cEt5eA6TQvi+av@tV
zwIp?Z_CSC-i0$yCdIDSPt*x)E+H1|JM+nEis}NC-55x{@;t>TFqf5qI)vObr#8S#c
zHSVf5f}9K60N?F@FiU>U9*1S3K;PRET@s+*|Kq{&uj(xm-{@pc@t@IJ8}TVR$POo{
zlpx*(d@IQ}o3t7|W#;cdU043}XfmVQ>x`6hEMQk?7m6_&F}WTED&R-7Dal~jS}hp{
zOMM|sC=G>F3(wORB|9q^7pcy@@Kr{P7v?Y?G56mp?>kXFv?D+0ZVa;@8>ag*ILvpOnf18=wES~6Fh(uGyOdhBX@@puGVyl&xt-|f6Pgl8(TLaCPH+&*F!
z2g*vs6ZQ6~rOh5Ot1(r~Xj)FJjye6`30tQ;7EZGbq0!2JEy%a>ITy)bHZc0`DD#sM
zcnS{M5e~WF9yVHsU8pSWgaz4}ey9QS--NXtalS(N<581PEOPK%VfXG(QpVb-BAmsm
z6H>+_fSLT@9(mDySFbsV*^~vuz&(dT`$u62tEH=|0zp{ES$aHqD8BjXg*i0p;M^x
zd@~xl)PM{JCYu!|gD+6W#ve>Y*U9c0F$lL-k2Q^~-!0`E{%m4@
zDeId4V+@Zdr4wb_AAIqj7H;<>p2mB_F}BjC?X@NO5k#uX-J|%wIoe_)Cw1KI9_YqW3F8+x`BQ87Kr;H!(=E`Pa0_zq5
zo18dd7ZW=3Fj-akgFsbo?$U0(K#z-09NC@XP#$_W&YwEAu<;wfLy$cBGq6U{LC_>m
z8%^MoZkBk=_b}wl*IJ;%c$^`5+XZqZGH&ksY^x)Hl(#cyk
z@7_7w3`W__?OyIKrD3uq+^c;;AQy^5z5!y3GXXeY|I;B6-DL-=B0Jy}B9NMLw8ZAo#!b!*}yu>mmX4
zhaCxTQy%qtLHZYSsL1ZEe!(NQZUGW7ok{{KOKY2hPg!xoD<);*UON%Z>#b6Y$M%n%
z2i6%=;LahK7=ai#m%UFpiXN^*AJCRTkMbMl63o%VKLih0%Wn3e=NRls
zfF+Jxzi^1mZ=sLB9OV47@BU(N^gsNL;tM{hDFvV2I!5!#7lmud(;cRBE)Nq+j;Fkn
z1#EXDe|4Ju-5o?4{TE^RBvAkW
literal 0
HcmV?d00001
diff --git a/src/std/rec/image/compress-2.gif b/src/std/rec/image/compress-2.gif
new file mode 100644
index 0000000000000000000000000000000000000000..e4cbf62a7f500e418fe548c9785fa24831703ac6
GIT binary patch
literal 4136
zcmeH``9BkmqjSBh8Vk
z8k?(JIlt237$H|k=KJ{k5#L`vkMHvzcs-u4$Kz=Uu`n?7__lBVzF85Gy}dn=|L4Ex
zfxSK9f051qxr*}kfqfz(6cMfeEd8em5ecD)zT`=8WnLRrLRQ0-SDF7l@tBGg6#Qvse)UKZX6_so*k9>8Q0g{5@YAuzVziTH
z&C!O`IlpqQ?c%)p(sF1eqp9>n<=6MP(RV$aTY=v?UZ_~QT^p(T(M7y6>`Kk9nfaJ!
zmZarsuw-A(bSQh+(@?uGTkC@WI)|dlwzEc>9Q-B+>NA!qOu~HViycikBt-N@EoPC!EDx5te@V^LPc?jom0~8tXC)Y%D#_2V){(rC
zY$XR_Wt|g^JezgRew&$d|AOFLu0|~IY!1|D)HCaX7^EoOo524{Hf-3=rvl#@7Lsl=
z@d`2FL3mY~QWXDO-q|)2UVQ87sfcv;_WsQI#K$?9iy
z`x4Zh0cwuQFYmYODlcm}>%UO+$+SK%reg-^K`=P*p}l1P+Xc+QS9d0L}uFBc-ztIh!2SN7ol`_~BCqHyR;*
zF^C>&Q=uFEPq)hRkX5dk^!hp((TkcAtpNn}p-+E&vI$QsL-$-tmELKaage&aHG9}!
zdjnj3@yhQZQARl(shy_vhHFRgU+xw-W>=dRY{uhb4wf@^V>Yh;*;u|i*vSz6I;?e|
zx&*_KnIdY{o!>9@3*)dlMKu^XFQRRURXN}xt_uEF!s1T+^|2iCt*xto1bo7ivmUR5
ziN<1jT^QkT|L4PS0LcCLN=#el8EJULb)nJ<)+~@IEepQB_h5zapqnYT>%av?tt34<
z!UB9`eYi|sN%pnzjE=i6BP->l5mXL7iO}i552mLeyIH3;c^#&IR?=c#OZzVIJOdA(#S~7~cX?!Oie*d+e;sSF}suWh%4l*xB|
zIS+S!e$%k=GI+ND8fpL8CufZoQOiHMVCC3kzoCNMEwdCw-H%(U)h$p?g4Hrdnoh*4
z7ejcCZiEqzu3i=G=}#EY_zj=_x;nW>%NrE%5AP79`f$p37b`{Q!WZGSMe4#Nuh&L0
zLo?>DU!ntpYzF|Nt2u8T{?i2y>l$nSmc!w|lS43+J40f;%gu|R&|;JV3nX6|Z(b4Z
zY3(L(l=q^i_&YU~P5dCs*B7BHZqm9Y)ufSv$(pzOtn5G>Zo`KIS72f@(>j#MCdAcwA?c{6m!!*3B}{3B
zGVN-w`@B{qn)b?O@!G817m`Y(9h|y5;d4!zoQIiQrkiEV?c0OW1H`)TN}6~iHq`fD
z>O&Mr2F_Uq6!e|(Lzw;pI$`@gYmg%Mge`s4*U{5($Q;coz9Q;x^xk>sjrK;NeAB!O
zg4``r688A=*`EV4e38>I8DRv2FWv
z1gbfiF6C3u=pRtx5~)A;?|US0bTNXZTq
z{Wk2ge^=ts8*+xqAs%d
znp?5b*nE(11?pp%X^nb3Xl8Q5Fb(cyuN8H*5w*dDn~dR`kq1uIW6@e$IintPKi
zPnS(%5{<{FzUOYAY|(4WXM|L0wzL=q3G>y3EJTKr;tCEFJ%4$5<*!l8dz@4KqM6+j
zg6iqtGc)0wn!TY53rL}|SVOO7MWoP$&TjKYif%<|OdEE$yVJ}glHBMOTVeGVb;Eh#m@@2W>mWX-zJ78`cQ2tr)uksT5G
z#oR?%EO7!BJrsewq>p;WLj?UBEd=5e2w(t?XeHYbmokS-;NfbW;u*6*1QC+!i<^|Q
zqSV1iBN(Oup#T|vy3Wh3Bf)($ycidI9!F62MXPVc-|GlhQ44$dAX=53#CZ@`Bazf-
z5cP_OYMCRH_$I;h6S%x2zFKULTGD%7a_wC5fJ90MIK?k9<$)7nVkBj9D`g6h`pqEq
z2ivYsEqPc1+xFl|JubD1i2cZpJ$4_X?VG%2;P*=+$XG3ch6L>TVy4xg>vQsZYJU5-
zQx4*7#G&BBqbZV*7e`CL$Dsu2QTc!PFXGvEWl0f02viAwUl9mVE&-^FV&whgRn^l^
z``M`T!RJfxnubCf5I=ozJwwqT^DHoP6Y$~>(N+%+zD~mZE5IAo$C?2#29OMM^+anR
z7!nDw+y>t*K{}AE?n_!ak)ol7Ko^L$8!2k2>b3{ad$27N5}xSkm;-}Y>@&=HY>0S*
z&-pQz+;2tlsUmna7&ewQFdY-imnAtp(Z@ajZ^
zj@&vml9+&q_92<$NQX*DX%JHM4-!M2jP=XO7GzRKbIbUsylrBs9~pIvd~B5L?oH85
zq=XhyEVz`>-{f1+%xXhQGsKdMmuL(zrpO0nmVEa`bx9ibTg|BfQ
z5on}7bqsHPtRqobKkOlZ&hn$_eMSj#DaSf!mN>*t1ohl+x^o1LL8K0!gY9dlf7_&N
zl8~)F?!W8kqVu$7-4OLzMyq&^Zjn4HHg6VB{Vth54GsAv8FrkE7n1Tn;PSgR-UC`bVD&^y;x3_Efb?a4I-0Fplm_|J9&(a
JQaC1J{J)wmzxn_G
literal 0
HcmV?d00001
From 128d2a93c8bbadc5df148c8c1284923daa2fa9a7 Mon Sep 17 00:00:00 2001
From: Joao Paulo Martins
Date: Wed, 4 Sep 2019 17:25:28 +0200
Subject: [PATCH 099/281] Fixing waveform record documentation after review
---
src/ioc/db/menuFtype.dbd.pod | 2 +-
src/std/rec/waveformRecord.dbd.pod | 70 +++++-------------------------
2 files changed, 12 insertions(+), 60 deletions(-)
diff --git a/src/ioc/db/menuFtype.dbd.pod b/src/ioc/db/menuFtype.dbd.pod
index fd643f625..6b783dfec 100644
--- a/src/ioc/db/menuFtype.dbd.pod
+++ b/src/ioc/db/menuFtype.dbd.pod
@@ -10,7 +10,7 @@
=head1 Menu menuFtype
-This menu is used for the C field of all record types.
+This menu is used for the C and similar fields of many record types.
=menu menuFtype
diff --git a/src/std/rec/waveformRecord.dbd.pod b/src/std/rec/waveformRecord.dbd.pod
index e40aa2a21..ca48e4792 100644
--- a/src/std/rec/waveformRecord.dbd.pod
+++ b/src/std/rec/waveformRecord.dbd.pod
@@ -1,4 +1,11 @@
-=pod
+#*************************************************************************
+# 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.
+# EPICS BASE is distributed subject to a Software License Agreement found
+# in file LICENSE that is included with this distribution.
+#*************************************************************************
=title Waveform Record (waveform)
@@ -10,6 +17,8 @@ types.
=cut
+include "menuFtype.dbd"
+
menu(waveformPOST) {
choice(waveformPOST_Always,"Always")
choice(waveformPOST_OnChange,"On Change")
@@ -115,64 +124,7 @@ The RARM field causes the device to re-arm when this field is set to 1.
=head4 Possible data types for FTVL
-=begin html
-
-
-
Index
Identifier
Choice String
-
-
0
-
menuFtypeSTRING
-
STRING
-
-
-
1
-
menuFtypeCHAR
-
CHAR
-
-
-
2
-
menuFtypeUCHAR
-
UCHAR
-
-
-
3
-
menuFtypeSHORT
-
SHORT
-
-
-
4
-
menuFtypeUSHORT
-
USHORT
-
-
-
5
-
menuFtypeLONG
-
LONG
-
-
-
6
-
menuFtypeULONG
-
ULONG
-
-
-
7
-
menuFtypeFLOAT
-
FLOAT
-
-
-
8
-
menuFtypeDOUBLE
-
DOUBLE
-
-
-
9
-
menuFtypeENUM
-
ENUM
-
-
-
-
-=end html
+=menu menuFtype
=head3 Operator Display Parameters
From 31811e53b33f944ef5fbba52c3b0a5c6d81f721f Mon Sep 17 00:00:00 2001
From: Saeed Haghtalab
Date: Wed, 4 Sep 2019 21:14:11 +0200
Subject: [PATCH 100/281] Compress record pod update after review - Revert "N
to 1 Median" choice entry place - Convert images to PNG and update Makefile -
Update record support routins definition based on epics7 recSup.h
---
src/std/rec/Makefile | 8 +++-----
src/std/rec/compressRecord.dbd.pod | 28 +++++++++++++++-------------
src/std/rec/image/compress-1.jpg | Bin 16781 -> 0 bytes
src/std/rec/image/compress-1.png | Bin 0 -> 2126 bytes
src/std/rec/image/compress-2.gif | Bin 4136 -> 0 bytes
src/std/rec/image/compress-2.png | Bin 0 -> 7608 bytes
6 files changed, 18 insertions(+), 18 deletions(-)
delete mode 100644 src/std/rec/image/compress-1.jpg
create mode 100644 src/std/rec/image/compress-1.png
delete mode 100644 src/std/rec/image/compress-2.gif
create mode 100644 src/std/rec/image/compress-2.png
diff --git a/src/std/rec/Makefile b/src/std/rec/Makefile
index 307662ebb..3f9c763b5 100644
--- a/src/std/rec/Makefile
+++ b/src/std/rec/Makefile
@@ -54,8 +54,6 @@ dbRecStd_SRCS += $(patsubst %,%.c,$(stdRecords))
HTMLS += $(patsubst %.dbd.pod,%.html,$(notdir $(wildcard ../rec/*Record.dbd.pod)))
-vpath %.jpg $(USR_VPATH) .. $(SRC_DIRS) ../rec/image
-vpath %.gif $(USR_VPATH) .. $(SRC_DIRS) ../rec/image
-
-HTMLS += compress-1.jpg
-HTMLS += compress-2.gif
+vpath %.png $(SRC_DIRS)
+HTMLS += image/compress-1.png
+HTMLS += image/compress-2.png
diff --git a/src/std/rec/compressRecord.dbd.pod b/src/std/rec/compressRecord.dbd.pod
index 90cacb77d..f0a586baa 100644
--- a/src/std/rec/compressRecord.dbd.pod
+++ b/src/std/rec/compressRecord.dbd.pod
@@ -29,9 +29,9 @@ menu(compressALG) {
choice(compressALG_N_to_1_Low_Value,"N to 1 Low Value")
choice(compressALG_N_to_1_High_Value,"N to 1 High Value")
choice(compressALG_N_to_1_Average,"N to 1 Average")
- choice(compressALG_N_to_1_Median,"N to 1 Median")
choice(compressALG_Average,"Average")
choice(compressALG_Circular_Buffer,"Circular Buffer")
+ choice(compressALG_N_to_1_Median,"N to 1 Median")
}
recordtype(compress) {
@@ -134,9 +134,11 @@ holds the average of the first element of INP over N samples, VAL[1] holds the
average of the next element of INP over N samples, and so on. The following
shows the equation:
+=for comment Latex form of equation bellow : VAL[i] \leftarrow \frac{1}{N}\sum_{n=1}^NINP_{n}[i]
+
=begin html
-
+
=end html
@@ -183,7 +185,7 @@ as in the following diagram:
=begin html
-
+
=end html
@@ -246,57 +248,57 @@ WPTR is used by the dbGetlinks routines.
=head4 init_record
- static long init_record(compressRecord *prec, int pass)
+ long (*init_record)(struct dbCommon *precord, int pass)
Space for all necessary arrays is allocated. The addresses are stored in the
appropriate fields in the record.
=head4 process
- static long process(compressRecord *prec)
+ long (*process)(struct dbCommon *precord)
See L
=head4 special
- static long special(DBADDR *paddr, int after)
+ long (*special)(struct dbAddr *paddr, int after)
This routine is called when RSET, ALG, or N are set. It performs a reset.
=head4 cvt_dbaddr
- static long cvt_dbaddr(DBADDR *paddr)
+ long (*cvt_dbaddr)(struct dbAddr *paddr)
This is called by dbNameToAddr. It makes the dbAddr structure refer to the
actual buffer holding the result.
=head4 get_array_info
- static long get_array_info(DBADDR *paddr, long *no_elements, long *offset)
+ long (*get_array_info)(struct dbAddr *paddr, long *no_elements, long *offset)
Obtains values from the circular buffer referenced by VAL.
=head4 put_array_info
- static long put_array_info(DBADDR *paddr, long nNew)
+ long (*put_array_info)(struct dbAddr *paddr, long nNew);
Writes values into the circular buffer referenced by VAL.
=head4 get_units
- static long get_units(DBADDR *paddr, char *units)
+ long (*get_units)(struct dbAddr *paddr, char *units);
Retrieves EGU.
=head4 get_precision
- static long get_precision(DBADDR *paddr, long *precision)
+ long (*get_precision)(const struct dbAddr *paddr, long *precision);
Retrieves PREC.
=head4 get_graphic_double
- static long get_graphic_double(DBADDR *paddr, struct dbr_grDouble *pgd)
+ long (*get_graphic_double)(struct dbAddr *paddr, struct dbr_grDouble *p);
Sets the upper display and lower display limits for a field. If the field is
VAL, the limits are set to HOPR and LOPR, else if the field has upper and lower
@@ -305,7 +307,7 @@ the field type will be used.
=head4 get_control_double
- static long get_control_double(DBADDR *paddr, struct dbr_ctrlDouble *pcd)
+ long (*get_control_double)(struct dbAddr *paddr, struct dbr_ctrlDouble *p);
Sets the upper control and the lower control limits for a field. If the field is
VAL, the limits are set to HOPR and LOPR, else if the field has upper and lower
diff --git a/src/std/rec/image/compress-1.jpg b/src/std/rec/image/compress-1.jpg
deleted file mode 100644
index 9ce8f4145887e0361016b4cf3c7543d00d83cbc5..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 16781
zcmeHt2RvL|yYFT&`Vd{TC?VQJ5+VshB!~zig6PqW(Mt@5(M2Z`LJ}euxPfnNyxLf{tyzYzF^z<);s46N<#?S!sbJ39GT
zyI9+h*~7oF1zMM%i2Py;{~gE0FPncM@C$)o2>e3e7Xp8afB|3)*pq*DWY@wqa@rAa
z0({7K7qXDq1KCGG@8sm;<8cuRb@P_BwsW_!m$h|wg(9szpmMUOp@5PK(!<&oVecbk
zWA6xeQx@N-Z4eiN+bN41%j=xh@wj5|1iu#GWp5Cmd)qbuVSB+&Tm{BNuY|maboFqx
z_pug2y1KY|UqmVk|5p1&a{A|Es4z^)%g*8At*aV;lpyDnh5u-apP!$s-&t9AFGr}{
zg$oy;r_VsooRJ}ykn#3+^RY(CxOt2GQNdMvZ(A?8hY#G{P3UKZ);8|GKFY$rzHqyX
z4%QCxHg5FBSeZy8b_d>)%+Ty&IV-`jI&yX&kr$fT^izsHwm-)HJkUFfBbR1DRvc
zb3j-aS-ClQdAK>axsD5npE%Ah#?QqqEGsM~aY{;BidP7F7Akp0TvAH%XC)M1T3UKK
zdUgf|c1b>NKFPm)ks1M3TA+mTI*39Tpk$>0u~Lv)0YS1ajhY<0`x(Cb{9KO-)4(+))IP-vd;v)W=T9U8Z5vw+0J)vY!b}$)y!hD{tbs
z)r%E9YvUD0N6*Q{&BH5pQe5JcLsr^cGMHuSwc*``WZQgs#4ggQVi2f}aPnU2#>Fe}Y|KmHPh?p-`mC
z3ah_2d`|mNg?Wr{2TQQ8GLyNrus+X4w3Hq*G8Z(L5DbMqS`U^QNY}&$mleJrlX*R#
z3Y*bYF?}_CBydSEIzZ!*CwB^$Ug8D){fUSBl{;NIOc2OwTCf=QD6RY@?9i+;?M}C6
zR>yY%NZ@h)vuB|+P6E=I
zpY&Ll%bh9PMyC^99x`e$8$b71@{(#nJAX#Ho^xktLNIz<;Y*d&J(OeWAm|0PUnJ
zJ%?M9AyB+AM!X$*+_c+s2F-vEAYSsLlciIOh>wr10pr@-(ak1@YUrNm91<|qMs8f@
z{_0FYQW;DD+0JA%?vvJie?z9JF-_Q?psBxM<+IFNmG=IHnR9^oswdo+`@t#pITmyY
zfb2)s?nW3&qrka$0~A#X#t>k0d>gK~#pCVRaOn0SlHL9#=h-@E