=== modified file 'src/libCom/error/errlog.c' --- src/libCom/error/errlog.c 2009-06-19 18:24:22 +0000 +++ src/libCom/error/errlog.c 2010-05-17 19:11:43 +0000 @@ -445,7 +445,7 @@ static epicsThreadOnceId errlogOnceFlag = EPICS_THREAD_ONCE_INIT; struct initArgs config; - if (errlogOnceFlag > 0 && pvtData.atExit) + if (pvtData.atExit) return 0; if (bufsize < BUFFER_SIZE) bufsize = BUFFER_SIZE; === modified file 'src/libCom/osi/epicsThread.h' --- src/libCom/osi/epicsThread.h 2009-08-14 16:54:45 +0000 +++ src/libCom/osi/epicsThread.h 2010-05-17 19:11:43 +0000 @@ -48,23 +48,17 @@ epicsShareFunc unsigned int epicsShareAPI epicsThreadGetStackSize( epicsThreadStackSizeClass size); -typedef int epicsThreadOnceId; +/* (epicsThreadId)0 is guaranteed to be an invalid thread id */ +typedef struct epicsThreadOSD *epicsThreadId; + +typedef epicsThreadId 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( +epicsShareFunc void epicsShareAPI epicsThreadOnce( 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; - epicsShareFunc epicsThreadId epicsShareAPI epicsThreadCreate ( const char * name, unsigned int priority, unsigned int stackSize, EPICSTHREADFUNC funptr,void * parm ); === modified file 'src/libCom/osi/os/RTEMS/osdThread.c' --- src/libCom/osi/os/RTEMS/osdThread.c 2009-07-23 21:04:27 +0000 +++ src/libCom/osi/os/RTEMS/osdThread.c 2010-05-17 19:11:43 +0000 @@ -473,18 +473,29 @@ /* * 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); } === modified file 'src/libCom/osi/os/WIN32/osdThread.c' --- src/libCom/osi/os/WIN32/osdThread.c 2008-09-30 16:31:09 +0000 +++ src/libCom/osi/os/WIN32/osdThread.c 2010-05-17 20:26:21 +0000 @@ -48,7 +48,7 @@ DWORD tlsIndexThreadLibraryEPICS; } win32ThreadGlobal; -typedef struct win32ThreadParam { +typedef struct epicsThreadOSD { ELLNODE node; HANDLE handle; EPICSTHREADFUNC funptr; @@ -373,10 +373,10 @@ } if ( magnitude >= stateCount ) { - errlogPrintf ( + fprintf ( stderr, "Unrecognized WIN32 thread priority level %d.\n", osdPriority ); - errlogPrintf ( + fprintf ( stderr, "Mapping to EPICS thread priority level epicsThreadPriorityMin.\n" ); return epicsThreadPriorityMin; } @@ -997,24 +997,35 @@ /* * 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; - LeaveCriticalSection ( & pGbl->mutex ); - ( *func ) ( arg ); - EnterCriticalSection ( & pGbl->mutex ); - *id = 1; - } else - assert(*id > 0 /* func() called epicsThreadOnce() with same id */); - + if ( *id != EPICS_THREAD_ONCE_DONE ) { + if ( *id == EPICS_THREAD_ONCE_INIT ) { /* first call */ + *id = epicsThreadGetIdSelf(); /* mark active */ + LeaveCriticalSection ( & pGbl->mutex ); + func ( arg ); + EnterCriticalSection ( & pGbl->mutex ); + *id = EPICS_THREAD_ONCE_DONE; /* mark done */ + } else if ( *id == epicsThreadGetIdSelf() ) { + LeaveCriticalSection ( & pGbl->mutex ); + cantProceed( "Recursive epicsThreadOnce() initialization\n" ); + } else + 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 ); } === modified file 'src/libCom/osi/os/posix/osdThread.c' --- src/libCom/osi/os/posix/osdThread.c 2009-07-09 18:37:26 +0000 +++ src/libCom/osi/os/posix/osdThread.c 2010-05-17 19:11:43 +0000 @@ -321,40 +321,43 @@ #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, === modified file 'src/libCom/osi/os/vxWorks/osdPoolStatus.c' --- src/libCom/osi/os/vxWorks/osdPoolStatus.c 2002-07-12 21:35:43 +0000 +++ src/libCom/osi/os/vxWorks/osdPoolStatus.c 2010-05-17 19:11:43 +0000 @@ -3,8 +3,7 @@ * 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 +* EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ @@ -16,7 +15,7 @@ #include "osiPoolStatus.h" /* - * It turns out that memPartInfoGet() nad memFindMax() are very CPU intensive on vxWorks + * It turns out that memPartInfoGet() and memFindMax() are very CPU intensive on vxWorks * so we must spawn off a thread that periodically polls. Although this isnt 100% safe, I * dont see what else to do. * @@ -32,19 +31,15 @@ static void osdSufficentSpaceInPoolQuery () { int temp = memFindMax (); - if ( temp > 0 ) { - osdMaxBlockSize = (size_t) temp; - } - else { - osdMaxBlockSize = 0; - } + + osdMaxBlockSize = ( temp > 0 ) ? (size_t) temp : 0; } static void osdSufficentSpaceInPoolPoll ( void *pArgIn ) { while ( 1 ) { + epicsThreadSleep ( 1.0 ); osdSufficentSpaceInPoolQuery (); - epicsThreadSleep ( 1.0 ); } } @@ -54,14 +49,9 @@ osdSufficentSpaceInPoolQuery (); - id = epicsShareAPI epicsThreadCreate ( "poolPoll", epicsThreadPriorityMedium, - epicsThreadGetStackSize ( epicsThreadStackSmall ), osdSufficentSpaceInPoolPoll, 0 ); - if ( id ) { - osdMaxBlockOnceler = 1; - } - else { - epicsThreadSleep ( 0.1 ); - } + id = epicsThreadCreate ( "poolPoll", epicsThreadPriorityMedium, + epicsThreadGetStackSize ( epicsThreadStackSmall ), + osdSufficentSpaceInPoolPoll, 0 ); } /* === modified file 'src/libCom/osi/os/vxWorks/osdThread.c' --- src/libCom/osi/os/vxWorks/osdThread.c 2009-01-06 17:07:56 +0000 +++ src/libCom/osi/os/vxWorks/osdThread.c 2010-05-17 19:11:43 +0000 @@ -110,21 +110,39 @@ 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 @@ { 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 === modified file 'src/libCom/test/Makefile' --- src/libCom/test/Makefile 2009-07-23 17:25:51 +0000 +++ src/libCom/test/Makefile 2010-05-17 19:11:43 +0000 @@ -57,6 +57,11 @@ testHarness_SRCS += epicsThreadTest.cpp TESTS += epicsThreadTest +TESTPROD_HOST += epicsThreadOnceTest +epicsThreadOnceTest_SRCS += epicsThreadOnceTest.c +testHarness_SRCS += epicsThreadOnceTest.c +TESTS += epicsThreadOnceTest + TESTPROD_HOST += epicsThreadPriorityTest epicsThreadPriorityTest_SRCS += epicsThreadPriorityTest.cpp testHarness_SRCS += epicsThreadPriorityTest.cpp === modified file 'src/libCom/test/epicsRunLibComTests.c' --- src/libCom/test/epicsRunLibComTests.c 2009-07-29 14:29:54 +0000 +++ src/libCom/test/epicsRunLibComTests.c 2010-05-17 19:11:43 +0000 @@ -7,9 +7,6 @@ /* * Run libCom tests as a batch - * - * This is part of the work being done to provide a unified set of automated - * tests for EPICS. Many more changes will be forthcoming. */ #include #include @@ -27,6 +24,7 @@ int epicsMutexTest(void); int epicsStdioTest(void); int epicsStringTest(void); +int epicsThreadOnceTest(void); int epicsThreadPriorityTest(void); int epicsThreadPrivateTest(void); int epicsTimeTest(void); @@ -73,6 +71,8 @@ runTest(epicsStringTest); + runTest(epicsThreadOnceTest); + runTest(epicsThreadPriorityTest); runTest(epicsThreadPrivateTest); === added file 'src/libCom/test/epicsThreadOnceTest.c' --- src/libCom/test/epicsThreadOnceTest.c 1970-01-01 00:00:00 +0000 +++ src/libCom/test/epicsThreadOnceTest.c 2010-05-17 20:35:19 +0000 @@ -0,0 +1,110 @@ +/*************************************************************************\ +* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* $Id$ */ + +#include +#include + +#include "epicsEvent.h" +#include "epicsExit.h" +#include "epicsMutex.h" +#include "epicsThread.h" +#include "epicsUnitTest.h" +#include "testMain.h" + +#define NUM_ONCE_THREADS 8 + +epicsThreadOnceId onceFlag = EPICS_THREAD_ONCE_INIT; +epicsThreadOnceId twiceFlag = EPICS_THREAD_ONCE_INIT; +epicsMutexId lock; +epicsEventId go; + +int runCount = 0; +int initCount = 0; +char initBy[20]; +int doneCount = 0; + +void onceInit(void *ctx) +{ + initCount++; + strcpy(initBy, epicsThreadGetNameSelf()); +} + +void onceThread(void *ctx) +{ + epicsMutexMustLock(lock); + runCount++; + epicsMutexUnlock(lock); + + epicsEventMustWait(go); + epicsEventSignal(go); + + epicsThreadOnce(&onceFlag, onceInit, ctx); + testOk(initCount == 1, "%s: initCount = %d", + epicsThreadGetNameSelf(), initCount); + + epicsMutexMustLock(lock); + doneCount++; + epicsMutexUnlock(lock); +} + + +void recurseInit(void); +void onceRecurse(void *ctx) +{ + recurseInit(); +} + +void recurseInit(void) +{ + epicsThreadOnce(&twiceFlag, onceRecurse, 0); +} + +void recurseThread(void *ctx) +{ + recurseInit(); + testFail("Recursive epicsThreadOnce() not detected"); +} + + +MAIN(epicsThreadOnceTest) +{ + int i; + epicsThreadId tid; + + testPlan(3 + NUM_ONCE_THREADS); + + go = epicsEventMustCreate(epicsEventEmpty); + lock = epicsMutexMustCreate(); + + for (i = 0; i < NUM_ONCE_THREADS; i++) { + char name[20]; + + sprintf(name, "once-%d", i); + epicsThreadCreate(name, epicsThreadPriorityMedium, + epicsThreadGetStackSize(epicsThreadStackSmall), + onceThread, 0); + } + epicsThreadSleep(0.1); + + testOk(runCount == NUM_ONCE_THREADS, "runCount = %d", runCount); + epicsEventSignal(go); + epicsThreadSleep(0.1); + + testOk(doneCount == NUM_ONCE_THREADS, "doneCount = %d", doneCount); + testDiag("init was run by %s", initBy); + + tid = epicsThreadCreate("recurse", epicsThreadPriorityMedium, + epicsThreadGetStackSize(epicsThreadStackSmall), + recurseThread, 0); + do { + epicsThreadSleep(0.1); + } while (!epicsThreadIsSuspended(tid)); + testPass("Recursive epicsThreadOnce() detected"); + + return testDone(); +}