Merge remote-tracking branch 'origin/3.15' into 7.0

* origin/3.15: (40 commits)
  Revert "More msi.plt retries for Jenkins builds on Windows"
  Revert "Testing msi: Add retries if necessary"
  msi: Flush stdout on program exit
  Now fix the non-windows systems
  Don't use / in Windows program paths
  Redirect msi's STDERR to /dev/null (NUL on Windows) during tests
  VxWorks: Mark undetected underflow parse test as ToDo
  Fix VxWorks osiSockOptMcastLoop_t => char
  Fix sync filter example in dbd.pod
  doc/ca: clarify variable size array subscription
  Remove cacExitHandler
  Don't clear caClientCallbackThreadId in CA's exit handler
  appveyor-ci: ANL Make install 4.1 -> 4.2.1
  appveyor-ci: use pre-installed AppVeyor MinGW
  appveyor-ci: use choco MinGW 5.3.0 to work around build problem (fixes lp:1827225)
  appveyor-ci: exclude some cygwin builds (broken compiler) appveyor-ci: remove slack, add email and GitHub notifications (cherry-picked from branch 7.0)
  epicsTime: rely on implicit copy constructor
  iocLogServer: check return values
  Fix potential buffer overflow in iocLogServer
  Fix weird use of strncpy
  ...

# Conflicts:
#	modules/database/src/ioc/dbtemplate/msi.c
#	modules/database/src/std/rec/stringinRecord.c
#	modules/database/src/std/rec/stringoutRecord.c
#	modules/database/test/ioc/dbtemplate/msi.plt
#	modules/libcom/src/osi/os/vxWorks/osdSock.h
#	src/ioc/dbtemplate/msi.c
#	src/ioc/dbtemplate/msi.cpp
This commit is contained in:
Michael Davidsaver
2019-08-31 15:05:37 -07:00
18 changed files with 914 additions and 210 deletions

View File

@@ -30,6 +30,76 @@ release.</p>
-->
<h3>New and modified epicsThread APIs</h3>
<h4><code>epicsThreadCreateOpt()</code></h4>
<p>A new routine <code>epicsThreadCreateOpt()</code> is an alternative to
<code>epicsThreadCreate()</code> which takes some arguments via a structure
(<code>struct epicsThreadOpts</code>) to allow for future extensions.</p>
<blockquote><pre>
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);
</pre></blockquote>
<p>The final <code>opts</code> parameter may be <code>NULL</code> 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 <code>epicsThreadOpts</code>
structure as shown below. Always initialize one of these structures using the
<code>EPICS_THREAD_OPTS_INIT</code> macro to ensure that any additional fields
that get added in the future are set to their default values.</p>
<blockquote><pre>
void startitup(void) {
epicsThreadOpts opts = EPICS_THREAD_OPTS_INIT;
epicsThreadId tid;
opts.priority = epicsThreadPriorityMedium;
tid = epicsThreadCreateOpt("my thread", &amp;threadMain, NULL, &amp;opts);
}
</pre></blockquote>
<p>C or C++ Code that also needs to build on earlier versions of Base can use
<code>#ifdef&nbsp;EPICS_THREAD_OPTS_INIT</code> to determine whether the
<code>epicsThreadCreateOpt()</code> API is available on this Base version.</p>
<h4>Thread stack sizes</h4>
<p>The <code>stackSize</code> member of the <code>epicsThreadOpts</code>
structure and the equivalent parameters to the <code>epicsThreadCreate()</code>
and <code>epicsThreadMustCreate()</code> routines can now be passed either one
of the <code>epicsThreadStackSizeClass</code> enum values or a value returned
from the <code>epicsThreadGetStackSize()</code> routine.</p>
<h4><code>epicsThreadMustJoin()</code></h4>
<p>If the new <code>joinable</code> flag of an <code>epicsThreadOpts</code>
structure is non-zero (the default value is zero), the new API routine
<code>epicsThreadMustJoin()</code> <em>must</em> be called with the thread's
<code>epicsThreadId</code> 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
<code>epicsThreadId</code> will no longer be valid and should not be used after
the <code>epicsThreadMustJoin()</code> routine returns.</p>
<p>A thread that was originally created with its joinable flag set may itself
call <code>epicsThreadMustJoin()</code>, 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 <code>epicsThreadId</code>
of a thread that is not joinable gets invalidated as soon as its main function
returns.</p>
<h3>Non-VME RTEMS targets now define pdevLibVME</h3>
<p>Previously IOC executables that made calls to devLib routines would fail to

View File

@@ -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);}

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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();
}

View File

@@ -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)

View File

@@ -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 ();
}

View File

@@ -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 & );

View File

@@ -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;

View File

@@ -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, &note);
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)
{

View File

@@ -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 */

View File

@@ -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 ()
*/

View File

@@ -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 */

View File

@@ -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<epicsThreadStackSmall) {
errlogPrintf("epicsThreadGetStackSize illegal argument (too small)");
return stackSizeTable[epicsThreadStackBig];
@@ -511,42 +526,66 @@ epicsShareFunc void epicsShareAPI epicsThreadOnce(epicsThreadOnceId *id, void (*
checkStatusQuit(status,"pthread_mutex_unlock","epicsThreadOnce");
}
epicsShareFunc epicsThreadId epicsShareAPI 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 )
{
unsigned int stackSize;
epicsThreadOSD *pthreadInfo;
int status;
sigset_t blockAllSig, oldSig;
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,priority,stackSize,funptr,parm);
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,priority,stackSize,funptr,parm);
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");
return(pthreadInfo);
status = pthread_sigmask(SIG_SETMASK, &oldSig, NULL);
checkStatusOnce(status, "pthread_sigmask");
if (pthreadInfo->joinable) {
/* extra ref for epicsThreadMustJoin() */
epicsAtomicIncrIntT(&pthreadInfo->refcnt);
}
return pthreadInfo;
}
/*
@@ -584,7 +623,40 @@ static epicsThreadOSD *createImplicit(void)
}
return pthreadInfo;
}
void epicsThreadMustJoin(epicsThreadId id)
{
void *ret = NULL;
int status;
if(!id) {
return;
} else if(!id->joinable) {
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;
}
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");
id->joinable = 0;
free_threadInfo(id);
}
epicsShareFunc void epicsShareAPI epicsThreadSuspendSelf(void)
{
epicsThreadOSD *pthreadInfo;

View File

@@ -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
@@ -22,6 +21,7 @@ extern "C" {
typedef struct epicsThreadOSD {
ELLNODE node;
int refcnt;
pthread_t tid;
pthread_attr_t attr;
struct sched_param schedParam;
@@ -34,6 +34,7 @@ typedef struct epicsThreadOSD {
int isRealTimeScheduled;
int isOnThreadList;
unsigned int osiPriority;
int joinable;
char name[1]; /* actually larger */
} epicsThreadOSD;

View File

@@ -34,6 +34,22 @@
#include "vxLib.h"
#include "epicsExit.h"
#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.
*/
#include <taskUtilLib.h>
#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;
@@ -170,19 +187,58 @@ void epicsThreadOnce(epicsThreadOnceId *id, void (*func)(void *), void *arg)
}
semGive(epicsThreadOnceMutex);
}
#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.
*/
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
@@ -190,27 +246,101 @@ 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 )
{
unsigned int stackSize;
int tid;
epicsThreadInit();
if(stackSize<100) {
errlogPrintf("epicsThreadCreate %s illegal stackSize %d\n",name,stackSize);
return(0);
if (!opts) {
static const epicsThreadOpts opts_default = EPICS_THREAD_OPTS_INIT;
opts = &opts_default;
}
tid = taskSpawn((char *)name,getOssPriorityValue(priority),
stackSize = opts->stackSize;
if (stackSize <= epicsThreadStackBig)
stackSize = epicsThreadGetStackSize(stackSize);
if (stackSize < 100) {
errlogPrintf("epicsThreadCreate %s illegal stackSize %d\n",
name, stackSize);
return 0;
}
tid = taskCreate((char *)name,getOssPriorityValue(opts->priority),
TASK_FLAGS, 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)
{
#ifdef EPICS_THREAD_CAN_JOIN
const char *fn = "epicsThreadMustJoin";
int tid = (int) id;
SEM_ID joinSem;
STATUS status;
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);
}
#endif
}
void epicsThreadSuspendSelf()

View File

@@ -3,11 +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_MINOR < 9)
#undef EPICS_THREAD_CAN_JOIN
#endif
#endif /* osdThreadh */

View File

@@ -18,11 +18,14 @@
#include <math.h>
#include "epicsThread.h"
#include "epicsEvent.h"
#include "epicsTime.h"
#include "errlog.h"
#include "epicsUnitTest.h"
#include "testMain.h"
namespace {
static epicsThreadPrivate<int> privateKey;
class myThread: public epicsThreadRunable {
@@ -37,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;
@@ -52,7 +55,7 @@ void myThread::run()
startEvt.signal();
int *pset = argvalue;
privateKey.set(argvalue);
epicsThreadSleep(2.0);
int *pget = privateKey.get();
testOk1(pget == pset);
@@ -60,34 +63,8 @@ void myThread::run()
testOk1(thread.getPriority() == epicsThreadGetPriority(self));
}
typedef struct info {
int isOkToBlock;
} info;
extern "C" {
static void thread(void *arg)
void testMyThread()
{
info *pinfo = (info *)arg;
epicsThreadSetOkToBlock(pinfo->isOkToBlock);
epicsThreadSleep(1.0);
testOk(epicsThreadIsOkToBlock() == pinfo->isOkToBlock,
"%s epicsThreadIsOkToBlock() = %d",
epicsThreadGetNameSelf(), pinfo->isOkToBlock);
epicsThreadSleep(0.1);
}
}
MAIN(epicsThreadTest)
{
testPlan(9);
unsigned int ncpus = epicsThreadGetCPUs();
testDiag("System has %u CPUs", ncpus);
testOk1(ncpus > 0);
const int ntasks = 3;
myThread *myThreads[ntasks];
@@ -107,16 +84,149 @@ MAIN(epicsThreadTest)
myThreads[i]->thread.exitWait();
delete myThreads[i];
}
}
unsigned int stackSize = epicsThreadGetStackSize(epicsThreadStackSmall);
struct joinStuff {
epicsThreadOpts *opts;
epicsEvent *trigger;
epicsEvent *finished;
};
info infoA = {0};
epicsThreadCreate("threadA", 50, stackSize, thread, &infoA);
void donothing(void *arg)
{}
info infoB = {1};
epicsThreadCreate("threadB", 50, stackSize, thread, &infoB);
void dowait(void *arg)
{
epicsEvent *trigger = (epicsEvent *) arg;
trigger->wait();
epicsThreadSleep(0.1);
}
void dodelay(void *arg)
{
epicsThreadSleep(2.0);
}
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);
// 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);
// This is a no-op as well, except for a warning.
eltc(0);
epicsThreadMustJoin(self);
eltc(1);
stuff->finished->signal();
}
void testJoining()
{
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.
testOk(finished.wait(10.0), "Join tests completed");
}
} // namespace
typedef struct info {
int isOkToBlock;
int didSomething;
} info;
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 = EPICS_THREAD_OPTS_INIT;
opts.priority = 50;
opts.joinable = 1;
info infoA = {0, 0};
epicsThreadId threadA = epicsThreadCreateOpt("threadA", thread, &infoA, &opts);
info infoB = {1, 0};
epicsThreadId threadB = epicsThreadCreateOpt("threadB", thread, &infoB, &opts);
// join B first to better our chance of detecting if it never runs.
epicsThreadMustJoin(threadB);
testOk1(infoB.didSomething);
epicsThreadMustJoin(threadA);
testOk1(infoA.didSomething);
}
MAIN(epicsThreadTest)
{
testPlan(13);
unsigned int ncpus = epicsThreadGetCPUs();
testDiag("System has %u CPUs", ncpus);
testOk1(ncpus > 0);
testDiag("main() thread %p", epicsThreadGetIdSelf());
testJoining(); // Do this first, ~epicsThread() uses it...
testMyThread();
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();
}