From ca800fa57dcfa76d03666571ee6cdaa75ec55e19 Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Wed, 4 Apr 2018 11:01:45 -0700
Subject: [PATCH 01/25] 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 02/25] 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 03/25] 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 04/25] 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 05/25] 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 06/25] 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 e8db975e7f764c9d6e446de01e307ed27d9313aa Mon Sep 17 00:00:00 2001
From: Andrew Johnson
Date: Mon, 17 Jun 2019 14:33:28 -0500
Subject: [PATCH 07/25] 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 08/25] 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 09/25] 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 10/25] 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 32b3eddb94cf4f47494ff2cc58d6f5e0f3f71d21 Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Sun, 23 Jun 2019 08:56:32 -0700
Subject: [PATCH 11/25] 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 12/25] 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 13/25] 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 14/25] 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 15/25] 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 16/25] 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.
+
+
+void startitup(void) {
+ epicsThreadOpts opts;
+ epicsThreadOptsDefaults(&opts);
+ opts.priority = epicsThreadPriorityMedium;
+
+ ... = epicsThreadCreateOpt("my thread", &threadMain, NULL, &opts);
+}
+
+
+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 17/25] 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 18/25] 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 ff1462fcc78a326548198afff43d3dff9acebb20 Mon Sep 17 00:00:00 2001
From: Andrew Johnson
Date: Fri, 28 Jun 2019 12:28:41 -0500
Subject: [PATCH 19/25] 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 20/25] 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 21/25] 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 22/25] 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 23/25] 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 24/25] 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.
+
+
+typedef struct epicsThreadOpts {
+ unsigned int priority;
+ unsigned int stackSize;
+ unsigned int joinable;
+} epicsThreadOpts;
+#define EPICS_THREAD_OPTS_INIT { \
+ epicsThreadPriorityLow, epicsThreadStackMedium, 0}
+
+epicsThreadId epicsThreadCreateOpt(const char * name,
+ EPICSTHREADFUNC funptr, void * parm, const epicsThreadOpts *opts);
+
+
+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.
void startitup(void) {
- epicsThreadOpts opts;
- epicsThreadOptsDefaults(&opts);
- opts.priority = epicsThreadPriorityMedium;
+ epicsThreadOpts opts = EPICS_THREAD_OPTS_INIT;
+ epicsThreadId tid;
- ... = epicsThreadCreateOpt("my thread", &threadMain, NULL, &opts);
+ opts.priority = epicsThreadPriorityMedium;
+ tid = epicsThreadCreateOpt("my thread", &threadMain, NULL, &opts);
}
-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 25/25] 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);