From c5a27fa32e9cccdd68900f633a279cce6fb0be82 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Mon, 26 Apr 2010 15:38:11 -0500 Subject: [PATCH] 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. --- src/libCom/osi/epicsThread.h | 22 ++++++--------- src/libCom/osi/os/RTEMS/osdThread.c | 21 +++++++++++---- src/libCom/osi/os/WIN32/osdThread.c | 23 +++++++++++----- src/libCom/osi/os/posix/osdThread.c | 39 ++++++++++++++------------- src/libCom/osi/os/vxWorks/osdThread.c | 32 +++++++++++++++++----- 5 files changed, 86 insertions(+), 51 deletions(-) diff --git a/src/libCom/osi/epicsThread.h b/src/libCom/osi/epicsThread.h index d86583c6d..fa0a6bd70 100644 --- a/src/libCom/osi/epicsThread.h +++ b/src/libCom/osi/epicsThread.h @@ -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 ); diff --git a/src/libCom/osi/os/RTEMS/osdThread.c b/src/libCom/osi/os/RTEMS/osdThread.c index 29feacb99..a5cf629d4 100644 --- a/src/libCom/osi/os/RTEMS/osdThread.c +++ b/src/libCom/osi/os/RTEMS/osdThread.c @@ -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); } diff --git a/src/libCom/osi/os/WIN32/osdThread.c b/src/libCom/osi/os/WIN32/osdThread.c index 1d9cdd4b1..50002b4e9 100644 --- a/src/libCom/osi/os/WIN32/osdThread.c +++ b/src/libCom/osi/os/WIN32/osdThread.c @@ -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 ); } diff --git a/src/libCom/osi/os/posix/osdThread.c b/src/libCom/osi/os/posix/osdThread.c index 5d101f9a1..073bdb626 100644 --- a/src/libCom/osi/os/posix/osdThread.c +++ b/src/libCom/osi/os/posix/osdThread.c @@ -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, diff --git a/src/libCom/osi/os/vxWorks/osdThread.c b/src/libCom/osi/os/vxWorks/osdThread.c index 4bda3cb52..9542778e2 100644 --- a/src/libCom/osi/os/vxWorks/osdThread.c +++ b/src/libCom/osi/os/vxWorks/osdThread.c @@ -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