From bd1b1479f466bf9de72113ab1eeb2b9959d8b006 Mon Sep 17 00:00:00 2001 From: Jeff Hill Date: Mon, 15 Aug 2011 17:00:01 -0600 Subject: [PATCH] o no longer need to define OSD_ATOMIC_GCC o removed function epicsAtomicTestAndSetUIntT o added new functions epicsAtomicSetPtrT epicsAtomicGetPtrT epicsAtomicCmpAndSwapUIntT epicsAtomicCmpAndSwapPtrT o changed msvc intrinsics to define memory fence o fixed mutex synchronized version so that its slow, but correct if the c++ compiler doesnt synchronized local scope static initialization o changed most of the set/get methods to use memory barriers instead of some other primitive o added additional tests --- src/libCom/osi/compiler/gcc/epicsAtomicCD.h | 37 +++++- src/libCom/osi/compiler/msvc/epicsAtomicCD.h | 112 ++++++++++------- src/libCom/osi/epicsAtomic.h | 32 +++-- src/libCom/osi/epicsAtomicLocked.cpp | 57 +++++++-- src/libCom/osi/epicsAtomicLocked.h | 42 +++++-- src/libCom/osi/os/WIN32/epicsAtomicOSD.h | 96 +++++++++------ src/libCom/osi/os/solaris/epicsAtomicOSD.h | 109 +++++++--------- src/libCom/osi/os/vxWorks/epicsAtomicOSD.h | 109 ++++++++++++++-- src/libCom/test/epicsAtomicPerform.cpp | 74 +++++------ src/libCom/test/epicsAtomicTest.c | 123 +++++++++++++------ 10 files changed, 525 insertions(+), 266 deletions(-) diff --git a/src/libCom/osi/compiler/gcc/epicsAtomicCD.h b/src/libCom/osi/compiler/gcc/epicsAtomicCD.h index faf1c7886..8b06bd7bb 100644 --- a/src/libCom/osi/compiler/gcc/epicsAtomicCD.h +++ b/src/libCom/osi/compiler/gcc/epicsAtomicCD.h @@ -39,16 +39,17 @@ ( defined ( __i486 ) || defined ( __pentium ) || \ defined ( __pentiumpro ) || defined ( __MMX__ ) ) +#define GCC_ATOMIC_INTRINSICS_GCC4_OR_BETTER \ + ( ( __GNUC__ * 100 + __GNUC_MINOR__ ) >= 401 ) + #define GCC_ATOMIC_INTRINSICS_AVAIL_EARLIER \ ( GCC_ATOMIC_INTRINSICS_MIN_X86 && \ - ( ( __GNUC__ * 100 + __GNUC_MINOR__ ) >= 401 ) ) + GCC_ATOMIC_INTRINSICS_GCC4_OR_BETTER ) #if ( GCC_ATOMIC_INTRINSICS_AVAIL_UINT_T \ && GCC_ATOMIC_INTRINSICS_AVAIL_SIZE_T ) \ || GCC_ATOMIC_INTRINSICS_AVAIL_EARLIER -#define OSD_ATOMIC_GCC - #ifdef __cplusplus extern "C" { #endif @@ -77,6 +78,13 @@ OSD_ATOMIC_INLINE void epicsAtomicSetUIntT ( unsigned * pTarget, __sync_synchronize (); } +OSD_ATOMIC_INLINE void epicsAtomicSetPtrT ( EpicsAtomicPtrT * pTarget, + EpicsAtomicPtrT newValue ) +{ + *pTarget = newValue; + __sync_synchronize (); +} + OSD_ATOMIC_INLINE size_t epicsAtomicGetSizeT ( const size_t * pTarget ) { __sync_synchronize (); @@ -89,10 +97,22 @@ OSD_ATOMIC_INLINE unsigned epicsAtomicGetUIntT ( const unsigned * pTarget ) return *pTarget; } -OSD_ATOMIC_INLINE unsigned epicsAtomicTestAndSetUIntT ( unsigned * pTarget ) +OSD_ATOMIC_INLINE EpicsAtomicPtrT epicsAtomicGetPtrT ( const EpicsAtomicPtrT * pTarget ) { - const size_t prev = __sync_lock_test_and_set ( pTarget, 1u ); - return prev == 0; + __sync_synchronize (); + return *pTarget; +} + +OSD_ATOMIC_INLINE unsigned epicsAtomicCmpAndSwapUIntT ( unsigned * pTarget, + unsigned oldVal, unsigned newVal ) +{ + return __sync_val_compare_and_swap ( pTarget, oldVal, newVal); +} + +OSD_ATOMIC_INLINE EpicsAtomicPtrT epicsAtomicCmpAndSwapPtrT ( EpicsAtomicPtrT * pTarget, + EpicsAtomicPtrT oldVal, EpicsAtomicPtrT newVal ) +{ + return __sync_val_compare_and_swap ( pTarget, oldVal, newVal); } #ifdef __cplusplus @@ -101,6 +121,11 @@ OSD_ATOMIC_INLINE unsigned epicsAtomicTestAndSetUIntT ( unsigned * pTarget ) #else /* if GCC_ATOMIC_INTRINSICS_AVAIL */ +# if GCC_ATOMIC_INTRINSICS_GCC4_OR_BETTER && __i386 + /* 386 hardware is probably rare today even in embedded systems */ +# warning "this code will run much faster if specifying i486 or better" +# endif + /* * not available as gcc intrinsics so we * will employ an os specific inline solution diff --git a/src/libCom/osi/compiler/msvc/epicsAtomicCD.h b/src/libCom/osi/compiler/msvc/epicsAtomicCD.h index 08cd5ea31..4923664e7 100644 --- a/src/libCom/osi/compiler/msvc/epicsAtomicCD.h +++ b/src/libCom/osi/compiler/msvc/epicsAtomicCD.h @@ -24,21 +24,6 @@ #ifdef _MSC_EXTENSIONS -/* - * I have discovered an anomaly in visual c++ where - * the DLL instantiation of an exported inline interface - * does not occur in a c++ code unless "inline" is used. - */ -#if defined ( __cplusplus ) -# define OSD_ATOMIC_INLINE inline -#elif defined ( _MSC_VER ) -# define OSD_ATOMIC_INLINE __inline -#endif - -#if defined ( _M_X64 ) || defined ( _M_IA64 ) -# define OSD_ATOMIC_64 -#endif /* defined ( _M_X64 ) || defined ( _M_IA64 ) */ - #include #pragma intrinsic ( _InterlockedExchange ) @@ -54,6 +39,33 @@ # pragma intrinsic ( _InterlockedExchangeAdd64 ) #endif +#if _MSC_VER >= 1200 +# define OSD_ATOMIC_INLINE __forceinline +#else +# define OSD_ATOMIC_INLINE __inline +#endif + +#if defined ( _M_IX86 ) +# pragma warning( push ) +# pragma warning( disable : 4793 ) + OSD_ATOMIC_INLINE void OSD_ATOMIC_SYNC () + { + long fence; + __asm { xchg fence, eax } + } +# pragma warning( pop ) +#elif defined ( _M_X64 ) +# define OSD_ATOMIC_64 +# pragma intrinsic ( __faststorefence ) +# define OSD_ATOMIC_SYNC __faststorefence +#elif defined ( _M_IA64 ) +# define OSD_ATOMIC_64 +# pragma intrinsic ( __mf ) +# define OSD_ATOMIC_SYNC __mf +#else +# error unexpected target architecture, msvc version of epicsAtomicCD.h +#endif + /* * The windows doc appears to recommend defining InterlockedExchange * to be _InterlockedExchange to cause it to be an intrinsic, but that @@ -71,27 +83,53 @@ STATIC_ASSERT ( sizeof ( long ) == sizeof ( unsigned ) ); OSD_ATOMIC_INLINE void epicsAtomicSetUIntT ( unsigned * pTarget, unsigned newVal ) { - long * const pTarg = ( long * ) ( pTarget ); - _InterlockedExchange ( pTarg, ( long ) newVal ); + *pTarget = newVal; + OSD_ATOMIC_SYNC (); +} + +OSD_ATOMIC_INLINE void epicsAtomicSetSizeT ( size_t * pTarget, size_t newVal ) +{ + *pTarget = newVal; + OSD_ATOMIC_SYNC (); +} + +OSD_ATOMIC_INLINE void epicsAtomicSetPtrT ( EpicsAtomicPtrT * pTarget, + EpicsAtomicPtrT newVal ) +{ + *pTarget = newVal; + OSD_ATOMIC_SYNC (); } OSD_ATOMIC_INLINE unsigned epicsAtomicGetUIntT ( const unsigned * pTarget ) { - long * const pTarg = ( long * ) ( pTarget ); - return _InterlockedExchangeAdd ( pTarg, 0 ); + OSD_ATOMIC_SYNC (); + return *pTarget; } -OSD_ATOMIC_INLINE unsigned epicsAtomicTestAndSetUIntT ( unsigned * pTarget ) +OSD_ATOMIC_INLINE size_t epicsAtomicGetSizeT ( const size_t * pTarget ) +{ + OSD_ATOMIC_SYNC (); + return *pTarget; +} + +OSD_ATOMIC_INLINE EpicsAtomicPtrT epicsAtomicGetPtrT ( const EpicsAtomicPtrT * pTarget ) +{ + OSD_ATOMIC_SYNC (); + return *pTarget; +} + +OSD_ATOMIC_INLINE unsigned epicsAtomicCmpAndSwapUIntT ( unsigned * pTarget, + unsigned oldVal, unsigned newVal ) { long * const pTarg = ( long * ) ( pTarget ); - return _InterlockedCompareExchange ( pTarg, 1, 0 ) == 0; + return (unsigned) _InterlockedCompareExchange ( pTarg, + (long) newVal, (long) oldVal ); } - #if ! OSD_ATOMIC_64 /* - * necessary for next four functions + * necessary for next five functions * * looking at the MS documentation it appears that they will * keep type long the same size as an int on 64 bit builds @@ -110,22 +148,18 @@ OSD_ATOMIC_INLINE size_t epicsAtomicDecrSizeT ( size_t * pTarget ) return _InterlockedDecrement ( pTarg ); } -OSD_ATOMIC_INLINE void epicsAtomicSetSizeT ( size_t * pTarget, size_t newVal ) +OSD_ATOMIC_INLINE EpicsAtomicPtrT epicsAtomicCmpAndSwapPtrT ( EpicsAtomicPtrT * pTarget, + EpicsAtomicPtrT oldVal, EpicsAtomicPtrT newVal ) { long * const pTarg = ( long * ) ( pTarget ); - _InterlockedExchange ( pTarg, ( long ) newVal ); -} - -OSD_ATOMIC_INLINE size_t epicsAtomicGetSizeT ( const size_t * pTarget ) -{ - long * const pTarg = ( long * ) ( pTarget ); - return _InterlockedExchangeAdd ( pTarg, 0 ); + return (EpicsAtomicPtrT) _InterlockedCompareExchange ( pTarg, + (long) newVal, (long) oldVal ); } #else /* ! OSD_ATOMIC_64 */ /* - * necessary for next four functions + * necessary for next five functions */ STATIC_ASSERT ( sizeof ( long long ) == sizeof ( size_t ) ); @@ -141,16 +175,12 @@ OSD_ATOMIC_INLINE size_t epicsAtomicDecrSizeT ( size_t * pTarget ) return _InterlockedDecrement64 ( pTarg ); } -OSD_ATOMIC_INLINE void epicsAtomicSetSizeT ( size_t * pTarget, size_t newVal ) +OSD_ATOMIC_INLINE EpicsAtomicPtrT epicsAtomicCmpAndSwapPtrT ( EpicsAtomicPtrT * pTarget, + EpicsAtomicPtrT oldVal, EpicsAtomicPtrT newVal ) { - long long * const pTarg = ( long long * ) ( pTarget ); - _InterlockedExchange64 ( pTarg, ( long long ) newVal ); -} - -OSD_ATOMIC_INLINE size_t epicsAtomicGetSizeT ( const size_t * pTarget ) -{ - long long * const pTarg = ( long long * ) ( pTarget ); - return _InterlockedExchangeAdd64 ( pTarg, 0 ); + long long * const pTarg = ( longlong * ) ( pTarget ); + return (EpicsAtomicPtrT) _InterlockedCompareExchange64 ( pTarg, + (long long) newVal, (long long) oldVal ); } #endif /* ! OSD_ATOMIC_64 */ diff --git a/src/libCom/osi/epicsAtomic.h b/src/libCom/osi/epicsAtomic.h index 72bb63e54..b444b1240 100644 --- a/src/libCom/osi/epicsAtomic.h +++ b/src/libCom/osi/epicsAtomic.h @@ -24,6 +24,8 @@ extern "C" { #endif +typedef void * EpicsAtomicPtrT; + /* * lock out other smp processors from accessing the target, * sync target in cache, add one to target, flush target in @@ -53,26 +55,31 @@ epicsShareFunc size_t epicsAtomicDecrSizeT ( size_t * pTarget ); */ epicsShareFunc void epicsAtomicSetSizeT ( size_t * pTarget, size_t newValue ); epicsShareFunc void epicsAtomicSetUIntT ( unsigned * pTarget, unsigned newValue ); +epicsShareFunc void epicsAtomicSetPtrT ( EpicsAtomicPtrT * pTarget, EpicsAtomicPtrT newValue ); /* * fetch target in cache, return new value of target */ epicsShareFunc size_t epicsAtomicGetSizeT ( const size_t * pTarget ); epicsShareFunc unsigned epicsAtomicGetUIntT ( const unsigned * pTarget ); +epicsShareFunc EpicsAtomicPtrT epicsAtomicGetPtrT ( const EpicsAtomicPtrT * pTarget ); /* * lock out other smp processors from accessing the target, - * sync target in cache, if target is zero set target to - * non-zero (true) value, flush target in cache, allow out - * other smp processors to access the target, return true if - * this request changed the target from a zero value to a - * non-zero (true) value and otherwise false + * sync target in cache, if target is equal to oldVal set target + * to newVal, flush target in cache, allow other smp processors + * to access the target, return the original value stored in the + * target * - * test and set is chosen as the primitive here because, - * compared to comapare-and-swap it is more likely to - * be implemented atomically on old architectures such as 68k + * !!!! beware, this will not guarantee an atomic operation + * !!!! on legacy vxWorks (prior to 6.6) if the target operand + * !!!! is on a multi-commander bus (such as the VME bus) */ -epicsShareFunc unsigned epicsAtomicTestAndSetUIntT ( unsigned * pTarget ); +epicsShareFunc unsigned epicsAtomicCmpAndSwapUIntT ( unsigned * pTarget, + unsigned oldVal, unsigned newVal ); +epicsShareFunc EpicsAtomicPtrT epicsAtomicCmpAndSwapPtrT ( EpicsAtomicPtrT * pTarget, + EpicsAtomicPtrT oldVal, EpicsAtomicPtrT newVal ); + /* * the following are, never inline and always synchronized by a global @@ -84,9 +91,14 @@ epicsShareFunc size_t epicsLockedIncrSizeT ( size_t * pTarget ); epicsShareFunc size_t epicsLockedDecrSizeT ( size_t * pTarget ); epicsShareFunc void epicsLockedSetSizeT ( size_t * pTarget, size_t newVal ); epicsShareFunc void epicsLockedSetUIntT ( unsigned * pTarget, unsigned newVal ); +epicsShareFunc void epicsLockedSetPtrT ( unsigned * pTarget, EpicsAtomicPtrT newVal ); epicsShareFunc size_t epicsLockedGetSizeT ( const size_t * pTarget ); epicsShareFunc unsigned epicsLockedGetUIntT ( const unsigned * pTarget ); -epicsShareFunc unsigned epicsLockedTestAndSetUIntT ( unsigned * pTarget ); +epicsShareFunc EpicsAtomicPtrT epicsLockedGetPtrT ( const unsigned * pTarget ); +epicsShareFunc unsigned epicsLockedCmpAndSwapUIntT ( unsigned * pTarget, + unsigned oldval, unsigned newval ); +epicsShareFunc EpicsAtomicPtrT epicsLockedCmpAndSwapPtrT ( EpicsAtomicPtrT * pTarget, + EpicsAtomicPtrT oldVal, EpicsAtomicPtrT newVal ); #ifdef __cplusplus } /* end of extern "C" */ diff --git a/src/libCom/osi/epicsAtomicLocked.cpp b/src/libCom/osi/epicsAtomicLocked.cpp index 625dea118..65a64fb83 100644 --- a/src/libCom/osi/epicsAtomicLocked.cpp +++ b/src/libCom/osi/epicsAtomicLocked.cpp @@ -24,6 +24,7 @@ #define epicsExportSharedSymbols #include "epicsAtomic.h" +#include "epicsThread.h" #include "epicsMutex.h" namespace { @@ -33,23 +34,47 @@ public: AtomicGuard (); ~AtomicGuard (); private: - static epicsMutexOSD & m_mutex; + static epicsMutexOSD * m_pMutex; + static epicsThreadOnceId m_onceFlag; + static void m_once ( void * ); }; // -// see c++ FAQ, static init order fiasco +// c++ 0x specifies the behavior for concurrent +// access to block scope statics but some compiler +// writers, lacking clear guidance in the earlier +// c++ standards, curiously implement thread unsafe +// block static variables despite ensuring for +// proper multithreaded behavior for many other +// parst of the compiler infrastructure such as +// runtime support for exception handling // -epicsMutexOSD & AtomicGuard :: m_mutex = * epicsMutexOsdCreate (); +// since this is potentially used by the implementation +// of staticInstance we cant use it here and must use +// epicsThreadOnce despite its perfomance pentalty +// +// using epicsThreadOnce here (at this time) increases +// the overhead of AtomicGuard by as much as 100% +// +epicsMutexOSD * AtomicGuard :: m_pMutex = 0; +epicsThreadOnceId AtomicGuard :: m_onceFlag = EPICS_THREAD_ONCE_INIT; + +void AtomicGuard :: m_once ( void * ) +{ + m_pMutex = epicsMutexOsdCreate (); +} inline AtomicGuard :: AtomicGuard () { - const int status = epicsMutexOsdLock ( & m_mutex ); + + epicsThreadOnce ( & m_onceFlag, m_once, 0 ); + const int status = epicsMutexOsdLock ( m_pMutex ); assert ( status == epicsMutexLockOK ); } inline AtomicGuard :: ~AtomicGuard () { - epicsMutexOsdUnlock ( & m_mutex ); + epicsMutexOsdUnlock ( m_pMutex ); } } // end of anonymous namespace @@ -92,14 +117,26 @@ unsigned epicsLockedGetUIntT ( const unsigned * pTarget ) return *pTarget; } -unsigned epicsLockedTestAndSetUIntT ( unsigned * pTarget ) +unsigned epicsLockedCmpAndSwapUIntT ( unsigned * pTarget, + unsigned oldval, unsigned newval ) { AtomicGuard atomicGuard; - const bool weWillSetIt = ( *pTarget == 0u ); - if ( weWillSetIt ) { - *pTarget = 1u; + const unsigned cur = *pTarget; + if ( cur == oldval ) { + *pTarget = newval; } - return weWillSetIt; + return cur; +} + +EpicsAtomicPtrT epicsLockedCmpAndSwapPtrT ( EpicsAtomicPtrT * pTarget, + EpicsAtomicPtrT oldval, EpicsAtomicPtrT newval ) +{ + AtomicGuard atomicGuard; + const EpicsAtomicPtrT cur = *pTarget; + if ( cur == oldval ) { + *pTarget = newval; + } + return cur; } } // end of extern "C" diff --git a/src/libCom/osi/epicsAtomicLocked.h b/src/libCom/osi/epicsAtomicLocked.h index 1ca29fd4c..4f9749781 100644 --- a/src/libCom/osi/epicsAtomicLocked.h +++ b/src/libCom/osi/epicsAtomicLocked.h @@ -22,16 +22,6 @@ extern "C" { #endif /* __cplusplus */ -OSD_ATOMIC_INLINE unsigned epicsAtomicTestAndSetUIntT ( unsigned * pTarget ) -{ - return epicsLockedTestAndSetUIntT ( pTarget ); -} - -OSD_ATOMIC_INLINE void epicsAtomicSetUIntT ( unsigned * pTarget, unsigned newVal ) -{ - epicsLockedSetUIntT ( pTarget, newVal ); -} - OSD_ATOMIC_INLINE size_t epicsAtomicIncrSizeT ( size_t * pTarget ) { return epicsLockedIncrSizeT ( pTarget ); @@ -57,6 +47,33 @@ OSD_ATOMIC_INLINE unsigned epicsAtomicGetUIntT ( const unsigned * pTarget ) return epicsLockedGetUIntT ( pTarget ); } +OSD_ATOMIC_INLINE void epicsAtomicSetUIntT ( unsigned * pTarget, unsigned newVal ) +{ + epicsLockedSetUIntT ( pTarget, newVal ); +} + +OSD_ATOMIC_INLINE void epicsAtomicSetPtrT ( EpicsAtomicPtrT * pTarget, EpicsAtomicPtrT newVal ) +{ + epicsLockedSetPtrT ( pTarget, newVal ); +} + +OSD_ATOMIC_INLINE EpicsAtomicPtrT epicsAtomicGetPtrT ( const EpicsAtomicPtrT * pTarget ) +{ + return epicsLockedGetPtrT ( pTarget ); +} + +OSD_ATOMIC_INLINE unsigned epicsAomicCmpAndSwapUIntT ( unsigned * pTarget, + unsigned oldVal, unsigned newVal ) +{ + return epicsLockedCmpAndSwapUIntT ( pTarget, oldVal, newVal ); +} + +OSD_ATOMIC_INLINE EpicsAtomicPtrT epicsLockedCmpAndSwapPtrT ( EpicsAtomicPtrT * pTarget, + EpicsAtomicPtrT oldval, EpicsAtomicPtrT newval ) +{ + return epicsLockedCmpAndSwapPtrT ( pTarget, oldVal, newVal ); +} + #ifdef __cplusplus } /* end of extern "C" */ #endif /* __cplusplus */ @@ -67,9 +84,12 @@ OSD_ATOMIC_INLINE unsigned epicsAtomicGetUIntT ( const unsigned * pTarget ) # define epicsAtomicDecrSizeT epicsLockedDecrSizeT # define epicsAtomicSetSizeT epicsLockedSetSizeT # define epicsAtomicSetUIntT epicsLockedSetUIntT +# define epicsAtomicSetPtrT epicsLockedSetPtrT # define epicsAtomicGetSizeT epicsLockedGetSizeT # define epicsAtomicGetUIntT epicsLockedGetUIntT -# define epicsAtomicTestAndSetUIntT epicsLockedTestAndSetUIntT +# define epicsAtomicGetPtrT epicsLockedGetPtrT +# define epicsAtomicCmpAndSwapUIntT epicsLockedCmpAndSwapUIntT +# define epicsAtomicCmpAndSwapPtrT epicsLockedCmpAndSwapPtrT #endif /* if defined ( OSD_ATOMIC_INLINE ) */ diff --git a/src/libCom/osi/os/WIN32/epicsAtomicOSD.h b/src/libCom/osi/os/WIN32/epicsAtomicOSD.h index 27b59b242..3b3ab6942 100644 --- a/src/libCom/osi/os/WIN32/epicsAtomicOSD.h +++ b/src/libCom/osi/os/WIN32/epicsAtomicOSD.h @@ -20,9 +20,6 @@ #include -#define __STDC_LIMIT_MACROS /* define SIZE_MAX for c++ */ -#include - #include "epicsAssert.h" #define STRICT @@ -33,37 +30,70 @@ extern "C" { #endif /* __cplusplus */ +/* + * mingw doesnt currently provide MemoryBarrier + * (this is mostly for testing purposes as the gnu + * intrinsics are used if compiling for 486 or better + * minimum hardware) + */ +#ifdef __MINGW32__ + extern inline void MemoryBarrier() { + int fence = 0; + __asm__ __volatile__( "xchgl %%eax,%0 ":"=r" (fence) ); + } +#endif // ifdef __MINGW32__ + /* necessary for next three functions */ STATIC_ASSERT ( sizeof ( LONG ) == sizeof ( unsigned ) ); OSD_ATOMIC_INLINE void epicsAtomicSetUIntT ( unsigned * pTarget, unsigned newVal ) { - LONG * const pTarg = ( LONG * ) ( pTarget ); - InterlockedExchange ( pTarg, ( LONG ) newVal ); + *pTarget = newVal; + MemoryBarrier (); +} + +OSD_ATOMIC_INLINE void epicsAtomicSetSizeT ( size_t * pTarget, size_t newVal ) +{ + *pTarget = newVal; + MemoryBarrier (); +} + +OSD_ATOMIC_INLINE void epicsAtomicSetPtrT ( EpicsAtomicPtrT * pTarget, EpicsAtomicPtrT newVal ) +{ + *pTarget = newVal; + MemoryBarrier (); } OSD_ATOMIC_INLINE unsigned epicsAtomicGetUIntT ( const unsigned * pTarget ) { - LONG * const pTarg = ( LONG * ) ( pTarget ); - return InterlockedExchangeAdd ( pTarg, 0 ); + MemoryBarrier (); + return *pTarget; } -OSD_ATOMIC_INLINE unsigned epicsAtomicTestAndSetUIntT ( unsigned * pTarget ) +OSD_ATOMIC_INLINE size_t epicsAtomicGetSizeT ( const size_t * pTarget ) { - long * const pTarg = ( LONG * ) ( pTarget ); - return InterlockedCompareExchange ( pTarg, 1, 0 ) == 0; + MemoryBarrier (); + return *pTarget; } -/* - * on win32 a LONG is 32 bits, but I am concerned that - * we shouldnt use LONG_MAX here because with certain - * compilers a long will be 64 bits wide - */ -#define WIN32_LONG_MAX 0xffffffff -#if SIZE_MAX == WIN32_LONG_MAX +OSD_ATOMIC_INLINE EpicsAtomicPtrT epicsAtomicGetPtrT ( const EpicsAtomicPtrT * pTarget ) +{ + MemoryBarrier (); + return *pTarget; +} + +OSD_ATOMIC_INLINE unsigned epicsAtomicCmpAndSwapUIntT ( unsigned * pTarget, + unsigned oldVal, unsigned newVal ) +{ + LONG * const pTarg = ( LONG * ) ( pTarget ); + return (unsigned) InterlockedCompareExchange ( pTarg, + (LONG) newVal, (LONG) oldVal ); +} + +#if defined ( _WIN32 ) /* - * necessary for next four functions + * necessary for next five functions * * looking at the MS documentation it appears that they will * keep type long the same size as an int on 64 bit builds @@ -82,22 +112,18 @@ OSD_ATOMIC_INLINE size_t epicsAtomicDecrSizeT ( size_t * pTarget ) return InterlockedDecrement ( pTarg ); } -OSD_ATOMIC_INLINE void epicsAtomicSetSizeT ( size_t * pTarget, size_t newVal ) +OSD_ATOMIC_INLINE EpicsAtomicPtrT epicsAtomicCmpAndSwapPtrT ( EpicsAtomicPtrT * pTarget, + EpicsAtomicPtrT oldVal, EpicsAtomicPtrT newVal ) { LONG * const pTarg = ( LONG * ) ( pTarget ); - InterlockedExchange ( pTarg, ( LONG ) newVal ); + return (EpicsAtomicPtrT) InterlockedCompareExchange ( pTarg, + (LONG) newVal, (LONG) oldVal ); } -OSD_ATOMIC_INLINE size_t epicsAtomicGetSizeT ( const size_t * pTarget ) -{ - LONG * const pTarg = ( LONG * ) ( pTarget ); - return InterlockedExchangeAdd ( pTarg, 0 ); -} - -#else /* SIZE_MAX == WIN32_LONG_MAX */ +#elif defined ( _WIN64 ) /* - * necessary for next four functions + * necessary for next five functions */ STATIC_ASSERT ( sizeof ( LONGLONG ) == sizeof ( size_t ) ); @@ -113,19 +139,15 @@ OSD_ATOMIC_INLINE size_t epicsAtomicDecrSizeT ( size_t * pTarget ) return InterlockedDecrement64 ( pTarg ); } -OSD_ATOMIC_INLINE void epicsAtomicSetSizeT ( size_t * pTarget, size_t newVal ) +OSD_ATOMIC_INLINE EpicsAtomicPtrT epicsAtomicCmpAndSwapPtrT ( EpicsAtomicPtrT * pTarget, + EpicsAtomicPtrT oldVal, EpicsAtomicPtrT newVal ) { LONGLONG * const pTarg = ( LONGLONG * ) ( pTarget ); - InterlockedExchange64 ( pTarg, ( LONGLONG ) newVal ); + return (EpicsAtomicPtrT) InterlockedCompareExchange64 ( pTarg, + (LONGLONG) newVal, (LONGLONG) oldVal ); } -OSD_ATOMIC_INLINE size_t epicsAtomicGetSizeT ( const size_t * pTarget ) -{ - LONGLONG * const pTarg = ( LONGLONG * ) ( pTarget ); - return InterlockedExchangeAdd64 ( pTarg, 0 ); -} - -#endif /* SIZE_MAX == WIN32_LONG_MAX */ +#endif /* if defined ( _WIN32 ) */ #ifdef __cplusplus } /* end of extern "C" */ diff --git a/src/libCom/osi/os/solaris/epicsAtomicOSD.h b/src/libCom/osi/os/solaris/epicsAtomicOSD.h index 7a6946899..542997ccf 100644 --- a/src/libCom/osi/os/solaris/epicsAtomicOSD.h +++ b/src/libCom/osi/os/solaris/epicsAtomicOSD.h @@ -33,91 +33,66 @@ extern "C" { #endif /* __cplusplus */ -OSD_ATOMIC_INLINE unsigned epicsAtomicTestAndSetUIntT ( unsigned * pTarget ) +OSD_ATOMIC_INLINE unsigned epicsAtomicCmpAndSwapUIntT ( unsigned * pTarget, + unsigned oldVal, unsigned newVal ) { - const uint_t oldVal = atomic_cas_uint ( pTarget, 0u, 1u ); - return oldVal == 0u; + return atomic_cas_uint ( pTarget, oldVal, newVal ); +} + +OSD_ATOMIC_INLINE EpicsAtomicPtrT epicsAtomicCmpAndSwapPtrT ( EpicsAtomicPtrT * pTarget, + EpicsAtomicPtrT oldVal, EpicsAtomicPtrT newVal ) +{ + return atomic_cas_ptr ( pTarget, oldVal, newVal ); +} + +OSD_ATOMIC_INLINE size_t epicsAtomicIncrSizeT ( size_t * pTarget ) +{ + void * const pTarg = ( void * ) ( pTarget ); + return ( size_t ) atomic_inc_ptr_nv ( pTarg ); +} + +OSD_ATOMIC_INLINE size_t epicsAtomicDecrSizeT ( size_t * pTarget ) +{ + void * const pTarg = ( void * ) ( pTarget ); + return atomic_dec_ptr_nv ( pTarg ); } OSD_ATOMIC_INLINE void epicsAtomicSetUIntT ( unsigned * pTarget, unsigned newVal ) { - atomic_swap_uint ( pTarget, newVal ); + *pTarget = newVal; + membar_producer(); +} + +OSD_ATOMIC_INLINE void epicsAtomicSetSizeT ( size_t * pTarget, size_t newVal ) +{ + *pTarget = newVal; + membar_producer(); +} + +OSD_ATOMIC_INLINE void epicsAtomicSetPtrT ( EpicsAtomicPtrT * pTarget, EpicsAtomicPtrT newVal ) +{ + *pTarget = newVal; + membar_producer(); } OSD_ATOMIC_INLINE unsigned epicsAtomicGetUIntT ( const unsigned * pTarget ) { - /* - * we cast away const, but are careful not to modify - * the target - */ - unsigned * const pTarg = ( unsigned * ) ( pTarget ); - return atomic_or_uint_nv ( pTarg, 0U ); -} - -#if SIZE_MAX == UINT_MAX - -OSD_ATOMIC_INLINE size_t epicsAtomicIncrSizeT ( size_t * pTarget ) -{ - unsigned * const pTarg = ( unsigned * ) ( pTarget ); - return atomic_inc_uint_nv ( pTarg ); -} - -OSD_ATOMIC_INLINE size_t epicsAtomicDecrSizeT ( size_t * pTarget ) -{ - unsigned * const pTarg = ( unsigned * ) ( pTarget ); - return atomic_dec_uint_nv ( pTarg ); -} - -OSD_ATOMIC_INLINE void epicsAtomicSetSizeT ( size_t * pTarget, size_t newVal ) -{ - unsigned * const pTarg = ( unsigned * ) ( pTarget ); - atomic_swap_uint ( pTarg, newVal ); + membar_consumer (); + return *pTarget; } OSD_ATOMIC_INLINE size_t epicsAtomicGetSizeT ( const size_t * pTarget ) { - /* - * we cast away const, but are careful not to modify - * the target - */ - unsigned * const pTarg = ( unsigned * ) ( pTarget ); - return atomic_or_uint_nv ( pTarg, 0U ); + membar_consumer (); + return *pTarget; } -#elif SIZE_MAX == ULONG_MAX - -OSD_ATOMIC_INLINE size_t epicsAtomicIncrSizeT ( size_t * pTarget ) +OSD_ATOMIC_INLINE EpicsAtomicPtrT epicsAtomicGetPtrT ( const EpicsAtomicPtrT * pTarget ) { - unsigned long * const pTarg = ( unsigned long * ) ( pTarget ); - return atomic_inc_ulong_nv ( pTarg ); + membar_consumer (); + return *pTarget; } -OSD_ATOMIC_INLINE size_t epicsAtomicDecrSizeT ( size_t * pTarget ) -{ - unsigned long * const pTarg = ( unsigned long * ) ( pTarget ); - return atomic_dec_ulong_nv ( pTarg ); -} - -OSD_ATOMIC_INLINE void epicsAtomicSetSizeT ( size_t * pTarget, size_t newVal ) -{ - unsigned long * const pTarg = ( unsigned long * ) ( pTarget ); - atomic_swap_ulong ( pTarg, newVal ); -} - -OSD_ATOMIC_INLINE size_t epicsAtomicGetSizeT ( const size_t * pTarget ) -{ - /* - * we cast away const, but are careful not to modify - * the target - */ - unsigned long * const pTarg = ( unsigned long * ) ( pTarget ); - return atomic_or_ulong_nv ( pTarg, 0UL ); -} - -#else -# error unsupported solaris architecture -#endif /* SIZE_MAX == UINT_MAX */ - #ifdef __cplusplus } /* end of extern "C" */ #endif /* __cplusplus */ diff --git a/src/libCom/osi/os/vxWorks/epicsAtomicOSD.h b/src/libCom/osi/os/vxWorks/epicsAtomicOSD.h index 76ecf0c84..e71f6d9b6 100644 --- a/src/libCom/osi/os/vxWorks/epicsAtomicOSD.h +++ b/src/libCom/osi/os/vxWorks/epicsAtomicOSD.h @@ -75,13 +75,32 @@ OSD_ATOMIC_INLINE void epicsAtomicSetSizeT ( size_t * pTarget, size_t newVal ) vxAtomicSet ( pTarg, newVal ); } +OSD_ATOMIC_INLINE void epicsAtomicSetPtrT ( EpicsAtomicPtrT * pTarget, EpicsAtomicPtrT newVal ) +{ + atomic_t * const pTarg = ( atomic_t * ) ( pTarget ); + vxAtomicSet ( pTarg, newVal ); +} + OSD_ATOMIC_INLINE size_t epicsAtomicGetSizeT ( const size_t * pTarget ) { atomic_t * const pTarg = ( atomic_t * ) ( pTarget ); return ( size_t ) vxAtomicGet ( pTarg ); } -#else /* SIZE_MAX == UINT_MAX */ +OSD_ATOMIC_INLINE EpicsAtomicPtrT epicsAtomicGetPtrT ( const EpicsAtomicPtrT * pTarget ) +{ + atomic_t * const pTarg = ( atomic_t * ) ( pTarget ); + return ( EpicsAtomicPtrT ) vxAtomicGet ( pTarg ); +} + +OSD_ATOMIC_INLINE EpicsAtomicPtrT epicsAtomicCmpAndSwapPtrT ( EpicsAtomicPtrT * pTarget, + EpicsAtomicPtrT oldVal, EpicsAtomicPtrT newVal ) +{ + atomic_t * const pTarg = ( atomic_t * ) ( pTarget ); + return (EpicsAtomicPtrT) vxCas ( pTarg, (atomic_t) oldVal, (atomic_t) newVal ); +} + +#else /* ULONG_MAX == UINT_MAX */ /* * if its 64 bit vxWorks and the compiler doesnt @@ -104,12 +123,28 @@ OSD_ATOMIC_INLINE void epicsAtomicSetSizeT ( size_t * pTarget, size_t newVal ) epicsLockedSetSizeT ( pTarget, newVal ); } +OSD_ATOMIC_INLINE void epicsAtomicSetPtrT ( EpicsAtomicPtrT * pTarget, EpicsAtomicPtrT newVal ) +{ + epicsLockedSetPtrT ( pTarget, newVal ); +} + OSD_ATOMIC_INLINE size_t epicsAtomicGetSizeT ( const size_t * pTarget ) { return epicsLockedGetSizeT ( pTarget ); } -#endif /* SIZE_MAX == UINT_MAX */ +OSD_ATOMIC_INLINE EpicsAtomicPtrT epicsAtomicGetPtrT ( const EpicsAtomicPtrT * pTarget ) +{ + return epicsLockedGetPtrT ( pTarget ); +} + +OSD_ATOMIC_INLINE EpicsAtomicPtrT epicsAtomicCmpAndSwapPtrT ( EpicsAtomicPtrT * pTarget, + EpicsAtomicPtrT oldVal, EpicsAtomicPtrT newVal ) +{ + return epicsLockedCmpAndSwapPtrT ( pTarget, oldVal, newVal ); +} + +#endif /* ULONG_MAX == UINT_MAX */ STATIC_ASSERT ( sizeof ( atomic_t ) == sizeof ( unsigned ) ); @@ -119,13 +154,6 @@ OSD_ATOMIC_INLINE void epicsAtomicSetUIntT ( unsigned * pTarget, unsigned newVal vxAtomicSet ( pTarg, newVal ); } -OSD_ATOMIC_INLINE unsigned epicsAtomicTestAndSetUIntT ( unsigned * pTarget ) -{ - STATIC_ASSERT ( sizeof ( atomic_t ) == sizeof ( unsigned ) ); - atomic_t * const pTarg = ( atomic_t * ) ( pTarget ); - return vxCas ( pTarg, 0, 1 ) != 0; -} - OSD_ATOMIC_INLINE unsigned epicsAtomicGetUIntT ( const unsigned * pTarget ) { STATIC_ASSERT ( sizeof ( atomic_t ) == sizeof ( unsigned ) ); @@ -133,6 +161,13 @@ OSD_ATOMIC_INLINE unsigned epicsAtomicGetUIntT ( const unsigned * pTarget ) return ( unsigned ) vxAtomicGet ( pTarg ); } +OSD_ATOMIC_INLINE unsigned epicsAtomicCmpAndSwapUIntT ( unsigned * pTarget, + unsigned oldVal, unsigned newVal ) +{ + atomic_t * const pTarg = ( atomic_t * ) ( pTarget ); + return (unsigned) vxCas ( pTarg, (atomic_t) oldVal, (atomic_t) newVal ); +} + #ifdef __cplusplus } /* end of extern "C" */ #endif /* __cplusplus */ @@ -196,6 +231,15 @@ OSD_ATOMIC_INLINE void epicsAtomicSetUIntT ( unsigned * pTarget, unsigned newVal *pTarget = newVal; } +OSD_ATOMIC_INLINE void epicsAtomicSetPtrT ( EpicsAtomicPtrT * pTarget, EpicsAtomicPtrT newVal ) +{ + /* + * no need for memory barrior since this + * is a single cpu system + */ + *pTarget = newVal; +} + OSD_ATOMIC_INLINE size_t epicsAtomicGetSizeT ( const size_t * pTarget ) { /* @@ -214,10 +258,51 @@ OSD_ATOMIC_INLINE unsigned epicsAtomicGetUIntT ( const unsigned * pTarget ) return *pTarget; } -OSD_ATOMIC_INLINE unsigned epicsAtomicTestAndSetUIntT ( unsigned * pTarget ) +OSD_ATOMIC_INLINE EpicsAtomicPtrT epicsAtomicGetPtrT ( const EpicsAtomicPtrT * pTarget ) { - STATIC_ASSERT ( sizeof ( int ) == sizeof ( unsigned ) ); - return vxTas ( pTarget ); + /* + * no need for memory barrior since this + * is a single cpu system + */ + return *pTarget; +} + +OSD_ATOMIC_INLINE unsigned epicsAtomicCmpAndSwapUIntT ( unsigned * pTarget, + unsigned oldVal, unsigned newVal ) +{ + /* + * no need for memory barrior since this + * is a single cpu system + * + * !!!! beware, this will not guarantee an atomic operation + * !!!! if the target operand is on the VME bus + */ + const int key = intLock (); + const unsigned curr = *pTarget; + if ( curr == oldVal ) { + *pTarget = newVal; + } + intUnlock ( key ); + return curr; +} + +OSD_ATOMIC_INLINE EpicsAtomicPtrT epicsAtomicCmpAndSwapPtrT ( EpicsAtomicPtrT * pTarget, + EpicsAtomicPtrT oldVal, EpicsAtomicPtrT newVal ) +{ + /* + * no need for memory barrior since this + * is a single cpu system + * + * !!!! beware, this will not guarantee an atomic operation + * !!!! if the target operand is on the VME bus + */ + const int key = intLock (); + const EpicsAtomicPtrT curr = *pTarget; + if ( curr == oldVal ) { + *pTarget = newVal; + } + intUnlock ( key ); + return curr; } #ifdef __cplusplus diff --git a/src/libCom/test/epicsAtomicPerform.cpp b/src/libCom/test/epicsAtomicPerform.cpp index 5a8307598..3a116008d 100644 --- a/src/libCom/test/epicsAtomicPerform.cpp +++ b/src/libCom/test/epicsAtomicPerform.cpp @@ -305,46 +305,46 @@ inline void oneThousandAtomicIncr ( size_t & target ) oneHundredAtomicIncr ( target ); } -inline void tenAtomicTestAndSet ( unsigned & target ) +inline void tenAtomicCmpAndSwap ( unsigned & target ) { - epicsAtomicTestAndSetUIntT ( & target ); - epicsAtomicTestAndSetUIntT ( & target ); - epicsAtomicTestAndSetUIntT ( & target ); - epicsAtomicTestAndSetUIntT ( & target ); - epicsAtomicTestAndSetUIntT ( & target ); - epicsAtomicTestAndSetUIntT ( & target ); - epicsAtomicTestAndSetUIntT ( & target ); - epicsAtomicTestAndSetUIntT ( & target ); - epicsAtomicTestAndSetUIntT ( & target ); - epicsAtomicTestAndSetUIntT ( & target ); + epicsAtomicCmpAndSwapUIntT ( & target, false, true ); + epicsAtomicCmpAndSwapUIntT ( & target, false, true ); + epicsAtomicCmpAndSwapUIntT ( & target, false, true ); + epicsAtomicCmpAndSwapUIntT ( & target, false, true ); + epicsAtomicCmpAndSwapUIntT ( & target, false, true ); + epicsAtomicCmpAndSwapUIntT ( & target, false, true ); + epicsAtomicCmpAndSwapUIntT ( & target, false, true ); + epicsAtomicCmpAndSwapUIntT ( & target, false, true ); + epicsAtomicCmpAndSwapUIntT ( & target, false, true ); + epicsAtomicCmpAndSwapUIntT ( & target, false, true ); } -inline void oneHundredAtomicTestAndSet ( unsigned & target ) +inline void oneHundredAtomicCmpAndSwap ( unsigned & target ) { - tenAtomicTestAndSet ( target ); - tenAtomicTestAndSet ( target ); - tenAtomicTestAndSet ( target ); - tenAtomicTestAndSet ( target ); - tenAtomicTestAndSet ( target ); - tenAtomicTestAndSet ( target ); - tenAtomicTestAndSet ( target ); - tenAtomicTestAndSet ( target ); - tenAtomicTestAndSet ( target ); - tenAtomicTestAndSet ( target ); + tenAtomicCmpAndSwap ( target ); + tenAtomicCmpAndSwap ( target ); + tenAtomicCmpAndSwap ( target ); + tenAtomicCmpAndSwap ( target ); + tenAtomicCmpAndSwap ( target ); + tenAtomicCmpAndSwap ( target ); + tenAtomicCmpAndSwap ( target ); + tenAtomicCmpAndSwap ( target ); + tenAtomicCmpAndSwap ( target ); + tenAtomicCmpAndSwap ( target ); } -inline void oneThousandAtomicTestAndSet ( unsigned & target ) +inline void oneThousandAtomicCmpAndSwap ( unsigned & target ) { - oneHundredAtomicTestAndSet ( target ); - oneHundredAtomicTestAndSet ( target ); - oneHundredAtomicTestAndSet ( target ); - oneHundredAtomicTestAndSet ( target ); - oneHundredAtomicTestAndSet ( target ); - oneHundredAtomicTestAndSet ( target ); - oneHundredAtomicTestAndSet ( target ); - oneHundredAtomicTestAndSet ( target ); - oneHundredAtomicTestAndSet ( target ); - oneHundredAtomicTestAndSet ( target ); + oneHundredAtomicCmpAndSwap ( target ); + oneHundredAtomicCmpAndSwap ( target ); + oneHundredAtomicCmpAndSwap ( target ); + oneHundredAtomicCmpAndSwap ( target ); + oneHundredAtomicCmpAndSwap ( target ); + oneHundredAtomicCmpAndSwap ( target ); + oneHundredAtomicCmpAndSwap ( target ); + oneHundredAtomicCmpAndSwap ( target ); + oneHundredAtomicCmpAndSwap ( target ); + oneHundredAtomicCmpAndSwap ( target ); } inline void tenAtomicSet ( size_t & target ) @@ -421,20 +421,20 @@ void epicsAtomicIncrPerformance () testDiag ( "epicsAtomicIncr() takes %f microseconds", delay ); } -void atomicTestAndSetPerformance () +void atomicCmpAndSwapPerformance () { epicsTime begin = epicsTime::getCurrent (); unsigned target; epicsAtomicSetUIntT ( & target, 0 ); testOk1 ( ! target ); for ( unsigned i = 0; i < N; i++ ) { - oneThousandAtomicTestAndSet ( target ); + oneThousandAtomicCmpAndSwap ( target ); } double delay = epicsTime::getCurrent () - begin; testOk1 ( target ); delay /= N * 1000u; // convert to delay per call delay *= 1e6; // convert to micro seconds - testDiag ( "epicsAtomicCompareAndSet() takes %f microseconds", delay ); + testDiag ( "epicsAtomicCmpAndSwap() takes %f microseconds", delay ); } void recursiveOwnershipRetPerformance () @@ -494,6 +494,6 @@ MAIN(epicsAtomicPerform) epicsAtomicIncrPerformance (); recursiveOwnershipRetPerformance (); ownershipPassRefPerformance (); - atomicCompareAndSetPerformance (); + atomicCmpAndSwapPerformance (); return testDone(); } diff --git a/src/libCom/test/epicsAtomicTest.c b/src/libCom/test/epicsAtomicTest.c index 5e0760b44..79f90f6d5 100644 --- a/src/libCom/test/epicsAtomicTest.c +++ b/src/libCom/test/epicsAtomicTest.c @@ -27,94 +27,147 @@ static void decr ( void *arg ) epicsAtomicIncrSizeT ( & pTestData->m_testIterations ); } -typedef struct TestDataTNS { +typedef struct TestDataTNS_UIntT { unsigned m_testValue; size_t m_testIterationsSet; size_t m_testIterationsNotSet; -} TestDataTNS; +} TestDataTNS_UIntT; + +typedef struct TestDataTNS_PtrT { + EpicsAtomicPtrT m_testValue; + size_t m_testIterationsSet; + size_t m_testIterationsNotSet; +} TestDataTNS_PtrT; + int isModulo ( size_t N, size_t n ) { return ( n % N ) == 0u; } -static void tns ( void *arg ) +static void tns_UIntT ( void *arg ) { - TestDataTNS * const pTestData = (TestDataTNS *) arg; + TestDataTNS_UIntT * const pTestData = (TestDataTNS_UIntT *) arg; /* * intentionally waste cpu and maximize * contention for the shared data */ epicsAtomicIncrSizeT ( & pTestData->m_testIterationsNotSet ); - while ( ! epicsAtomicTestAndSetUIntT ( & pTestData->m_testValue ) ) { + while ( ! epicsAtomicCmpAndSwapUIntT ( & pTestData->m_testValue, 0u, 1u ) ) { } - testOk ( epicsAtomicGetUIntT ( & pTestData->m_testValue ), - "test and set must leave operand in true state" ); epicsAtomicDecrSizeT ( & pTestData->m_testIterationsNotSet ); epicsAtomicSetUIntT ( & pTestData->m_testValue, 0u ); epicsAtomicIncrSizeT ( & pTestData->m_testIterationsSet ); } -MAIN(epicsAtomicTest) +static void tns_PtrT ( void *arg ) +{ + static char dummy = 0; + TestDataTNS_PtrT * const pTestData = (TestDataTNS_PtrT *) arg; + /* + * intentionally waste cpu and maximize + * contention for the shared data + */ + epicsAtomicIncrSizeT ( & pTestData->m_testIterationsNotSet ); + while ( ! epicsAtomicCmpAndSwapPtrT ( & pTestData->m_testValue, 0, & dummy ) ) { + } + epicsAtomicDecrSizeT ( & pTestData->m_testIterationsNotSet ); + epicsAtomicSetPtrT ( & pTestData->m_testValue, 0u ); + epicsAtomicIncrSizeT ( & pTestData->m_testIterationsSet ); +} + +MAIN(osiAtomicTest) { const unsigned int stackSize = epicsThreadGetStackSize ( epicsThreadStackSmall ); - static const size_t N_incrDecr = 100; - static const size_t N_testAndSet = 10; - - testPlan( 9 + N_testAndSet ); + testPlan(14); { - + static const size_t N = 100; size_t i; - TestDataIncrDecr testData = { 0, N_incrDecr }; - epicsAtomicSetSizeT ( & testData.m_testValue, N_incrDecr ); - testOk ( epicsAtomicGetSizeT ( & testData.m_testValue ) == N_incrDecr, + TestDataIncrDecr testData = { 0, N };; + epicsAtomicSetSizeT ( & testData.m_testValue, N ); + testOk ( epicsAtomicGetSizeT ( & testData.m_testValue ) == N, "set/get %u", testData.m_testValue ); epicsAtomicSetSizeT ( & testData.m_testIterations, 0u ); testOk ( epicsAtomicGetSizeT ( & testData.m_testIterations ) == 0u, "set/get %u", testData.m_testIterations ); - for ( i = 0u; i < N_incrDecr; i++ ) { - epicsThreadCreate ( "incr", 50, stackSize, incr, & testData ); - epicsThreadCreate ( "decr", 50, stackSize, decr, & testData ); + for ( i = 0u; i < N; i++ ) { + epicsThreadCreate ( "incr", + 50, stackSize, incr, & testData ); + epicsThreadCreate ( "decr", + 50, stackSize, decr, & testData ); } - while ( testData.m_testIterations < 2 * N_incrDecr ) { + while ( testData.m_testIterations < 2 * N ) { epicsThreadSleep ( 0.01 ); } - testOk ( epicsAtomicGetSizeT ( & testData.m_testIterations ) == 2 * N_incrDecr, - "incr/decr iterations %u", testData.m_testIterations ); - testOk ( epicsAtomicGetSizeT ( & testData.m_testValue ) == N_incrDecr, - "incr/decr final value %u", testData.m_testValue ); + testOk ( epicsAtomicGetSizeT ( & testData.m_testIterations ) == 2 * N, + "incr/decr iterations %u", + testData.m_testIterations ); + testOk ( epicsAtomicGetSizeT ( & testData.m_testValue ) == N, + "incr/decr final value %u", + testData.m_testValue ); } { + static const unsigned initVal = 1; + static const size_t N = 10; size_t i; - TestDataTNS testData = { 1, N_testAndSet, N_testAndSet }; + TestDataTNS_UIntT testData = { 0, N, N }; epicsAtomicSetSizeT ( & testData.m_testIterationsSet, 0u ); testOk ( epicsAtomicGetSizeT ( & testData.m_testIterationsSet ) == 0u, "set/get %u", testData.m_testIterationsSet ); epicsAtomicSetSizeT ( & testData.m_testIterationsNotSet, 0u ); testOk ( epicsAtomicGetSizeT ( & testData.m_testIterationsNotSet ) == 0u, "set/get %u", testData.m_testIterationsNotSet ); - for ( i = 0u; i < N_testAndSet; i++ ) { + epicsAtomicSetUIntT ( & testData.m_testValue, initVal ); + testOk ( epicsAtomicGetUIntT ( & testData.m_testValue ) == initVal, + "PtrT set/get %u", testData.m_testValue ); + for ( i = 0u; i < N; i++ ) { epicsThreadCreate ( "tns", - 50, stackSize, tns, & testData ); + 50, stackSize, tns_UIntT, & testData ); } epicsAtomicSetUIntT ( & testData.m_testValue, 0u ); - while ( testData.m_testIterationsSet < N_testAndSet ) { + while ( testData.m_testIterationsSet < N ) { epicsThreadSleep ( 0.01 ); } - testOk ( epicsAtomicGetSizeT ( & testData.m_testIterationsSet ) == N_testAndSet, - "test and set iterations %u", testData.m_testIterationsSet ); + testOk ( epicsAtomicGetSizeT ( & testData.m_testIterationsSet ) == N, + "test and set iterations %u", + testData.m_testIterationsSet ); testOk ( epicsAtomicGetSizeT ( & testData.m_testIterationsNotSet ) == 0u, - "test and set not-set tracking = %u", testData.m_testIterationsNotSet ); + "test and set not-set tracking = %u", + testData.m_testIterationsNotSet ); + } + { - static unsigned setVal = 5; - epicsAtomicSetUIntT ( & testData.m_testValue, setVal ); - testOk ( epicsAtomicGetUIntT ( & testData.m_testValue ) == setVal, - "unsigned version of set and get must concur" ); + static const size_t N = 10; + static char dummy; + size_t i; + TestDataTNS_PtrT testData = { 0, N, N }; + epicsAtomicSetSizeT ( & testData.m_testIterationsSet, 0u ); + testOk ( epicsAtomicGetSizeT ( & testData.m_testIterationsSet ) == 0u, + "set/get %u", testData.m_testIterationsSet ); + epicsAtomicSetSizeT ( & testData.m_testIterationsNotSet, 0u ); + testOk ( epicsAtomicGetSizeT ( & testData.m_testIterationsNotSet ) == 0u, + "SizeT set/get %u", testData.m_testIterationsNotSet ); + epicsAtomicSetPtrT ( & testData.m_testValue, & dummy ); + testOk ( epicsAtomicGetPtrT ( & testData.m_testValue ) == & dummy, + "PtrT set/get %p", testData.m_testValue ); + for ( i = 0u; i < N; i++ ) { + epicsThreadCreate ( "tns", + 50, stackSize, tns_PtrT, & testData ); } + epicsAtomicSetPtrT ( & testData.m_testValue, 0u ); + while ( testData.m_testIterationsSet < N ) { + epicsThreadSleep ( 0.01 ); + } + testOk ( epicsAtomicGetSizeT ( & testData.m_testIterationsSet ) == N, + "test and set iterations %u", + testData.m_testIterationsSet ); + testOk ( epicsAtomicGetSizeT ( & testData.m_testIterationsNotSet ) == 0u, + "test and set not-set tracking = %u", + testData.m_testIterationsNotSet ); } return testDone();