From f9092783f89d3626fdbd431457f15a084b2fb884 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Sat, 23 Jun 2018 12:36:07 -0700 Subject: [PATCH] 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);