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:
@@ -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 );
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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 );
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user