diff --git a/src/libCom/osi/compiler/clang/epicsAtomicCD.h b/src/libCom/osi/compiler/clang/epicsAtomicCD.h new file mode 100644 index 000000000..ecb3833d1 --- /dev/null +++ b/src/libCom/osi/compiler/clang/epicsAtomicCD.h @@ -0,0 +1,27 @@ + +/*************************************************************************\ +* Copyright (c) 2011 LANS LLC, as Operator of +* Los Alamos National Laboratory. +* Copyright (c) 2011 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. +\*************************************************************************/ + +/* + * Author Jeffrey O. Hill + * johill@lanl.gov + */ + +#ifndef cdAtomic_h +#define cdAtomic_h + +#if defined ( __cplusplus ) +# define OSD_ATOMIC_INLINE inline +#else +# define OSD_ATOMIC_INLINE __inline__ +#endif + +#include "osdAtomic.h" + +#endif /* cdAtomic_h */ diff --git a/src/libCom/osi/compiler/default/epicsAtomicCD.h b/src/libCom/osi/compiler/default/epicsAtomicCD.h new file mode 100644 index 000000000..a4edb81c9 --- /dev/null +++ b/src/libCom/osi/compiler/default/epicsAtomicCD.h @@ -0,0 +1,25 @@ + +/*************************************************************************\ +* Copyright (c) 2011 LANS LLC, as Operator of +* Los Alamos National Laboratory. +* Copyright (c) 2011 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. +\*************************************************************************/ + +/* + * Author Jeffrey O. Hill + * johill@lanl.gov + */ + +#ifndef cdAtomic_h +#define cdAtomic_h + +#if __STDC_VERSION__ >= 199901L || defined ( __cplusplus ) +# define OSD_ATOMIC_INLINE inline +#endif + +#include "osdAtomic.h" + +#endif /* cdAtomic_h */ diff --git a/src/libCom/osi/compiler/gcc/epicsAtomicCD.h b/src/libCom/osi/compiler/gcc/epicsAtomicCD.h new file mode 100644 index 000000000..364a86e0d --- /dev/null +++ b/src/libCom/osi/compiler/gcc/epicsAtomicCD.h @@ -0,0 +1,97 @@ + +/*************************************************************************\ +* Copyright (c) 2011 LANS LLC, as Operator of +* Los Alamos National Laboratory. +* Copyright (c) 2011 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. +\*************************************************************************/ + +/* + * Author Jeffrey O. Hill + * johill@lanl.gov + */ + +#ifndef cdAtomic_h +#define cdAtomic_h + +#ifndef __GNUC__ +# error this header is only for use with the gnu compiler +#endif + +#define OSD_ATOMIC_INLINE __inline__ + +#define GCC_ATOMIC_CONCAT( A, B ) GCC_ATOMIC_CONCATR(A,B) +#define GCC_ATOMIC_CONCATR( A, B ) A ## B + +#define GCC_ATOMIC_INTRINSICS_AVAIL_UINT_T \ + GCC_ATOMIC_CONCAT ( \ + __GCC_HAVE_SYNC_COMPARE_AND_SWAP_, \ + __SIZEOF_INT__ ) + +#define GCC_ATOMIC_INTRINSICS_AVAIL_SIZE_T \ + GCC_ATOMIC_CONCAT ( \ + __GCC_HAVE_SYNC_COMPARE_AND_SWAP_, \ + __SIZEOF_SIZE_T__ ) + +#if GCC_ATOMIC_INTRINSICS_AVAIL_UINT_T \ + && defined GCC_ATOMIC_INTRINSICS_AVAIL_SIZE_T + +#define OSD_ATOMIC_GCC + +#ifdef __cplusplus +extern "C" { +#endif + +OSD_ATOMIC_INLINE size_t epicsAtomicIncrSizeT ( size_t * pTarget ) +{ + return __sync_add_and_fetch ( pTarget, 1u ); +} + +OSD_ATOMIC_INLINE size_t epicsAtomicDecrSizeT ( size_t * pTarget ) +{ + return __sync_sub_and_fetch ( pTarget, 1u ); +} + +OSD_ATOMIC_INLINE void epicsAtomicSetSizeT ( size_t * pTarget, + size_t newValue ) +{ + *pTarget = newValue; + __sync_synchronize (); +} + +OSD_ATOMIC_INLINE void epicsAtomicSetUIntT ( unsigned * pTarget, + unsigned newValue ) +{ + *pTarget = newValue; + __sync_synchronize (); +} + +OSD_ATOMIC_INLINE size_t epicsAtomicGetSizeT ( const size_t * pTarget ) +{ + __sync_synchronize (); + return *pTarget; +} + +OSD_ATOMIC_INLINE unsigned epicsAtomicTestAndSetUIntT ( unsigned * pTarget ) +{ + const size_t prev = __sync_lock_test_and_set ( pTarget, 1u ); + return prev == 0; +} + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#else /* if GCC_ATOMIC_INTRINSICS_AVAIL */ + + /* + * not available as gcc intrinsics so we + * will employ an os specific inline solution + */ +# include "osdAtomic.h" + +#endif /* if GCC_ATOMIC_INTRINSICS_AVAIL */ + +#endif /* cdAtomic_h */ diff --git a/src/libCom/osi/compiler/msvc/epicsAtomicCD.h b/src/libCom/osi/compiler/msvc/epicsAtomicCD.h new file mode 100644 index 000000000..1353d121e --- /dev/null +++ b/src/libCom/osi/compiler/msvc/epicsAtomicCD.h @@ -0,0 +1,165 @@ + +/*************************************************************************\ +* Copyright (c) 2011 LANS LLC, as Operator of +* Los Alamos National Laboratory. +* Copyright (c) 2011 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. +\*************************************************************************/ + +/* + * Author Jeffrey O. Hill + * johill@lanl.gov + */ + +#ifndef cdAtomic_h +#define cdAtomic_h + +#include "epicsAssert.h" + +#ifndef _MSC_VER +# error this header file is only for use with with the Microsoft Compiler +#endif + +#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 ) +#pragma intrinsic ( _InterlockedCompareExchange ) +#pragma intrinsic ( _InterlockedIncrement ) +#pragma intrinsic ( _InterlockedDecrement ) +#pragma intrinsic ( _InterlockedExchange ) +#pragma intrinsic ( _InterlockedExchangeAdd ) +#if OSD_ATOMIC_64 +# pragma intrinsic ( _InterlockedIncrement64 ) +# pragma intrinsic ( _InterlockedDecrement64 ) +# pragma intrinsic ( _InterlockedExchange64 ) +# pragma intrinsic ( _InterlockedExchangeAdd64 ) +#endif + +/* + * The windows doc appears to recommend defining InterlockedExchange + * to be _InterlockedExchange to cause it to be an intrinsic, but that + * creates issues when later, in a windows os specific header, we include + * windows.h so we except some code duplication between the msvc csAtomic.h + * and win32 osdAtomic.h to avoid problems, and to keep the os specific + * windows.h header file out of the msvc cdAtomic.h + */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* necessary for next two 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 ); +} + +OSD_ATOMIC_INLINE unsigned epicsAtomicTestAndSetUIntT ( unsigned * pTarget ) +{ + long * const pTarg = ( long * ) ( pTarget ); + return _InterlockedCompareExchange ( pTarg, 1, 0 ) == 0; +} + + +#if ! OSD_ATOMIC_64 + +/* + * necessary for next four functions + * + * looking at the MS documentation it appears that they will + * keep type long the same size as an int on 64 bit builds + */ +STATIC_ASSERT ( sizeof ( long ) == sizeof ( size_t ) ); + +OSD_ATOMIC_INLINE size_t epicsAtomicIncrSizeT ( size_t * pTarget ) +{ + long * const pTarg = ( long * ) pTarget; + return _InterlockedIncrement ( pTarg ); +} + +OSD_ATOMIC_INLINE size_t epicsAtomicDecrSizeT ( size_t * pTarget ) +{ + long * const pTarg = ( long * ) ( pTarget ); + return _InterlockedDecrement ( pTarg ); +} + +OSD_ATOMIC_INLINE void epicsAtomicSetSizeT ( size_t * pTarget, size_t 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 ); +} + +#else /* ! OSD_ATOMIC_64 */ + +/* + * necessary for next four functions + */ +STATIC_ASSERT ( sizeof ( long long ) == sizeof ( size_t ) ); + +OSD_ATOMIC_INLINE size_t epicsAtomicIncrSizeT ( size_t * pTarget ) +{ + long long * const pTarg = ( long long * ) pTarget; + return _InterlockedIncrement64 ( pTarg ); +} + +OSD_ATOMIC_INLINE size_t epicsAtomicDecrSizeT ( size_t * pTarget ) +{ + long long * const pTarg = ( long long * ) ( pTarget ); + return _InterlockedDecrement64 ( pTarg ); +} + +OSD_ATOMIC_INLINE void epicsAtomicSetSizeT ( size_t * pTarget, size_t 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 ); +} + +#endif /* ! OSD_ATOMIC_64 */ + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif /* __cplusplus */ + +#else /* ifdef _MSC_EXTENSIONS */ + +#if defined ( __cplusplus ) +# define OSD_ATOMIC_INLINE inline +# include "osdAtomic.h" +#endif + +#endif /* ifdef _MSC_EXTENSIONS */ + +#endif /* cdAtomic_h */ diff --git a/src/libCom/osi/compiler/solStudio/epicsAtomicCD.h b/src/libCom/osi/compiler/solStudio/epicsAtomicCD.h new file mode 100644 index 000000000..a69bb19bb --- /dev/null +++ b/src/libCom/osi/compiler/solStudio/epicsAtomicCD.h @@ -0,0 +1,27 @@ + +/*************************************************************************\ +* Copyright (c) 2011 LANS LLC, as Operator of +* Los Alamos National Laboratory. +* Copyright (c) 2011 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. +\*************************************************************************/ + +/* + * Author Jeffrey O. Hill + * johill@lanl.gov + */ + +#ifndef cdAtomic_h +#define cdAtomic_h + +#if defined ( __cplusplus ) +# define OSD_ATOMIC_INLINE inline +#else +# define OSD_ATOMIC_INLINE __inline +#endif + +#include "osdAtomic.h" + +#endif /* cdAtomic_h */ diff --git a/src/libCom/osi/epicsAtomic.h b/src/libCom/osi/epicsAtomic.h new file mode 100644 index 000000000..c94c2de1e --- /dev/null +++ b/src/libCom/osi/epicsAtomic.h @@ -0,0 +1,99 @@ + +/*************************************************************************\ +* Copyright (c) 2011 LANS LLC, as Operator of +* Los Alamos National Laboratory. +* Copyright (c) 2011 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. +\*************************************************************************/ + +/* + * Author Jeffrey O. Hill + * johill@lanl.gov + */ + +#ifndef epicsAtomic_h +#define epicsAtomic_h + +#include /* define size_t */ + +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * lock out other smp processors from accessing the target, + * sync target in cache, add one to target, flush target in + * cache, allow other smp processors to access the target, + * return new value of target as modified by this operation + * + * increment is chosen as the primitive here because, + * compared to add, it is more likely to be implemented + * atomically on old architectures such as 68k + */ +epicsShareFunc size_t epicsAtomicIncrSizeT ( size_t * pTarget ); + +/* + * lock out other smp processors from accessing the target, + * sync target in cache, subtract one from target, flush target + * in cache, allow out other smp processors to access the target, + * return new value of target as modified by this operation + * + * decrement is chosen as the primitive here because, + * compared to subtract, it is more likely to be implemented + * atomically on old architectures such as 68k + */ +epicsShareFunc size_t epicsAtomicDecrSizeT ( size_t * pTarget ); + +/* + * set target in cache, flush target in cache + */ +epicsShareFunc void epicsAtomicSetSizeT ( size_t * pTarget, size_t newValue ); +epicsShareFunc void epicsAtomicSetUIntT ( unsigned * pTarget, unsigned newValue ); + +/* + * fetch target in cache, return new value of target + */ +epicsShareFunc size_t epicsAtomicGetSizeT ( const size_t * 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 + * + * 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 + */ +epicsShareFunc unsigned epicsAtomicTestAndSetUIntT ( unsigned * pTarget ); + +/* + * the following are, never inline and always synchronized by a global + * mutual exclusion lock, implementations of the epicsAtomicXxxx interface + * which may used to implement the epicsAtomicXxxx functions when + * more efficent primitives aren't available + */ +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 size_t epicsLockedGetSizeT ( const size_t * pTarget ); +epicsShareFunc unsigned epicsLockedTestAndSetUIntT ( unsigned * pTarget ); + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +/* + * options for inline compiler instrinsic or os specific + * implementations of the above function prototypes + */ +#include "epicsAtomicCD.h" + +#endif /* epicsAtomic_h */ diff --git a/src/libCom/osi/epicsAtomicLocked.cpp b/src/libCom/osi/epicsAtomicLocked.cpp new file mode 100644 index 000000000..b9ea22757 --- /dev/null +++ b/src/libCom/osi/epicsAtomicLocked.cpp @@ -0,0 +1,103 @@ + +/*************************************************************************\ +* Copyright (c) 2011 LANS LLC, as Operator of +* Los Alamos National Laboratory. +* Copyright (c) 2011 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. +\*************************************************************************/ + +/* + * Author Jeffrey O. Hill + * johill@lanl.gov + * + * Provide a global mutex version of the atomic functions for when + * we dont have more efficent OS primitives or compiler intriniscs + * to use instead. + * + * We implement these mutex-based primitives upon the libCom private + * interface epicsMutexOsdXxxx because, in libCom, it is convenient + * to make this a standalone primitive upon which we can implement + * epicsMutex. + */ + +#define epicsExportSharedSymbols +#include "epicsAtomic.h" +#include "epicsMutex.h" + +namespace { + +class AtomicGuard { +public: + AtomicGuard (); + ~AtomicGuard (); +private: + static epicsMutexOSD & m_mutex; +}; + +// +// see c++ FAQ, static init order fiasco +// +epicsMutexOSD & AtomicGuard :: m_mutex = * epicsMutexOsdCreate (); + +inline AtomicGuard :: AtomicGuard () +{ + const int status = epicsMutexOsdLock ( & m_mutex ); + assert ( status == epicsMutexLockOK ); +} + +inline AtomicGuard :: ~AtomicGuard () +{ + epicsMutexOsdUnlock ( & m_mutex ); +} + +} // end of anonymous namespace + +extern "C" { + +size_t epicsLockedIncrSizeT ( size_t * pTarget ) +{ + AtomicGuard atomicGuard; + return ++(*pTarget); +} + +size_t epicsLockedDecrSizeT ( size_t * pTarget ) +{ + AtomicGuard atomicGuard; + return --(*pTarget); +} + +void epicsLockedSetSizeT ( size_t * pTarget, size_t newVal ) +{ + AtomicGuard atomicGuard; + *pTarget = newVal; +} + +void epicsLockedSetUIntT ( unsigned * pTarget, unsigned newVal ) +{ + AtomicGuard atomicGuard; + *pTarget = newVal; +} + +size_t epicsLockedGetSizeT ( const size_t * pTarget ) +{ + AtomicGuard atomicGuard; + return *pTarget; +} + +unsigned epicsLockedTestAndSetUIntT ( unsigned * pTarget ) +{ + AtomicGuard atomicGuard; + const bool weWillSetIt = ( *pTarget == 0u ); + if ( weWillSetIt ) { + *pTarget = 1u; + } + return weWillSetIt; +} + +} // end of extern "C" + + + + diff --git a/src/libCom/osi/epicsAtomicLocked.h b/src/libCom/osi/epicsAtomicLocked.h new file mode 100644 index 000000000..0aec9c8b5 --- /dev/null +++ b/src/libCom/osi/epicsAtomicLocked.h @@ -0,0 +1,72 @@ + +/*************************************************************************\ +* Copyright (c) 2011 LANS LLC, as Operator of +* Los Alamos National Laboratory. +* Copyright (c) 2011 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. +\*************************************************************************/ + +/* + * Author Jeffrey O. Hill + * johill@lanl.gov + */ + +#ifndef epicsAtomicLocked_h +#define epicsAtomicLocked_h + +#if defined ( OSD_ATOMIC_INLINE ) + +#ifdef __cplusplus +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 ); +} + +OSD_ATOMIC_INLINE size_t epicsAtomicDecrSizeT ( size_t * pTarget ) +{ + return epicsLockedDecrSizeT ( pTarget ); +} + +OSD_ATOMIC_INLINE void epicsAtomicSetSizeT ( size_t * pTarget, size_t newVal ) +{ + epicsLockedSetSizeT ( pTarget, newVal ); +} + +OSD_ATOMIC_INLINE size_t epicsAtomicGetSizeT ( const size_t * pTarget ) +{ + return epicsLockedGetSizeT ( pTarget ); +} + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif /* __cplusplus */ + +#else /* if defined ( OSD_ATOMIC_INLINE ) */ + +# define epicsAtomicIncrSizeT epicsLockedIncrSizeT +# define epicsAtomicDecrSizeT epicsLockedDecrSizeT +# define epicsAtomicSetSizeT epicsLockedSetSizeT +# define epicsAtomicSetUIntT epicsLockedSetUIntT +# define epicsAtomicGetSizeT epicsLockedGetSizeT +# define epicsAtomicTestAndSetUIntT epicsLockedTestAndSetUIntT + +#endif /* if defined ( OSD_ATOMIC_INLINE ) */ + +#endif /* epicsAtomicLocked_h */ + + diff --git a/src/libCom/osi/epicsAtomicOSD.cpp b/src/libCom/osi/epicsAtomicOSD.cpp new file mode 100644 index 000000000..a1b2ff4f5 --- /dev/null +++ b/src/libCom/osi/epicsAtomicOSD.cpp @@ -0,0 +1,45 @@ + +/*************************************************************************\ +* Copyright (c) 2011 LANS LLC, as Operator of +* Los Alamos National Laboratory. +* Copyright (c) 2011 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. +\*************************************************************************/ + +/* + * Author Jeffrey O. Hill + * johill@lanl.gov + */ + + #include "epicsAtomic.h" + + /* + * OSD_ATOMIC_INLINE + * defined: define atomic functions in osdAtomic.h + * undefined: dont define atomic functions in osdAtomic.h + * + * OSD_ATOMIC_INLINE is typically set by the compiler specific file cdAtomic.h + * + * when we discover here that there is no implementation, and we guess that + * there isnt an inline keyword in the compiler then we define OSD_ATOMIC_INLINE + * empty and include osdAtomic.h to force an out of line implementation + * + * My first inclination was to place only one file of this type under os/default, + * but then this source file always includes default/osdAtomic.h, which isnt correct + * if a more specific os/xxx/osdAtromic.h exists, and so rather than duplicate an + * identical file in all of the os directories (that will get rediculous if there + * is ever a posix interface for the atomics) I moved it here to the osi directory. + * Another option would be to not make tthe current location be the first search + * in the gnu make VPATH, but I am somewhat nervous about changing something that + * fundamental in the build system. + * + * this behavior will work most of the time by defualt but it can always be + * replaced by an os specific implementation, which might be required if + * some of the functions are inline and some are not + */ + #ifndef OSD_ATOMIC_INLINE + # include "osdAtomic.h" + #endif + diff --git a/src/libCom/osi/os/WIN32/epicsAtomicOSD.h b/src/libCom/osi/os/WIN32/epicsAtomicOSD.h new file mode 100644 index 000000000..7ea6385d4 --- /dev/null +++ b/src/libCom/osi/os/WIN32/epicsAtomicOSD.h @@ -0,0 +1,130 @@ + +/*************************************************************************\ +* Copyright (c) 2011 LANS LLC, as Operator of +* Los Alamos National Laboratory. +* Copyright (c) 2011 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. +\*************************************************************************/ + +/* + * Author Jeffrey O. Hill + * johill@lanl.gov + */ + +#ifndef osdAtomic_h +#define osdAtomic_h + +#if defined ( OSD_ATOMIC_INLINE ) + +#include + +#define __STDC_LIMIT_MACROS /* define SIZE_MAX for c++ */ +#include + +#include "epicsAssert.h" + +#define STRICT +#define VC_EXTRALEAN +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* necessary for next two 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 ); +} + +OSD_ATOMIC_INLINE unsigned epicsAtomicTestAndSetUIntT ( unsigned * pTarget ) +{ + long * const pTarg = ( LONG * ) ( pTarget ); + return InterlockedCompareExchange ( pTarg, 1, 0 ) == 0; +} + +/* + * 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 + +/* + * necessary for next four functions + * + * looking at the MS documentation it appears that they will + * keep type long the same size as an int on 64 bit builds + */ +STATIC_ASSERT ( sizeof ( LONG ) == sizeof ( size_t ) ); + +OSD_ATOMIC_INLINE size_t epicsAtomicIncrSizeT ( size_t * pTarget ) +{ + LONG * const pTarg = ( LONG * ) pTarget; + return InterlockedIncrement ( pTarg ); +} + +OSD_ATOMIC_INLINE size_t epicsAtomicDecrSizeT ( size_t * pTarget ) +{ + LONG * const pTarg = ( LONG * ) ( pTarget ); + return InterlockedDecrement ( pTarg ); +} + +OSD_ATOMIC_INLINE void epicsAtomicSetSizeT ( size_t * pTarget, size_t 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 ); +} + +#else /* SIZE_MAX == WIN32_LONG_MAX */ + +/* + * necessary for next four functions + */ +STATIC_ASSERT ( sizeof ( LONGLONG ) == sizeof ( size_t ) ); + +OSD_ATOMIC_INLINE size_t epicsAtomicIncrSizeT ( size_t * pTarget ) +{ + LONGLONG * const pTarg = ( LONGLONG * ) pTarget; + return InterlockedIncrement64 ( pTarg ); +} + +OSD_ATOMIC_INLINE size_t epicsAtomicDecrSizeT ( size_t * pTarget ) +{ + LONGLONG * const pTarg = ( LONGLONG * ) ( pTarget ); + return InterlockedDecrement64 ( pTarg ); +} + +OSD_ATOMIC_INLINE void epicsAtomicSetSizeT ( size_t * pTarget, size_t newVal ) +{ + LONGLONG * const pTarg = ( LONGLONG * ) ( pTarget ); + InterlockedExchange64 ( pTarg, ( LONGLONG ) newVal ); +} + +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 */ + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif /* __cplusplus */ + +#endif /* if defined ( OSD_ATOMIC_INLINE ) */ + +#endif /* ifndef osdAtomic_h */ diff --git a/src/libCom/osi/os/default/epicsAtomicOSD.h b/src/libCom/osi/os/default/epicsAtomicOSD.h new file mode 100644 index 000000000..f8033d9d5 --- /dev/null +++ b/src/libCom/osi/os/default/epicsAtomicOSD.h @@ -0,0 +1,21 @@ + +/*************************************************************************\ +* Copyright (c) 2011 LANS LLC, as Operator of +* Los Alamos National Laboratory. +* Copyright (c) 2011 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. +\*************************************************************************/ + +/* + * Author Jeffrey O. Hill + * johill@lanl.gov + */ + +#ifndef osdAtomic +#define osdAtomic + +#include "epicsAtomicLocked.h" + +#endif /* osdAtomic */ diff --git a/src/libCom/osi/os/solaris/epicsAtomicOSD.h b/src/libCom/osi/os/solaris/epicsAtomicOSD.h new file mode 100644 index 000000000..b2521848d --- /dev/null +++ b/src/libCom/osi/os/solaris/epicsAtomicOSD.h @@ -0,0 +1,105 @@ + +/*************************************************************************\ +* Copyright (c) 2011 LANS LLC, as Operator of +* Los Alamos National Laboratory. +* Copyright (c) 2011 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. +\*************************************************************************/ + +/* + * Author Jeffrey O. Hill + * johill@lanl.gov + */ + +#ifndef osdAtomic_h +#define osdAtomic_h + +#if defined ( OSD_ATOMIC_INLINE ) + +/* + * atomic.h exists only in Solaris 10 or higher + */ +#if defined ( __SunOS_5_10 ) + +#include +#include + +#define __STDC_LIMIT_MACROS /* define SIZE_MAX for c++ */ +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +OSD_ATOMIC_INLINE unsigned epicsAtomicTestAndSetUIntT ( unsigned * pTarget ) +{ + const uchar_t oldVal = atomic_cas_uint ( pTarget, 0u, 1u ); + return oldVal == 0u; +} + +OSD_ATOMIC_INLINE void epicsAtomicSetUIntT ( unsigned * pTarget, unsigned newVal ) +{ + atomic_swap_uint ( pTarget, newVal ); +} + +#if SIZE_MAX == UINT_MAX + +OSD_ATOMIC_INLINE size_t epicsAtomicIncrSizeT ( size_t * pTarget ) +{ + return atomic_inc_uint_nv ( pTarget ); +} + +OSD_ATOMIC_INLINE size_t epicsAtomicDecrSizeT ( size_t * pTarget ) +{ + return atomic_dec_uint_nv ( pTarget ); +} + +OSD_ATOMIC_INLINE void epicsAtomicSetSizeT ( size_t * pTarget, size_t newVal ) +{ + atomic_swap_uint ( pTarget, newVal ); +} + +OSD_ATOMIC_INLINE size_t epicsAtomicGetSizeT ( const size_t * pTarget ) +{ + return atomic_or_uint_nv ( pTarget, 0U ); +} + +#else /* SIZE_MAX == UINT_MAX */ + +OSD_ATOMIC_INLINE size_t epicsAtomicIncrSizeT ( size_t * pTarget ) +{ + return atomic_inc_ulong_nv ( pTarget ); +} + +OSD_ATOMIC_INLINE size_t epicsAtomicDecrSizeT ( size_t * pTarget ) +{ + return atomic_dec_ulong_nv ( pTarget ); +} + +OSD_ATOMIC_INLINE void epicsAtomicSetSizeT ( size_t * pTarget, size_t newVal ) +{ + atomic_swap_ulong ( pTarget, newval ); +} + +OSD_ATOMIC_INLINE size_t epicsAtomicGetSizeT ( const size_t * pTarget ) +{ + return atomic_or_ulong_nv ( pTarget, 0U ); +} + +#endif /* SIZE_MAX == UINT_MAX */ + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif /* __cplusplus */ + +#else /* ifdef __SunOS_5_10 */ + +#include "epicsAtomicLocked.h" + +#endif /* ifdef __SunOS_5_10 */ + +#endif /* if defined ( OSD_ATOMIC_INLINE ) */ + +#endif /* osdAtomic_h */ diff --git a/src/libCom/osi/os/vxWorks/epicsAtomicOSD.h b/src/libCom/osi/os/vxWorks/epicsAtomicOSD.h new file mode 100644 index 000000000..8b8abd900 --- /dev/null +++ b/src/libCom/osi/os/vxWorks/epicsAtomicOSD.h @@ -0,0 +1,211 @@ +/*************************************************************************\ +* Copyright (c) 2011 LANS LLC, as Operator of +* Los Alamos National Laboratory. +* Copyright (c) 2011 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. +\*************************************************************************/ + +/* + * Author Jeffrey O. Hill + * johill@lanl.gov + */ + +#ifndef osdAtomic_h +#define osdAtomic_h + +#if defined ( OSD_ATOMIC_INLINE ) + +#include "vxWorks.h" /* obtain the version of vxWorks */ + +/* + * With vxWorks 6.6 and later we need to use vxAtomicLib + * to implement this functionality correctly on SMP systems + */ +#if _WRS_VXWORKS_MAJOR * 100 + _WRS_VXWORKS_MINOR >= 606 + +#define __STDC_LIMIT_MACROS /* define SIZE_MAX for c++ */ +#include + +#include +#include +#include "epicsAssert.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* + * we make the probably correct guess that if SIZE_MAX + * is the same as UINT_MAX then sizeof ( atomic_t ) + * will be the same as sizeof ( size_t ) + * + * if SIZE_MAX != UINT_MAX then its 64 bit vxWorks and + * WRS doesnt not supply at this time the atomic interface + * for 8 byte integers that is needed - so that architecture + * receives the lock synchronized version + */ +#if SIZE_MAX == UINT_MAX + +STATIC_ASSERT ( sizeof ( atomic_t ) == sizeof ( size_t ) ); + +OSD_ATOMIC_INLINE size_t epicsAtomicIncrSizeT ( size_t * pTarget ) +{ + /* + * vxAtomicLib doc indicates that vxAtomicInc is + * implemented using unsigned arithmetic + */ + atomic_t * const pTarg = ( atomic_t * ) ( pTarget ); + const atomic_t oldVal = vxAtomicInc ( pTarg ) + 1; + return ( ( size_t ) ( oldVal ) ) + 1u; +} + +OSD_ATOMIC_INLINE size_t epicsAtomicDecrSizeT ( size_t * pTarget ) +{ + /* + * vxAtomicLib doc indicates that vxAtomicDec is + * implemented using unsigned arithmetic + */ + atomic_t * const pTarg = ( atomic_t * ) ( pTarget ); + const atomic_t oldVal = vxAtomicDec ( pTarg ) - 1; + return ( ( size_t ) ( oldVal ) ) - 1u; +} + +OSD_ATOMIC_INLINE void epicsAtomicSetSizeT ( size_t * pTarget, size_t 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 */ + +/* + * if its 64 bit vxWorks and the compiler doesnt + * have an intrinsic then maybe there isnt any way to + * implement these without using a global lock because + * size_t is bigger than atomic_t + */ +OSD_ATOMIC_INLINE size_t epicsAtomicIncrSizeT ( size_t * pTarget ) +{ + return epicsLockedIncrSizeT ( pTarget ); +} + +OSD_ATOMIC_INLINE size_t epicsAtomicDecrSizeT ( size_t * pTarget ) +{ + return epicsLockedDecrSizeT ( pTarget ); +} + +OSD_ATOMIC_INLINE void epicsAtomicSetSizeT ( size_t * pTarget, size_t newVal ) +{ + epicsLockedSetSizeT ( pTarget, newVal ); +} + +OSD_ATOMIC_INLINE size_t epicsAtomicGetSizeT ( const size_t * pTarget ) +{ + return epicsLockedGetSizeT ( pTarget ); +} + +#endif /* SIZE_MAX == UINT_MAX */ + +STATIC_ASSERT ( sizeof ( atomic_t ) == sizeof ( unsigned ) ); + +OSD_ATOMIC_INLINE void epicsAtomicSetUIntT ( unsigned * pTarget, unsigned newVal ) +{ + atomic_t * const pTarg = ( atomic_t * ) ( pTarget ); + 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 ( pTarget, 0, 1 ) != 0; +} + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif /* __cplusplus */ + +#else /* _WRS_VXWORKS_MAJOR * 100 + _WRS_VXWORKS_MINOR >= 606 */ + +#include "intLib.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +OSD_ATOMIC_INLINE size_t epicsAtomicIncrSizeT ( size_t * pTarget ) +{ + /* + * no need for memory barrior since this + * is a single cpu system. + */ + const int key = intLock (); + const size_t result = ++(*pTarget); + intUnlock ( key ); + return result; +} + +OSD_ATOMIC_INLINE size_t epicsAtomicDecrSizeT ( size_t * pTarget ) +{ + /* + * no need for memory barrior since this + * is a single cpu system + */ + const int key = intLock (); + const size_t result = --(*pTarget); + intUnlock ( key ); + return result; +} + +OSD_ATOMIC_INLINE void epicsAtomicSetSizeT ( size_t * pTarget, size_t newVal ) +{ + /* + * no need for memory barrior since this + * is a single cpu system + */ + *pTarget = newVal; +} + +OSD_ATOMIC_INLINE void epicsAtomicSetUIntT ( unsigned * pTarget, unsigned 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 ) +{ + /* + * no need for memory barrior since this + * is a single cpu system + */ + return *pTarget; +} + +OSD_ATOMIC_INLINE unsigned epicsAtomicTestAndSetUIntT ( unsigned * pTarget ) +{ + STATIC_ASSERT ( sizeof ( int ) == sizeof ( unsigned ) ); + int * const pTarg = ( int * ) ( pTarget ); + const BOOL weSetIt = vxTas ( pTarg, 0, 1 ); + return weSetIt != 0; +} + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif /* __cplusplus */ + +#endif /* _WRS_VXWORKS_MAJOR * 100 + _WRS_VXWORKS_MINOR >= 606 */ + +#endif /* if defined ( OSD_ATOMIC_INLINE ) */ + +#endif /* osdAtomic_h */ diff --git a/src/libCom/test/epicsAtomicPerform.cpp b/src/libCom/test/epicsAtomicPerform.cpp new file mode 100644 index 000000000..464c20afe --- /dev/null +++ b/src/libCom/test/epicsAtomicPerform.cpp @@ -0,0 +1,499 @@ + +#include +#include + +#include "epicsInterrupt.h" +#include "epicsAtomic.h" +#include "epicsTime.h" +#include "epicsUnitTest.h" +#include "testMain.h" + +using std :: size_t; + +class RefCtr { +public: + RefCtr (); + ~RefCtr (); + void reference (); + void unreference (); +private: + size_t m_cnt; +}; + +class Ownership { +public: + Ownership (); + Ownership ( RefCtr & refCtr ); + Ownership ( const Ownership & ); + ~Ownership (); + Ownership & operator = ( const Ownership & ); +private: + RefCtr * _pRefCtr; + static RefCtr m_noOwnership; +}; + +inline RefCtr :: RefCtr () +{ + epicsAtomicSetSizeT ( & m_cnt, 0 ); +} + +inline RefCtr :: ~RefCtr () +{ + unsigned cnt = epicsAtomicGetSizeT ( & m_cnt ); + assert ( cnt == 0u ); +} + +inline void RefCtr :: reference () +{ + epicsAtomicIncrSizeT ( & m_cnt ); +} + +inline void RefCtr :: unreference () +{ + epicsAtomicDecrSizeT ( & m_cnt ); +} + +RefCtr Ownership :: m_noOwnership; + +inline Ownership :: Ownership () : + _pRefCtr ( & m_noOwnership ) +{ + m_noOwnership.reference (); +} + +inline Ownership :: Ownership ( RefCtr & refCtr ) : + _pRefCtr ( & refCtr ) +{ + refCtr.reference (); +} + +inline Ownership :: Ownership ( const Ownership & ownership ) : + _pRefCtr ( ownership._pRefCtr ) +{ + _pRefCtr->reference (); +} + +inline Ownership :: ~Ownership () +{ + _pRefCtr->unreference (); +} + +inline Ownership & Ownership :: + operator = ( const Ownership & ownership ) +{ + RefCtr * const pOldRefCtr = _pRefCtr; + _pRefCtr = ownership._pRefCtr; + _pRefCtr->reference (); + pOldRefCtr->unreference (); + return *this; +} + +inline Ownership retOwnership ( const Ownership & ownership ) +{ + return Ownership ( ownership ); +} + +inline Ownership recurRetOwner10 ( const Ownership & ownershipIn ) +{ + Ownership ownership = + retOwnership ( + retOwnership ( + retOwnership ( + retOwnership ( + retOwnership ( ownershipIn ) ) ) ) ); + return retOwnership ( + retOwnership ( + retOwnership ( + retOwnership ( + retOwnership ( ownership ) ) ) ) ); +} + +inline Ownership recurRetOwner100 ( const Ownership & ownershipIn ) +{ + Ownership ownership = + recurRetOwner10 ( + recurRetOwner10 ( + recurRetOwner10 ( + recurRetOwner10 ( + recurRetOwner10 ( ownershipIn ) ) ) ) ); + return recurRetOwner10 ( + recurRetOwner10 ( + recurRetOwner10 ( + recurRetOwner10 ( + recurRetOwner10 ( ownership ) ) ) ) ); +} + +inline Ownership recurRetOwner1000 ( const Ownership & ownershipIn ) +{ + Ownership ownership = + recurRetOwner100 ( + recurRetOwner100 ( + recurRetOwner100 ( + recurRetOwner100 ( + recurRetOwner100 ( ownershipIn ) ) ) ) ); + return recurRetOwner100 ( + recurRetOwner100 ( + recurRetOwner100 ( + recurRetOwner100 ( + recurRetOwner100 ( ownership ) ) ) ) ); +} + +inline void passRefOwnership ( const Ownership & ownershipIn, Ownership & ownershipOut ) +{ + ownershipOut = ownershipIn; +} + +inline void passRefOwnership10 ( const Ownership & ownershipIn, Ownership & ownershipOut ) +{ + Ownership ownershipTmp0; + passRefOwnership ( ownershipIn, ownershipTmp0 ); + Ownership ownershipTmp1; + passRefOwnership ( ownershipTmp0, ownershipTmp1 ); + Ownership ownershipTmp2; + passRefOwnership ( ownershipTmp1, ownershipTmp2 ); + Ownership ownershipTmp3; + passRefOwnership ( ownershipTmp2, ownershipTmp3 ); + Ownership ownershipTmp4; + passRefOwnership ( ownershipTmp3, ownershipTmp4 ); + Ownership ownershipTmp5; + passRefOwnership ( ownershipTmp4, ownershipTmp5 ); + Ownership ownershipTmp6; + passRefOwnership ( ownershipTmp5, ownershipTmp6 ); + Ownership ownershipTmp7; + passRefOwnership ( ownershipTmp6, ownershipTmp7 ); + Ownership ownershipTmp8; + passRefOwnership ( ownershipTmp7, ownershipTmp8 ); + passRefOwnership ( ownershipTmp8, ownershipOut ); +} + +inline void passRefOwnership100 ( const Ownership & ownershipIn, Ownership & ownershipOut ) +{ + Ownership ownershipTmp0; + passRefOwnership10 ( ownershipIn, ownershipTmp0 ); + Ownership ownershipTmp1; + passRefOwnership10 ( ownershipTmp0, ownershipTmp1 ); + Ownership ownershipTmp2; + passRefOwnership10 ( ownershipTmp1, ownershipTmp2 ); + Ownership ownershipTmp3; + passRefOwnership10 ( ownershipTmp2, ownershipTmp3 ); + Ownership ownershipTmp4; + passRefOwnership10 ( ownershipTmp3, ownershipTmp4 ); + Ownership ownershipTmp5; + passRefOwnership10 ( ownershipTmp4, ownershipTmp5 ); + Ownership ownershipTmp6; + passRefOwnership10 ( ownershipTmp5, ownershipTmp6 ); + Ownership ownershipTmp7; + passRefOwnership10 ( ownershipTmp6, ownershipTmp7 ); + Ownership ownershipTmp8; + passRefOwnership10 ( ownershipTmp7, ownershipTmp8 ); + passRefOwnership10 ( ownershipTmp8, ownershipOut ); +} + +inline void passRefOwnership1000 ( const Ownership & ownershipIn, Ownership & ownershipOut ) +{ + Ownership ownershipTmp0; + passRefOwnership100 ( ownershipIn, ownershipTmp0 ); + Ownership ownershipTmp1; + passRefOwnership100 ( ownershipTmp0, ownershipTmp1 ); + Ownership ownershipTmp2; + passRefOwnership100 ( ownershipTmp1, ownershipTmp2 ); + Ownership ownershipTmp3; + passRefOwnership100 ( ownershipTmp2, ownershipTmp3 ); + Ownership ownershipTmp4; + passRefOwnership100 ( ownershipTmp3, ownershipTmp4 ); + Ownership ownershipTmp5; + passRefOwnership100 ( ownershipTmp4, ownershipTmp5 ); + Ownership ownershipTmp6; + passRefOwnership100 ( ownershipTmp5, ownershipTmp6 ); + Ownership ownershipTmp7; + passRefOwnership100 ( ownershipTmp6, ownershipTmp7 ); + Ownership ownershipTmp8; + passRefOwnership100 ( ownershipTmp7, ownershipTmp8 ); + passRefOwnership100 ( ownershipTmp8, ownershipOut ); +} + +time_t extTime = 0; + +// tests the time it takes to perform a call to an external +// function and also increment an integer word. The +// epicsInterruptIsInterruptContext function is an +// out-of-line function implemented in a sharable library +// so hopefully it wont be optimized away. +inline void tenOrdinaryIncr ( size_t & target ) +{ + int result = 0; + result += epicsInterruptIsInterruptContext (); + result += epicsInterruptIsInterruptContext (); + result += epicsInterruptIsInterruptContext (); + result += epicsInterruptIsInterruptContext (); + result += epicsInterruptIsInterruptContext (); + result += epicsInterruptIsInterruptContext (); + result += epicsInterruptIsInterruptContext (); + result += epicsInterruptIsInterruptContext (); + result += epicsInterruptIsInterruptContext (); + result += epicsInterruptIsInterruptContext (); + target = static_cast < unsigned > ( result ); +} + +inline void oneHundredOrdinaryIncr ( size_t & target ) +{ + tenOrdinaryIncr ( target ); + tenOrdinaryIncr ( target ); + tenOrdinaryIncr ( target ); + tenOrdinaryIncr ( target ); + tenOrdinaryIncr ( target ); + tenOrdinaryIncr ( target ); + tenOrdinaryIncr ( target ); + tenOrdinaryIncr ( target ); + tenOrdinaryIncr ( target ); + tenOrdinaryIncr ( target ); +} + +inline void oneThousandOrdinaryIncr ( size_t & target ) +{ + oneHundredOrdinaryIncr ( target ); + oneHundredOrdinaryIncr ( target ); + oneHundredOrdinaryIncr ( target ); + oneHundredOrdinaryIncr ( target ); + oneHundredOrdinaryIncr ( target ); + oneHundredOrdinaryIncr ( target ); + oneHundredOrdinaryIncr ( target ); + oneHundredOrdinaryIncr ( target ); + oneHundredOrdinaryIncr ( target ); + oneHundredOrdinaryIncr ( target ); +} + +inline void tenAtomicIncr ( size_t & target ) +{ + epicsAtomicIncrSizeT ( & target ); + epicsAtomicIncrSizeT ( & target ); + epicsAtomicIncrSizeT ( & target ); + epicsAtomicIncrSizeT ( & target ); + epicsAtomicIncrSizeT ( & target ); + epicsAtomicIncrSizeT ( & target ); + epicsAtomicIncrSizeT ( & target ); + epicsAtomicIncrSizeT ( & target ); + epicsAtomicIncrSizeT ( & target ); + epicsAtomicIncrSizeT ( & target ); +} + +inline void oneHundredAtomicIncr ( size_t & target ) +{ + tenAtomicIncr ( target ); + tenAtomicIncr ( target ); + tenAtomicIncr ( target ); + tenAtomicIncr ( target ); + tenAtomicIncr ( target ); + tenAtomicIncr ( target ); + tenAtomicIncr ( target ); + tenAtomicIncr ( target ); + tenAtomicIncr ( target ); + tenAtomicIncr ( target ); +} + +inline void oneThousandAtomicIncr ( size_t & target ) +{ + oneHundredAtomicIncr ( target ); + oneHundredAtomicIncr ( target ); + oneHundredAtomicIncr ( target ); + oneHundredAtomicIncr ( target ); + oneHundredAtomicIncr ( target ); + oneHundredAtomicIncr ( target ); + oneHundredAtomicIncr ( target ); + oneHundredAtomicIncr ( target ); + oneHundredAtomicIncr ( target ); + oneHundredAtomicIncr ( target ); +} + +inline void tenAtomicTestAndSet ( unsigned & target ) +{ + epicsAtomicTestAndSetUIntT ( & target ); + epicsAtomicTestAndSetUIntT ( & target ); + epicsAtomicTestAndSetUIntT ( & target ); + epicsAtomicTestAndSetUIntT ( & target ); + epicsAtomicTestAndSetUIntT ( & target ); + epicsAtomicTestAndSetUIntT ( & target ); + epicsAtomicTestAndSetUIntT ( & target ); + epicsAtomicTestAndSetUIntT ( & target ); + epicsAtomicTestAndSetUIntT ( & target ); + epicsAtomicTestAndSetUIntT ( & target ); +} + +inline void oneHundredAtomicTestAndSet ( unsigned & target ) +{ + tenAtomicTestAndSet ( target ); + tenAtomicTestAndSet ( target ); + tenAtomicTestAndSet ( target ); + tenAtomicTestAndSet ( target ); + tenAtomicTestAndSet ( target ); + tenAtomicTestAndSet ( target ); + tenAtomicTestAndSet ( target ); + tenAtomicTestAndSet ( target ); + tenAtomicTestAndSet ( target ); + tenAtomicTestAndSet ( target ); +} + +inline void oneThousandAtomicTestAndSet ( unsigned & target ) +{ + oneHundredAtomicTestAndSet ( target ); + oneHundredAtomicTestAndSet ( target ); + oneHundredAtomicTestAndSet ( target ); + oneHundredAtomicTestAndSet ( target ); + oneHundredAtomicTestAndSet ( target ); + oneHundredAtomicTestAndSet ( target ); + oneHundredAtomicTestAndSet ( target ); + oneHundredAtomicTestAndSet ( target ); + oneHundredAtomicTestAndSet ( target ); + oneHundredAtomicTestAndSet ( target ); +} + +inline void tenAtomicSet ( size_t & target ) +{ + epicsAtomicSetSizeT ( & target, 0 ); + epicsAtomicSetSizeT ( & target, 0 ); + epicsAtomicSetSizeT ( & target, 0 ); + epicsAtomicSetSizeT ( & target, 0 ); + epicsAtomicSetSizeT ( & target, 0 ); + epicsAtomicSetSizeT ( & target, 0 ); + epicsAtomicSetSizeT ( & target, 0 ); + epicsAtomicSetSizeT ( & target, 0 ); + epicsAtomicSetSizeT ( & target, 0 ); + epicsAtomicSetSizeT ( & target, 0 ); +} + +inline void oneHundredAtomicSet ( size_t & target ) +{ + tenAtomicSet ( target ); + tenAtomicSet ( target ); + tenAtomicSet ( target ); + tenAtomicSet ( target ); + tenAtomicSet ( target ); + tenAtomicSet ( target ); + tenAtomicSet ( target ); + tenAtomicSet ( target ); + tenAtomicSet ( target ); + tenAtomicSet ( target ); +} + +inline void oneThousandAtomicSet ( size_t & target ) +{ + oneHundredAtomicSet ( target ); + oneHundredAtomicSet ( target ); + oneHundredAtomicSet ( target ); + oneHundredAtomicSet ( target ); + oneHundredAtomicSet ( target ); + oneHundredAtomicSet ( target ); + oneHundredAtomicSet ( target ); + oneHundredAtomicSet ( target ); + oneHundredAtomicSet ( target ); + oneHundredAtomicSet ( target ); +} + +static const unsigned N = 10000; + +void ordinaryIncrPerformance () +{ + epicsTime begin = epicsTime::getCurrent (); + size_t target; + epicsAtomicSetSizeT ( & target, 0 ); + for ( unsigned i = 0; i < N; i++ ) { + oneThousandOrdinaryIncr ( target ); + } + double delay = epicsTime::getCurrent () - begin; + testOk1 ( target == 0u ); + delay /= N * 1000u; // convert to delay per call + delay *= 1e6; // convert to micro seconds + testDiag ( "raw incr and a NOOP function call takes %f microseconds", delay ); +} + +void epicsAtomicIncrPerformance () +{ + epicsTime begin = epicsTime::getCurrent (); + size_t target; + epicsAtomicSetSizeT ( & target, 0 ); + for ( unsigned i = 0; i < N; i++ ) { + oneThousandAtomicIncr ( target ); + } + double delay = epicsTime::getCurrent () - begin; + testOk1 ( target == N * 1000u ); + delay /= N * 1000u; // convert to delay per call + delay *= 1e6; // convert to micro seconds + testDiag ( "epicsAtomicIncr() takes %f microseconds", delay ); +} + +void atomicCompareAndSetPerformance () +{ + epicsTime begin = epicsTime::getCurrent (); + size_t target; + epicsAtomicSetSizeT ( & target, 0 ); + testOk1 ( ! target ); + for ( unsigned i = 0; i < N; i++ ) { + oneThousandAtomicTestAndSet ( 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 ); +} + +void recursiveOwnershipRetPerformance () +{ + RefCtr refCtr; + epicsTime begin = epicsTime::getCurrent (); + for ( unsigned i = 0; i < N; i++ ) { + Ownership ownership ( refCtr ); + recurRetOwner1000 ( ownership ); + } + double delay = epicsTime::getCurrent () - begin; + delay /= N * 1000u; // convert to delay per call + delay *= 1e6; // convert to micro seconds + testDiag ( "retOwnership() takes %f microseconds", delay ); +} + +void ownershipPassRefPerformance () +{ + RefCtr refCtr; + epicsTime begin = epicsTime::getCurrent (); + for ( unsigned i = 0; i < N; i++ ) { + Ownership ownershipSrc ( refCtr ); + Ownership ownershipDest; + passRefOwnership1000 ( ownershipSrc, ownershipDest ); + } + double delay = epicsTime::getCurrent () - begin; + delay /= N * 1000u; // convert to delay per call + delay *= 1e6; // convert to micro seconds + testDiag ( "passRefOwnership() takes %f microseconds", delay ); +} + +void epicsAtomicSetPerformance () +{ + epicsTime begin = epicsTime::getCurrent (); + unsigned target; + for ( unsigned i = 0; i < N; i++ ) { + oneThousandAtomicSet ( target ); + } + double delay = epicsTime::getCurrent () - begin; + testOk1 ( target == 0u ); + delay /= N * 1000u; // convert to delay per call + delay *= 1e6; // convert to micro seconds + testDiag ( "epicsAtomicSet() takes %f microseconds", delay ); +} + +MAIN(osiAtomicTest) +{ + testPlan(5); + // + // The tests running here are measuring fast + // functions so they tend to be impacted + // by where the cache lines are wrt to the + // virtual pages perhaps + // + epicsAtomicSetPerformance (); + ordinaryIncrPerformance (); + epicsAtomicIncrPerformance (); + recursiveOwnershipRetPerformance (); + ownershipPassRefPerformance (); + atomicCompareAndSetPerformance (); + return testDone(); +} diff --git a/src/libCom/test/epicsAtomicTest.c b/src/libCom/test/epicsAtomicTest.c new file mode 100644 index 000000000..552e38aa6 --- /dev/null +++ b/src/libCom/test/epicsAtomicTest.c @@ -0,0 +1,117 @@ + +#include +#include + +#include "epicsAtomic.h" +#include "epicsTime.h" +#include "epicsThread.h" +#include "epicsUnitTest.h" +#include "testMain.h" + +typedef struct TestDataIncrDecr { + size_t m_testValue; + size_t m_testIterations; +} TestDataIncrDecr; + +static void incr ( void *arg ) +{ + TestDataIncrDecr * const pTestData = (TestDataIncrDecr *) arg; + epicsAtomicIncrSizeT ( & pTestData->m_testValue ); + epicsAtomicIncrSizeT ( & pTestData->m_testIterations ); +} + +static void decr ( void *arg ) +{ + TestDataIncrDecr * const pTestData = (TestDataIncrDecr *) arg; + epicsAtomicDecrSizeT ( & pTestData->m_testValue ); + epicsAtomicIncrSizeT ( & pTestData->m_testIterations ); +} + +typedef struct TestDataTNS { + unsigned m_testValue; + size_t m_testIterationsSet; + size_t m_testIterationsNotSet; +} TestDataTNS; + +int isModulo ( size_t N, size_t n ) +{ + return ( n % N ) == 0u; +} + +static void tns ( void *arg ) +{ + TestDataTNS * const pTestData = (TestDataTNS *) arg; + /* + * intentionally waste cpu and maximize + * contention for the shared data + */ + epicsAtomicIncrSizeT ( & pTestData->m_testIterationsNotSet ); + while ( ! epicsAtomicTestAndSetUIntT ( & pTestData->m_testValue ) ) { + } + epicsAtomicDecrSizeT ( & pTestData->m_testIterationsNotSet ); + epicsAtomicSetUIntT ( & pTestData->m_testValue, 0u ); + epicsAtomicIncrSizeT ( & pTestData->m_testIterationsSet ); +} + +MAIN(osiAtomicTest) +{ + static const size_t N = 1000; + const unsigned int stackSize = + epicsThreadGetStackSize ( epicsThreadStackSmall ); + + testPlan(8); + + { + size_t i; + 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; i++ ) { + epicsThreadCreate ( "incr", + 50, stackSize, incr, & testData ); + epicsThreadCreate ( "decr", + 50, stackSize, decr, & testData ); + } + while ( testData.m_testIterations < 2 * N ) { + epicsThreadSleep ( 0.01 ); + } + 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 ); + } + + { + size_t i; + TestDataTNS testData = { 1, 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; i++ ) { + epicsThreadCreate ( "tns", + 50, stackSize, tns, & testData ); + } + epicsAtomicSetUIntT ( & 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(); +} +