Subject: |
[Merge] lp:~johill-lanl/epics-base/epicsThreadOnce-atomics-based into lp:epics-base |
From: |
Jeff Hill <[email protected]> |
To: |
[email protected] |
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 [email protected]
+ * 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
<2011>
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
- 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
<2011>
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
|