diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html index 10910080d..faf6e56ba 100644 --- a/documentation/RELEASE_NOTES.html +++ b/documentation/RELEASE_NOTES.html @@ -30,6 +30,76 @@ release.
--> +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 = EPICS_THREAD_OPTS_INIT;
+ epicsThreadId tid;
+
+ opts.priority = epicsThreadPriorityMedium;
+ tid = epicsThreadCreateOpt("my thread", &threadMain, NULL, &opts);
+}
+
+
+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.
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.
Previously IOC executables that made calls to devLib routines would fail to
diff --git a/modules/database/src/ioc/as/asCa.c b/modules/database/src/ioc/as/asCa.c
index d0180448b..21bb47f5f 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 = EPICS_THREAD_OPTS_INIT;
+
+ 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);}
diff --git a/modules/database/src/ioc/db/dbCa.c b/modules/database/src/ioc/db/dbCa.c
index 843fbfc0c..45e178cae 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 = EPICS_THREAD_OPTS_INIT;
+
+ 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);
}
diff --git a/modules/database/src/ioc/db/dbEvent.c b/modules/database/src/ioc/db/dbEvent.c
index 77b12de8d..5e0f4db7c 100644
--- a/modules/database/src/ioc/db/dbEvent.c
+++ b/modules/database/src/ioc/db/dbEvent.c
@@ -84,7 +84,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 */
@@ -123,8 +122,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 ) {
@@ -266,10 +263,6 @@ dbEventCtx db_init_events (void)
{
struct event_user * evUser;
- if (!stopSync) {
- stopSync = epicsMutexMustCreate();
- }
-
if (!dbevEventUserFreeList) {
freeListInitPvt(&dbevEventUserFreeList,
sizeof(struct event_user),8);
@@ -293,9 +286,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)
@@ -310,9 +300,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;
@@ -327,8 +314,6 @@ fail:
epicsEventDestroy (evUser->ppendsem);
if(evUser->pflush_sem)
epicsEventDestroy (evUser->pflush_sem);
- if(evUser->pexitsem)
- epicsEventDestroy (evUser->pexitsem);
freeListFree(dbevEventUserFreeList,evUser);
return NULL;
}
@@ -349,7 +334,6 @@ epicsShareFunc void db_cleanup_events(void)
dbevFieldLogFreeList = NULL;
}
- /* intentionally leak stopSync to avoid possible shutdown races */
/*
* DB_CLOSE_EVENTS()
*
@@ -371,30 +355,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 */
}
/*
@@ -1074,17 +1043,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;
}
@@ -1096,6 +1062,11 @@ int db_start_events (
void *init_func_arg, unsigned osiPriority )
{
struct event_user * const evUser = (struct event_user *) ctx;
+ epicsThreadOpts opts = EPICS_THREAD_OPTS_INIT;
+
+ opts.stackSize = epicsThreadGetStackSize(epicsThreadStackMedium);
+ opts.priority = osiPriority;
+ opts.joinable = 1;
epicsMutexMustLock ( evUser->lock );
@@ -1113,15 +1084,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;
}
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();
}
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/epicsThread.cpp b/modules/libcom/src/osi/epicsThread.cpp
index 892d73de0..92f833847 100644
--- a/modules/libcom/src/osi/epicsThread.cpp
+++ b/modules/libcom/src/osi/epicsThread.cpp
@@ -31,6 +31,18 @@
using namespace std;
+epicsThreadId epicsShareAPI epicsThreadCreate (
+ const char * name, unsigned int priority, unsigned int stackSize,
+ EPICSTHREADFUNC funptr,void * parm )
+{
+ epicsThreadOpts opts = EPICS_THREAD_OPTS_INIT;
+ opts.priority = priority;
+ opts.stackSize = stackSize;
+ opts.joinable = 0;
+
+ return epicsThreadCreateOpt(name, funptr, parm, &opts);
+}
+
epicsThreadRunable::~epicsThreadRunable () {}
void epicsThreadRunable::run () {}
void epicsThreadRunable::show ( unsigned int ) const {}
@@ -141,6 +153,13 @@ bool epicsThread::exitWait ( const double delay ) throw ()
if ( this->pThreadDestroyed ) {
*this->pThreadDestroyed = true;
}
+ if(!joined) {
+ {
+ epicsGuard < epicsMutex > guard ( this->mutex );
+ joined = true;
+ }
+ epicsThreadMustJoin(this->id);
+ }
return true;
}
epicsTime exitWaitBegin = epicsTime::getCurrent ();
@@ -154,6 +173,12 @@ bool epicsThread::exitWait ( const double delay ) throw ()
epicsTime current = epicsTime::getCurrent ();
exitWaitElapsed = current - exitWaitBegin;
}
+ if(this->terminated && !joined) {
+ joined = true;
+
+ epicsGuardRelease < epicsMutex > unguard ( guard );
+ epicsThreadMustJoin(this->id);
+ }
}
catch ( std :: exception & except ) {
errlogPrintf (
@@ -177,11 +202,18 @@ epicsThread::epicsThread (
epicsThreadRunable & runableIn, const char * pName,
unsigned stackSize, unsigned priority ) :
runable ( runableIn ), id ( 0 ), pThreadDestroyed ( 0 ),
- begin ( false ), cancel ( false ), terminated ( false )
+ begin ( false ), cancel ( false ), terminated ( false ),
+ joined ( false )
{
- this->id = epicsThreadCreate (
- pName, priority, stackSize, epicsThreadCallEntryPoint,
- static_cast < void * > ( this ) );
+ epicsThreadOpts opts = EPICS_THREAD_OPTS_INIT;
+ 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 84b2c4788..da16a0b25 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,46 +80,140 @@ 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;
+ /** 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).
+ * If joinable=1, then epicsThreadMustJoin() must be called for cleanup thread resources.
+ */
+ unsigned int joinable;
+} epicsThreadOpts;
+
+/** 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.
+ * @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 );
+ EPICSTHREADFUNC funptr,void * parm );
+
+/* 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);
+/** 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);
@@ -115,10 +224,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
@@ -130,33 +246,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 */
@@ -178,6 +325,7 @@ private:
bool begin;
bool cancel;
bool terminated;
+ bool joined;
bool beginWait () throw ();
epicsThread ( const epicsThread & );
diff --git a/modules/libcom/src/osi/os/Linux/osdThread.h b/modules/libcom/src/osi/os/Linux/osdThread.h
index 7d2a4868d..bb1fdcb0a 100644
--- a/modules/libcom/src/osi/os/Linux/osdThread.h
+++ b/modules/libcom/src/osi/os/Linux/osdThread.h
@@ -22,6 +22,7 @@ extern "C" {
typedef struct epicsThreadOSD {
ELLNODE node;
+ int refcnt;
pthread_t tid;
pid_t lwpId;
pthread_attr_t attr;
@@ -35,6 +36,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 769e95820..bdcd8c17e 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;
@@ -163,6 +167,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.
@@ -183,9 +203,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);
}
@@ -196,21 +219,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 ();
@@ -222,10 +258,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;
}
/*
@@ -247,7 +287,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,
@@ -263,15 +304,26 @@ 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 )
{
+ unsigned int stackSize;
rtems_id tid;
rtems_status_code sc;
char c[4];
- if (!initialized) epicsThreadInit();
+ 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 (stackSize < RTEMS_MINIMUM_STACK_SIZE) {
errlogPrintf ("Warning: epicsThreadCreate %s illegal stackSize %d\n",
name, stackSize);
@@ -279,7 +331,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,
@@ -289,7 +341,13 @@ epicsThreadCreate (const char *name,
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;
}
@@ -305,6 +363,51 @@ threadMustCreate (const char *name,
return tid;
}
+void epicsThreadMustJoin(epicsThreadId id)
+{
+ rtems_id target_tid = (rtems_id)id, self_tid;
+ struct taskVar *v = 0;
+
+ rtems_task_ident (RTEMS_SELF, 0, &self_tid);
+
+ {
+ uint32_t note;
+ 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 || !v->joinable) {
+ if(epicsThreadGetIdSelf()==id) {
+ 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
+ * '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) {
+ /* 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("epicsThreadMustJoin('%s') -> %s\n", v->name, rtems_status_text(sc));
+ }
+ }
+
+ v->joinable = 0;
+ 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 4451f845a..4eef8c01f 100644
--- a/modules/libcom/src/osi/os/RTEMS/osdThread.h
+++ b/modules/libcom/src/osi/os/RTEMS/osdThread.h
@@ -3,10 +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.
\*************************************************************************/
+#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.c b/modules/libcom/src/osi/os/WIN32/osdThread.c
index 8cdb4a3f4..1cfa1ec5c 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 );
@@ -526,6 +531,7 @@ static win32ThreadParam * epicsThreadParmCreate ( const char *pName )
pParmWIN32->pName = (char *) ( pParmWIN32 + 1 );
strcpy ( pParmWIN32->pName, pName );
pParmWIN32->isSuspended = 0;
+ epicsAtomicIncrIntT(&pParmWIN32->refcnt);
}
return pParmWIN32;
}
@@ -579,11 +585,14 @@ 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;
+ unsigned int stackSize;
int osdPriority;
DWORD wstat;
BOOL bstat;
@@ -592,18 +601,26 @@ epicsShareFunc epicsThreadId epicsShareAPI epicsThreadCreate (const char *pName,
return NULL;
}
+ 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 ) {
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, stackSize, epicsWin32ThreadEntry,
pParmWIN32,
CREATE_SUSPENDED | STACK_SIZE_PARAM_IS_A_RESERVATION,
& threadId );
@@ -615,7 +632,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 );
@@ -637,9 +654,48 @@ epicsShareFunc epicsThreadId epicsShareAPI epicsThreadCreate (const char *pName,
return NULL;
}
+ if(opts->joinable) {
+ pParmWIN32->joinable = 1;
+ epicsAtomicIncrIntT(&pParmWIN32->refcnt);
+ }
+
return ( epicsThreadId ) pParmWIN32;
}
+void epicsThreadMustJoin(epicsThreadId id)
+{
+ win32ThreadParam * pParmWIN32 = id;
+
+ if(!id) {
+ /* no-op */
+ } else if(!pParmWIN32->joinable) {
+ 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) {
+ DWORD status = WaitForSingleObject(pParmWIN32->handle, INFINITE);
+ if(status != WAIT_OBJECT_0) {
+ /* TODO: signal error? */
+ }
+
+ pParmWIN32->joinable = 0;
+ epicsParmCleanupWIN32(pParmWIN32);
+ } else {
+ /* join self silently does nothing */
+ pParmWIN32->joinable = 0;
+ epicsParmCleanupWIN32(pParmWIN32);
+ }
+}
+
/*
* epicsThreadSuspendSelf ()
*/
diff --git a/modules/libcom/src/osi/os/WIN32/osdThread.h b/modules/libcom/src/osi/os/WIN32/osdThread.h
index 136e96bf4..69bc364f0 100644
--- a/modules/libcom/src/osi/os/WIN32/osdThread.h
+++ b/modules/libcom/src/osi/os/WIN32/osdThread.h
@@ -3,13 +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
-
#endif /* osdThreadh */
diff --git a/modules/libcom/src/osi/os/posix/osdThread.c b/modules/libcom/src/osi/os/posix/osdThread.c
index e3e0fe3cf..a13d3876c 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");
@@ -445,15 +453,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 (stackSizeClass