From d989c8fade61a2fbff0823276f3a52449022a96c Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Wed, 4 Apr 2018 11:23:53 -0700 Subject: [PATCH] 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 */