EPICS Home

Experimental Physics and Industrial Control System


 
2002  2003  2004  2005  2006  2007  2008  2009  2010  <20112012  2013  2014  2015  2016  2017  2018  2019  2020  Index 2002  2003  2004  2005  2006  2007  2008  2009  2010  <20112012  2013  2014  2015  2016  2017  2018  2019  2020 
<== Date ==> <== Thread ==>

Subject: [Merge] lp:~johill-lanl/epics-base/epicsThreadOnce-atomics-based into lp:epics-base
From: Jeff Hill <johill@lanl.gov>
To: mp+73606@code.launchpad.net
Date: Wed, 31 Aug 2011 22:31:29 -0000
Jeff Hill has proposed merging lp:~johill-lanl/epics-base/epicsThreadOnce-atomics-based into lp:epics-base with lp:~epics-core/epics-base/epicsR3.15-atomics as a prerequisite.

Requested reviews:
  EPICS Core Developers (epics-core)

For more details, see:
https://code.launchpad.net/~johill-lanl/epics-base/epicsThreadOnce-atomics-based/+merge/73606

o added new epicsAtomics based, and OS independent, implementation of epicsThreadOnce in epicsThreadOnce.cpp
o changed type of epicsThreadOnceId from epicsThreadId to void *
o fixed once flag isnt initialized with EPICSTHREAD_ONCE_INIT in ipAddrToAsciiAsynchronous.cpp
o removed OS dependent implementations of epicsThreadOnce from {posix, RTEMS, vxWorks, win32}

-- 
https://code.launchpad.net/~johill-lanl/epics-base/epicsThreadOnce-atomics-based/+merge/73606
Your team EPICS Core Developers is requested to review the proposed merge of lp:~johill-lanl/epics-base/epicsThreadOnce-atomics-based into lp:epics-base.
=== modified file 'src/libCom/Makefile'
--- src/libCom/Makefile	2011-08-31 22:31:20 +0000
+++ src/libCom/Makefile	2011-08-31 22:31:20 +0000
@@ -256,6 +256,7 @@
 SRCS += osdEnv.c
 SRCS += epicsReadline.c
 SRCS += epicsTempFile.cpp
+SRCS += epicsThreadOnce.cpp
 SRCS += epicsStdio.c
 SRCS += osdStdio.c
 

=== modified file 'src/libCom/misc/ipAddrToAsciiAsynchronous.cpp'
--- src/libCom/misc/ipAddrToAsciiAsynchronous.cpp	2008-10-21 20:26:48 +0000
+++ src/libCom/misc/ipAddrToAsciiAsynchronous.cpp	2011-08-31 22:31:20 +0000
@@ -127,7 +127,7 @@
 ipAddrToAsciiEnginePrivate * ipAddrToAsciiEnginePrivate :: pEngine = 0;
 unsigned ipAddrToAsciiEnginePrivate :: numberOfReferences = 0u;
 bool ipAddrToAsciiEnginePrivate :: shutdownRequest = false;
-static epicsThreadOnceId ipAddrToAsciiEngineGlobalMutexOnceFlag = 0;
+static epicsThreadOnceId ipAddrToAsciiEngineGlobalMutexOnceFlag = EPICS_THREAD_ONCE_INIT;
 
 // the users are not required to supply a show routine
 // for there transaction callback class

=== modified file 'src/libCom/osi/epicsThread.h'
--- src/libCom/osi/epicsThread.h	2010-04-26 20:38:11 +0000
+++ src/libCom/osi/epicsThread.h	2011-08-31 22:31:20 +0000
@@ -51,7 +51,7 @@
 /* (epicsThreadId)0 is guaranteed to be an invalid thread id */
 typedef struct epicsThreadOSD *epicsThreadId;
 
-typedef epicsThreadId epicsThreadOnceId;
+typedef void * epicsThreadOnceId;
 #define EPICS_THREAD_ONCE_INIT 0
 
 epicsShareFunc void epicsShareAPI epicsThreadOnce(

=== added file 'src/libCom/osi/epicsThreadOnce.cpp'
--- src/libCom/osi/epicsThreadOnce.cpp	1970-01-01 00:00:00 +0000
+++ src/libCom/osi/epicsThreadOnce.cpp	2011-08-31 22:31:20 +0000
@@ -0,0 +1,129 @@
+
+/*************************************************************************\
+* Copyright (c) 2011 LANS LLC, as Operator of
+*     Los Alamos National Laboratory.
+* Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne
+*     National Laboratory.
+* Copyright (c) 2002 The Regents of the University of California, as
+*     Operator of Los Alamos National Laboratory.
+* EPICS BASE is distributed subject to a Software License Agreement found
+* in file LICENSE that is included with this distribution. 
+\*************************************************************************/
+
+/*
+ *  Author Jeffrey O. Hill johill@lanl.gov
+ *  Original Authors (before converting to operating system independent code): 
+ *                      Jeff Hill, Andrew Johnson, Marty Kraimer, Eric Norum
+ */
+
+#include <cstdlib>
+
+#define epicsExportSharedSymbols
+#include "errlog.h"
+#include "cantProceed.h"
+#include "epicsThread.h"
+#include "epicsAtomic.h"
+#include "epicsAssert.h"
+
+namespace {
+    
+struct ThreadID {
+public:
+    ThreadID ();
+    ThreadID ( epicsThreadId id );
+    bool operator == ( const ThreadID & ) const;
+private:
+    epicsThreadId m_threadId;
+    ThreadID ( const ThreadID & ); // disabled
+    ThreadID & operator = ( const ThreadID & ); // disabled
+};
+
+inline ThreadID :: ThreadID ( epicsThreadId id ) : 
+    m_threadId ( id ) 
+{
+}
+
+inline ThreadID :: ThreadID () : m_threadId ( 0 ) 
+{
+}
+
+inline bool ThreadID :: operator == ( const ThreadID & ctrl ) const
+{
+    return ctrl.m_threadId == m_threadId;
+}
+
+static ThreadID done;
+
+} // end of namespace annonymous
+
+using namespace epics;
+using namespace atomic;
+
+/*
+ * epicsThreadOnceOnly ()
+ */
+static void epicsShareAPI epicsThreadOnceOnly ( epicsThreadOnceId * pId, 
+                                                    void ( * pFunc )( void * ), 
+                                                    void * pArg )
+{
+    static const epicsThreadOnceId idStartup = EPICS_THREAD_ONCE_INIT;
+    ThreadID self ( epicsThreadGetIdSelf () );
+    epicsThreadOnceId pCurrent = compareAndSwap ( *pId, idStartup, & self );
+    if ( pCurrent != & done ) {
+        if ( pCurrent == idStartup ) {
+            ( *pFunc ) ( pArg );
+            set ( *pId, & done );
+        }
+        else {
+            {
+                ThreadID & initID = 
+                        * reinterpret_cast < ThreadID * > ( pCurrent );
+                if ( initID == self ) {
+                    cantProceed( "epicsThreadOnce() once was called "
+                                "recursively from the user's once fuction\n" );
+                }
+            }
+            static const std :: size_t spinDownInit = 1000u;
+            static const std :: size_t spinCount = 10u;
+            STATIC_ASSERT ( spinDownInit > spinCount );
+            static const std :: size_t spinThresh = spinDownInit - spinCount;
+            std :: size_t spinDown = spinDownInit;
+            do {
+                if ( spinDown <= spinThresh ) {
+                    epicsThreadSleep ( epicsThreadSleepQuantum () );
+                }
+                if ( spinDown > 0u ) {
+                    spinDown--;
+                }
+                else {
+                    errlogPrintf ( "epicsThreadOnce: waiting for another "
+                            "thread to finish calling the once function\n" );
+                    spinDown = spinThresh;
+                }
+                pCurrent = get ( *pId );
+            } while ( pCurrent != & done );
+        }
+    }
+}
+
+//
+// we implement this as a separate function so that it is easy to see
+// that the performance impact of the primary (most commonly used) path  
+// and that options for inlining this function are available (based on 
+// source code organization)
+//
+// performance might be slightly better if this were implemented as an inline
+// function, but that would pull epicsAtomic.h into epicsThread.h, and on
+// windows this includes (the lean and mean version of) windows.h, so perhaps 
+// its best to make this an out-of-line function as that type of instantiation 
+// will probably have only a small negative impact on performance
+//
+extern "C" void epicsShareAPI epicsThreadOnce ( epicsThreadOnceId * pId, 
+                                                void ( * pFunc )( void * ), 
+                                                void * pArg )
+{
+    const epicsThreadOnceId pCurrent = get ( *pId );
+    if ( pCurrent != & done ) {
+        epicsThreadOnceOnly ( pId, pFunc, pArg );
+    }
+}

=== modified file 'src/libCom/osi/os/RTEMS/osdThread.c'
--- src/libCom/osi/os/RTEMS/osdThread.c	2010-10-05 19:27:37 +0000
+++ src/libCom/osi/os/RTEMS/osdThread.c	2011-08-31 22:31:20 +0000
@@ -60,7 +60,6 @@
  * Support for `once-only' execution
  */
 static int initialized = 0;
-static epicsMutexId onceMutex;
 
 /*
  * Just map osi 0 to 99 into RTEMS 199 to 100
@@ -231,7 +230,6 @@
         rtems_task_priority old;
 
         rtems_task_set_priority (RTEMS_SELF, epicsThreadGetOssPriorityValue(99), &old);
-        onceMutex = epicsMutexMustCreate();
         taskVarMutex = epicsMutexMustCreate ();
         rtems_task_ident (RTEMS_SELF, 0, &tid);
         setThreadInfo (tid, "_main_", NULL, NULL);
@@ -471,36 +469,6 @@
 }
 
 /*
- * Ensure func() is run only once.
- */
-void epicsThreadOnce(epicsThreadOnceId *id, void(*func)(void *), void *arg)
-{
-    #define EPICS_THREAD_ONCE_DONE (epicsThreadId) 1
-
-    if (!initialized) epicsThreadInit();
-    epicsMutexMustLock(onceMutex);
-    if (*id != EPICS_THREAD_ONCE_DONE) {
-        if (*id == EPICS_THREAD_ONCE_INIT) { /* first call */
-            *id = epicsThreadGetIdSelf();    /* mark active */
-            epicsMutexUnlock(onceMutex);
-            func(arg);
-            epicsMutexMustLock(onceMutex);
-            *id = EPICS_THREAD_ONCE_DONE;    /* mark done */
-        } else if (*id == epicsThreadGetIdSelf()) {
-            epicsMutexUnlock(onceMutex);
-            cantProceed("Recursive epicsThreadOnce() initialization\n");
-        } else
-            while (*id != EPICS_THREAD_ONCE_DONE) {
-                /* Another thread is in the above func(arg) call. */
-                epicsMutexUnlock(onceMutex);
-                epicsThreadSleep(epicsThreadSleepQuantum());
-                epicsMutexMustLock(onceMutex);
-            }
-    }
-    epicsMutexUnlock(onceMutex);
-}
-
-/*
  * Thread private storage implementation based on the vxWorks
  * implementation by Andrew Johnson APS/ASD.
  */

=== modified file 'src/libCom/osi/os/WIN32/osdThread.c'
--- src/libCom/osi/os/WIN32/osdThread.c	2011-02-11 22:33:58 +0000
+++ src/libCom/osi/os/WIN32/osdThread.c	2011-08-31 22:31:20 +0000
@@ -1008,41 +1008,6 @@
 }
 
 /*
- * epicsThreadOnce ()
- */
-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 != 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 );
-}
-
-/*
  * epicsThreadPrivateCreate ()
  */
 epicsShareFunc epicsThreadPrivateId epicsShareAPI epicsThreadPrivateCreate ()

=== modified file 'src/libCom/osi/os/posix/osdThread.c'
--- src/libCom/osi/os/posix/osdThread.c	2010-05-14 22:26:54 +0000
+++ src/libCom/osi/os/posix/osdThread.c	2011-08-31 22:31:20 +0000
@@ -76,7 +76,6 @@
 } epicsThreadOSD;
 
 static pthread_key_t getpthreadInfo;
-static pthread_mutex_t onceLock;
 static pthread_mutex_t listLock;
 static ELLLIST pthreadList = ELLLIST_INIT;
 static commonAttr *pcommonAttr = 0;
@@ -207,8 +206,6 @@
     int status;
 
     pthread_key_create(&getpthreadInfo,0);
-    status = pthread_mutex_init(&onceLock,0);
-    checkStatusQuit(status,"pthread_mutex_init","epicsThreadInit");
     status = pthread_mutex_init(&listLock,0);
     checkStatusQuit(status,"pthread_mutex_init","epicsThreadInit");
     pcommonAttr = calloc(1,sizeof(commonAttr));
@@ -321,47 +318,6 @@
 #endif /*_POSIX_THREAD_ATTR_STACKSIZE*/
 }
 
-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,"epicsThreadOnce: pthread_mutex_lock returned %s.\n",
-            strerror(status));
-        exit(-1);
-    }
-
-    if (*id != EPICS_THREAD_ONCE_DONE) {
-        if (*id == EPICS_THREAD_ONCE_INIT) { /* first call */
-            *id = epicsThreadGetIdSelf();    /* mark active */
-            status = pthread_mutex_unlock(&onceLock);
-            checkStatusQuit(status,"pthread_mutex_unlock", "epicsThreadOnce");
-            func(arg);
-            status = mutexLock(&onceLock);
-            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 != EPICS_THREAD_ONCE_DONE) {
-                /* Another thread is in the above func(arg) call. */
-                status = pthread_mutex_unlock(&onceLock);
-                checkStatusQuit(status,"pthread_mutex_unlock", "epicsThreadOnce");
-                epicsThreadSleep(epicsThreadSleepQuantum());
-                status = mutexLock(&onceLock);
-                checkStatusQuit(status,"pthread_mutex_lock", "epicsThreadOnce");
-            }
-    }
-    status = pthread_mutex_unlock(&onceLock);
-    checkStatusQuit(status,"pthread_mutex_unlock","epicsThreadOnce");
-}
-
 epicsShareFunc epicsThreadId epicsShareAPI epicsThreadCreate(const char *name,
     unsigned int priority, unsigned int stackSize,
     EPICSTHREADFUNC funptr,void *parm)

=== modified file 'src/libCom/osi/os/vxWorks/osdThread.c'
--- src/libCom/osi/os/vxWorks/osdThread.c	2010-08-11 15:45:17 +0000
+++ src/libCom/osi/os/vxWorks/osdThread.c	2011-08-31 22:31:20 +0000
@@ -53,7 +53,6 @@
 static void **papTSD = 0;
 static int nepicsThreadPrivate = 0;
 
-static SEM_ID epicsThreadOnceMutex = 0;
 
 /* Just map osi 0 to 99 into vx 100 to 199 */
 /* remember that for vxWorks lower number means higher priority */
@@ -83,19 +82,6 @@
     }
 }
 
-static void epicsThreadInit(void)
-{
-    static int lock = 0;
-
-    while(!vxTas(&lock)) taskDelay(1);
-    if(epicsThreadOnceMutex==0) {
-        epicsThreadOnceMutex = semMCreate(
-                SEM_DELETE_SAFE|SEM_INVERSION_SAFE|SEM_Q_PRIORITY);
-        assert(epicsThreadOnceMutex);
-    }
-    lock = 0;
-}
-
 unsigned int epicsThreadGetStackSize (epicsThreadStackSizeClass stackSizeClass) 
 {
 
@@ -112,43 +98,6 @@
     return stackSizeTable[stackSizeClass];
 }
 
-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 != EPICS_THREAD_ONCE_DONE) {
-        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 = EPICS_THREAD_ONCE_DONE;    /* mark done */
-        } else if (*id == epicsThreadGetIdSelf()) {
-            semGive(epicsThreadOnceMutex);
-            cantProceed("Recursive epicsThreadOnce() initialization\n");
-        } else
-            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);
-}
 
 static void createFunction(EPICSTHREADFUNC func, void *parm)
 {
@@ -173,7 +122,6 @@
     EPICSTHREADFUNC funptr,void *parm)
 {
     int tid;
-    if(epicsThreadOnceMutex==0) epicsThreadInit();
     if(stackSize<100) {
         errlogPrintf("epicsThreadCreate %s illegal stackSize %d\n",name,stackSize);
         return(0);
@@ -343,7 +291,6 @@
     static int lock = 0;
     epicsThreadPrivateId id;
 
-    epicsThreadInit();
     /*lock is necessary because ++nepicsThreadPrivate may not be indivisible*/
     while(!vxTas(&lock)) taskDelay(1);
     id = (epicsThreadPrivateId)++nepicsThreadPrivate;


Navigate by Date:
Prev: [Merge] lp:~johill-lanl/epics-base/epicsThreadOnce-atomics-based into lp:epics-base Jeff Hill
Next: Re: [Merge] lp:~epics-core/epics-base/3.15-buildCompilerSpecific into lp:epics-base Jeff Hill
Index: 2002  2003  2004  2005  2006  2007  2008  2009  2010  <20112012  2013  2014  2015  2016  2017  2018  2019  2020 
Navigate by Thread:
Prev: [Merge] lp:~johill-lanl/epics-base/epicsThreadOnce-atomics-based into lp:epics-base Jeff Hill
Next: RE: Atomic operation library and spin-lock for the epics ring buffer Jeff Hill
Index: 2002  2003  2004  2005  2006  2007  2008  2009  2010  <20112012  2013  2014  2015  2016  2017  2018  2019  2020