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 }