=== 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 <stdio.h>
 #include <epicsThread.h>
@@ -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 <stdio.h>
+#include <string.h>
+
+#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();
+}

