Rework epicsThreadOnce() using ideas from Michael Davidsaver.

An epicsThreadOnceId is now an epicsThreadId.
During initialization, it is set to the thread running the init routine
which can now detect a recursive initialization attempt and suspend.
EPICS_THREAD_ONCE_INIT is still zero, the implementations now define a
new private value for EPICS_THREAD_ONCE_DONE.  This is deliberately not
made public.
This commit is contained in:
Andrew Johnson
2010-04-26 15:38:11 -05:00
parent 4c70951869
commit c5a27fa32e
5 changed files with 86 additions and 51 deletions

View File

@@ -48,23 +48,17 @@ typedef enum {
epicsShareFunc unsigned int epicsShareAPI epicsThreadGetStackSize(
epicsThreadStackSizeClass size);
typedef int epicsThreadOnceId;
#define EPICS_THREAD_ONCE_INIT 0
/* void epicsThreadOnce(epicsThreadOnceId *id, EPICSTHREADFUNC, void *arg); */
/* epicsThreadOnce is implemented as a macro */
/* epicsThreadOnceOsd should not be called by user code */
epicsShareFunc void epicsShareAPI epicsThreadOnceOsd(
epicsThreadOnceId *id, EPICSTHREADFUNC, void *arg);
#define epicsThreadOnce(id,func,arg) \
epicsThreadOnceOsd((id),(func),(arg))
epicsShareFunc void epicsShareAPI epicsThreadExitMain(void);
/* (epicsThreadId)0 is guaranteed to be an invalid thread id */
typedef struct epicsThreadOSD *epicsThreadId;
typedef epicsThreadId epicsThreadOnceId;
#define EPICS_THREAD_ONCE_INIT 0
epicsShareFunc void epicsShareAPI epicsThreadOnce(
epicsThreadOnceId *id, EPICSTHREADFUNC, void *arg);
epicsShareFunc void epicsShareAPI epicsThreadExitMain(void);
epicsShareFunc epicsThreadId epicsShareAPI epicsThreadCreate (
const char * name, unsigned int priority, unsigned int stackSize,
EPICSTHREADFUNC funptr,void * parm );

View File

@@ -473,18 +473,29 @@ epicsThreadId epicsThreadGetId (const char *name)
/*
* Ensure func() is run only once.
*/
void epicsThreadOnceOsd(epicsThreadOnceId *id, void(*func)(void *), void *arg)
void epicsThreadOnce(epicsThreadOnceId *id, void(*func)(void *), void *arg)
{
static struct epicsThreadOSD threadOnceComplete;
#define EPICS_THREAD_ONCE_DONE &threadOnceComplete
if (!initialized) epicsThreadInit();
epicsMutexMustLock(onceMutex);
if (*id == 0) {
*id = -1;
if (*id == EPICS_THREAD_ONCE_INIT) { /* first call */
*id = epicsThreadGetIdSelf(); /* mark active */
epicsMutexUnlock(onceMutex);
func(arg);
epicsMutexMustLock(onceMutex);
*id = 1;
*id = EPICS_THREAD_ONCE_DONE; /* mark done */
} else if (*id == epicsThreadGetIdSelf()) {
epicsMutexUnlock(onceMutex);
cantProceed("Recursive epicsThreadOnce() initialization\n");
} else
assert(*id > 0 /* func() called epicsThreadOnce() with same id */);
while (*id != EPICS_THREAD_ONCE_DONE) {
/* Another thread is in the above func(arg) call. */
epicsMutexUnlock(onceMutex);
epicsThreadSleep(epicsThreadSleepQuantum());
epicsMutexMustLock(onceMutex);
}
epicsMutexUnlock(onceMutex);
}

View File

@@ -997,24 +997,33 @@ epicsShareFunc void epicsShareAPI epicsThreadShow ( epicsThreadId id, unsigned l
/*
* epicsThreadOnce ()
*/
epicsShareFunc void epicsShareAPI epicsThreadOnceOsd (
epicsShareFunc void epicsShareAPI epicsThreadOnce (
epicsThreadOnceId *id, void (*func)(void *), void *arg )
{
static struct epicsThreadOSD threadOnceComplete;
#define EPICS_THREAD_ONCE_DONE & threadOnceComplete
win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal ();
assert ( pGbl );
EnterCriticalSection ( & pGbl->mutex );
if ( *id == 0 ) {
*id = -1;
if ( *id == EPICS_THREAD_ONCE_INIT ) { /* first call */
*id = epicsThreadGetIdSelf(); /* mark active */
LeaveCriticalSection ( & pGbl->mutex );
( *func ) ( arg );
func ( arg );
EnterCriticalSection ( & pGbl->mutex );
*id = 1;
*id = EPICS_THREAD_ONCE_DONE; /* mark done */
} else if ( *id == epicsThreadGetIdSelf() ) {
LeaveCriticalSection ( & pGbl->mutex );
cantProceed( "Recursive epicsThreadOnce() initialization\n" );
} else
assert(*id > 0 /* func() called epicsThreadOnce() with same id */);
while ( *id != EPICS_THREAD_ONCE_DONE ) {
/* Another thread is in the above func(arg) call. */
LeaveCriticalSection ( & pGbl->mutex );
epicsThreadSleep ( epicsThreadSleepQuantum() );
EnterCriticalSection ( & pGbl->mutex );
}
LeaveCriticalSection ( & pGbl->mutex );
}

View File

@@ -321,40 +321,43 @@ epicsShareFunc unsigned int epicsShareAPI epicsThreadGetStackSize (epicsThreadSt
#endif /*_POSIX_THREAD_ATTR_STACKSIZE*/
}
/* epicsThreadOnce is a macro that calls epicsThreadOnceOsd */
epicsShareFunc void epicsShareAPI epicsThreadOnceOsd(epicsThreadOnceId *id, void (*func)(void *), void *arg)
epicsShareFunc void epicsShareAPI epicsThreadOnce(epicsThreadOnceId *id, void (*func)(void *), void *arg)
{
static struct epicsThreadOSD threadOnceComplete;
#define EPICS_THREAD_ONCE_DONE &threadOnceComplete
int status;
epicsThreadInit();
status = mutexLock(&onceLock);
if(status) {
fprintf(stderr,"epicsThreadOnceOsd: pthread_mutex_lock returned %s.\n",
fprintf(stderr,"epicsThreadOnce: pthread_mutex_lock returned %s.\n",
strerror(status));
exit(-1);
}
if (*id == 0) { /* 0 => first call */
*id = -1; /* -1 => func() active */
/* avoid recursive locking */
if (*id == EPICS_THREAD_ONCE_INIT) { /* first call */
*id = epicsThreadGetIdSelf(); /* mark active */
status = pthread_mutex_unlock(&onceLock);
checkStatusQuit(status,"pthread_mutex_unlock","epicsThreadOnceOsd");
checkStatusQuit(status,"pthread_mutex_unlock", "epicsThreadOnce");
func(arg);
status = mutexLock(&onceLock);
checkStatusQuit(status,"pthread_mutex_lock","epicsThreadOnceOsd");
*id = +1; /* +1 => func() done */
checkStatusQuit(status,"pthread_mutex_lock", "epicsThreadOnce");
*id = EPICS_THREAD_ONCE_DONE; /* mark done */
} else if (*id == epicsThreadGetIdSelf()) {
status = pthread_mutex_unlock(&onceLock);
checkStatusQuit(status,"pthread_mutex_unlock", "epicsThreadOnce");
cantProceed("Recursive epicsThreadOnce() initialization\n");
} else
while (*id < 0) {
/* Someone is in the above func(arg) call. If that someone is
* actually us, we're screwed, but the other OS implementations
* will fire an assert() that should detect this condition.
*/
while (*id != EPICS_THREAD_ONCE_DONE) {
/* Another thread is in the above func(arg) call. */
status = pthread_mutex_unlock(&onceLock);
checkStatusQuit(status,"pthread_mutex_unlock","epicsThreadOnceOsd");
epicsThreadSleep(0.01);
checkStatusQuit(status,"pthread_mutex_unlock", "epicsThreadOnce");
epicsThreadSleep(epicsThreadSleepQuantum());
status = mutexLock(&onceLock);
checkStatusQuit(status,"pthread_mutex_lock","epicsThreadOnceOsd");
checkStatusQuit(status,"pthread_mutex_lock", "epicsThreadOnce");
}
status = pthread_mutex_unlock(&onceLock);
checkStatusQuit(status,"pthread_mutex_unlock","epicsThreadOnceOsd");
checkStatusQuit(status,"pthread_mutex_unlock","epicsThreadOnce");
}
epicsShareFunc epicsThreadId epicsShareAPI epicsThreadCreate(const char *name,

View File

@@ -110,21 +110,39 @@ unsigned int epicsThreadGetStackSize (epicsThreadStackSizeClass stackSizeClass)
return stackSizeTable[stackSizeClass];
}
void epicsThreadOnceOsd(epicsThreadOnceId *id, void (*func)(void *), void *arg)
struct epicsThreadOSD {};
/* Strictly speaking this should be a WIND_TCB, but we only need it to
* be able to create an epicsThreadId that is guaranteed never to be
* the same as any current TID, and since TIDs are pointers this works.
*/
void epicsThreadOnce(epicsThreadOnceId *id, void (*func)(void *), void *arg)
{
static struct epicsThreadOSD threadOnceComplete;
#define EPICS_THREAD_ONCE_DONE &threadOnceComplete
int result;
epicsThreadInit();
result = semTake(epicsThreadOnceMutex, WAIT_FOREVER);
assert(result == OK);
if (*id == 0) { /* 0 => first call */
*id = -1; /* -1 => func() active */
if (*id == EPICS_THREAD_ONCE_INIT) { /* first call */
*id = epicsThreadGetIdSelf(); /* mark active */
semGive(epicsThreadOnceMutex);
func(arg);
result = semTake(epicsThreadOnceMutex, WAIT_FOREVER);
assert(result == OK);
*id = +1; /* +1 => func() done */
*id = EPICS_THREAD_ONCE_DONE; /* mark done */
} else if (*id == epicsThreadGetIdSelf()) {
semGive(epicsThreadOnceMutex);
cantProceed("Recursive epicsThreadOnce() initialization\n");
} else
assert(*id > 0 /* func() called epicsThreadOnce() with same id */);
while (*id != EPICS_THREAD_ONCE_DONE) {
/* Another thread is in the above func(arg) call. */
semGive(epicsThreadOnceMutex);
epicsThreadSleep(epicsThreadSleepQuantum());
result = semTake(epicsThreadOnceMutex, WAIT_FOREVER);
assert(result == OK);
}
semGive(epicsThreadOnceMutex);
}
@@ -132,13 +150,13 @@ static void createFunction(EPICSTHREADFUNC func, void *parm)
{
int tid = taskIdSelf();
taskVarAdd(tid,(int *)&papTSD);
taskVarAdd(tid,(int *)(char *)&papTSD);
/*Make sure that papTSD is still 0 after that call to taskVarAdd*/
papTSD = 0;
(*func)(parm);
epicsExitCallAtThreadExits ();
free(papTSD);
taskVarDelete(tid,(int *)&papTSD);
taskVarDelete(tid,(int *)(char *)&papTSD);
}
#ifdef ALTIVEC