o changed to more generic implementation to reduce the code size

o changed name, OSD_ATOMIC_INLINE to EPICS_ATOMIC_INLINE
o changed supported data types, unsigned removed and int added (per reveiw at codeathon)
o added add/subtract functions (per reveiw at codeathon)
o now presuming that __sync_synchronize available all gcc 4  mingw does not provide windows mem barrier)
o consolodated on one implemention for Microsoft invarient of cmplr intrinisic or win32 by using macros to config a shared header file
o improved doc in epicsAtomic.h
o added overloaded c++ interface in namespace epics :: atomic to epicsAtomic.h
o added epicsAtomicReadMemoryBarrier and epicsAtomicWriteMemoryBarrier interface to epicsAtomic.h
o changed the implementation so that each of the functions can be individually specified for a particular compiler, os, or in the generic implementation (this is accomplished with macros)
o modified the functional and performance test so that they are based on templates so we can easily support new data types
o modified performance tests to repeat function calls and measure performance using a template
This commit is contained in:
unknown
2011-08-29 19:02:41 -06:00
committed by Andrew Johnson
parent 05ae89d306
commit f0afcef02e
22 changed files with 1606 additions and 1127 deletions

View File

@@ -45,6 +45,7 @@ INC += osiWireFormat.h
INC += osdWireFormat.h
INC += osdWireConfig.h
INC += epicsAtomic.h
INC += epicsAtomicDefault.h
INC += epicsAtomicLocked.h
INC += epicsAtomicOSD.h
INC += epicsAtomicCD.h
@@ -131,6 +132,8 @@ Com_SRCS_vxWorks += logMsgToErrlog.cpp
#This forces the vxWorks compatibility stuff to be loaded
OBJS_vxWorks = vxComLibrary
INC_WIN32 += epicsAtomicMS.h
Com_SRCS_WIN32 += epicsGetopt.c
Com_SRCS_WIN32 += setThreadName.cpp
#Com_SRCS_WIN32 += dllmain.cpp

View File

@@ -17,9 +17,9 @@
#define epicsAtomicCD_h
#if defined ( __cplusplus )
# define OSD_ATOMIC_INLINE inline
# define EPICS_ATOMIC_INLINE inline
#else
# define OSD_ATOMIC_INLINE __inline__
# define EPICS_ATOMIC_INLINE __inline__
#endif
#include "epicsAtomicOSD.h"

View File

@@ -17,7 +17,7 @@
#define epicsAtomicCD_h
#if __STDC_VERSION__ >= 199901L || defined ( __cplusplus )
# define OSD_ATOMIC_INLINE inline
# define EPICS_ATOMIC_INLINE inline
#endif
#include "epicsAtomicOSD.h"

View File

@@ -20,12 +20,12 @@
# error this header is only for use with the gnu compiler
#endif
#define OSD_ATOMIC_INLINE __inline__
#define EPICS_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 \
#define GCC_ATOMIC_INTRINSICS_AVAIL_INT_T \
GCC_ATOMIC_CONCAT ( \
__GCC_HAVE_SYNC_COMPARE_AND_SWAP_, \
__SIZEOF_INT__ )
@@ -46,89 +46,127 @@
( GCC_ATOMIC_INTRINSICS_MIN_X86 && \
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_INLINE_DEFINITION
#ifdef __cplusplus
extern "C" {
#endif
OSD_ATOMIC_INLINE size_t epicsAtomicIncrSizeT ( size_t * pTarget )
{
return __sync_add_and_fetch ( pTarget, 1u );
}
/*
* We are optimistic that __sync_synchronize is implemented
* in all version four gcc invarient of target. The gnu doc
* seems to say that when not supported by architecture a call
* to an external function is generated but in practice
* this isnt the case for some of the atomic intrinsics, and
* so there is an undefined symbol. So far we have not seen
* that with __sync_synchronize, but we can only guess based
* on experimental evidence.
*
* For example we know that when generating object code for
* 386 most of the atomic instrinsics are not present and
* we see undefined symbols with mingw, but we dont have
* troubles with __sync_synchronize.
*/
#if GCC_ATOMIC_INTRINSICS_GCC4_OR_BETTER
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 void epicsAtomicSetPtrT ( EpicsAtomicPtrT * pTarget,
EpicsAtomicPtrT newValue )
{
*pTarget = newValue;
__sync_synchronize ();
}
OSD_ATOMIC_INLINE size_t epicsAtomicGetSizeT ( const size_t * pTarget )
#ifndef EPICS_ATOMIC_READ_MEMORY_BARRIER
#define EPICS_ATOMIC_READ_MEMORY_BARRIER
EPICS_ATOMIC_INLINE void epicsAtomicReadMemoryBarrier ()
{
__sync_synchronize ();
return *pTarget;
}
#endif
OSD_ATOMIC_INLINE unsigned epicsAtomicGetUIntT ( const unsigned * pTarget )
#ifndef EPICS_ATOMIC_WRITE_MEMORY_BARRIER
#define EPICS_ATOMIC_WRITE_MEMORY_BARRIER
EPICS_ATOMIC_INLINE void epicsAtomicWriteMemoryBarrier ()
{
__sync_synchronize ();
return *pTarget;
}
#endif
OSD_ATOMIC_INLINE EpicsAtomicPtrT epicsAtomicGetPtrT ( const EpicsAtomicPtrT * pTarget )
#endif /* if GCC_ATOMIC_INTRINSICS_GCC4_OR_BETTER */
#if GCC_ATOMIC_INTRINSICS_AVAIL_INT_T \
|| GCC_ATOMIC_INTRINSICS_AVAIL_EARLIER
#define EPICS_ATOMIC_INCR_INTT
EPICS_ATOMIC_INLINE int epicsAtomicIncrIntT ( int * pTarget )
{
__sync_synchronize ();
return *pTarget;
return __sync_add_and_fetch ( pTarget, 1 );
}
OSD_ATOMIC_INLINE unsigned epicsAtomicCmpAndSwapUIntT ( unsigned * pTarget,
unsigned oldVal, unsigned newVal )
#define EPICS_ATOMIC_DECR_INTT
EPICS_ATOMIC_INLINE int epicsAtomicDecrIntT ( int * pTarget )
{
return __sync_sub_and_fetch ( pTarget, 1 );
}
#define EPICS_ATOMIC_ADD_INTT
EPICS_ATOMIC_INLINE int epicsAtomicAddIntT ( int * pTarget, int delta )
{
return __sync_add_and_fetch ( pTarget, delta );
}
#define EPICS_ATOMIC_CAS_INTT
EPICS_ATOMIC_INLINE int epicsAtomicCmpAndSwapIntT ( int * pTarget,
int oldVal, int newVal )
{
return __sync_val_compare_and_swap ( pTarget, oldVal, newVal);
}
OSD_ATOMIC_INLINE EpicsAtomicPtrT epicsAtomicCmpAndSwapPtrT ( EpicsAtomicPtrT * pTarget,
#endif /* if GCC_ATOMIC_INTRINSICS_AVAIL_INT_T */
#if GCC_ATOMIC_INTRINSICS_AVAIL_SIZE_T \
|| GCC_ATOMIC_INTRINSICS_AVAIL_EARLIER
#define EPICS_ATOMIC_INCR_SIZET
EPICS_ATOMIC_INLINE size_t epicsAtomicIncrSizeT ( size_t * pTarget )
{
return __sync_add_and_fetch ( pTarget, 1u );
}
#define EPICS_ATOMIC_DECR_SIZET
EPICS_ATOMIC_INLINE size_t epicsAtomicDecrSizeT ( size_t * pTarget )
{
return __sync_sub_and_fetch ( pTarget, 1u );
}
#define EPICS_ATOMIC_ADD_SIZET
EPICS_ATOMIC_INLINE size_t epicsAtomicAddSizeT ( size_t * pTarget, size_t delta )
{
return __sync_add_and_fetch ( pTarget, delta );
}
#define EPICS_ATOMIC_SUB_SIZET
EPICS_ATOMIC_INLINE size_t epicsAtomicSubSizeT ( size_t * pTarget, size_t delta )
{
return __sync_sub_and_fetch ( pTarget, delta );
}
#define EPICS_ATOMIC_CAS_SIZET
EPICS_ATOMIC_INLINE size_t epicsAtomicCmpAndSwapSizeT ( size_t * pTarget,
size_t oldVal, size_t newVal )
{
return __sync_val_compare_and_swap ( pTarget, oldVal, newVal);
}
#define EPICS_ATOMIC_CAS_PTRT
EPICS_ATOMIC_INLINE EpicsAtomicPtrT epicsAtomicCmpAndSwapPtrT (
EpicsAtomicPtrT * pTarget,
EpicsAtomicPtrT oldVal, EpicsAtomicPtrT newVal )
{
return __sync_val_compare_and_swap ( pTarget, oldVal, newVal);
}
#endif /* if GCC_ATOMIC_INTRINSICS_AVAIL_SIZE_T */
#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 "epicsAtomicOSD.h"
#endif /* if GCC_ATOMIC_INTRINSICS_AVAIL */
/*
* if currently unavailable as gcc intrinsics we
* will try for an os specific inline solution
*/
#include "epicsAtomicOSD.h"
#endif /* epicsAtomicCD_h */

View File

@@ -26,178 +26,99 @@
#include <intrin.h>
#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
#if _MSC_VER >= 1200
# define OSD_ATOMIC_INLINE __forceinline
# define EPICS_ATOMIC_INLINE __forceinline
#else
# define OSD_ATOMIC_INLINE __inline
# define EPICS_ATOMIC_INLINE __inline
#endif
#if defined ( _M_IX86 )
# pragma warning( push )
# pragma warning( disable : 4793 )
OSD_ATOMIC_INLINE void OSD_ATOMIC_SYNC ()
EPICS_ATOMIC_INLINE void epicsAtomicMemoryBarrier ()
{
long fence;
__asm { xchg fence, eax }
}
# pragma warning( pop )
#elif defined ( _M_X64 )
# define OSD_ATOMIC_64
# define MS_ATOMIC_64
# pragma intrinsic ( __faststorefence )
# define OSD_ATOMIC_SYNC __faststorefence
EPICS_ATOMIC_INLINE void epicsAtomicMemoryBarrier ()
{
__faststorefence ();
}
#elif defined ( _M_IA64 )
# define OSD_ATOMIC_64
# define MS_ATOMIC_64
# pragma intrinsic ( __mf )
# define OSD_ATOMIC_SYNC __mf
EPICS_ATOMIC_INLINE void epicsAtomicMemoryBarrier ()
{
__mf ();
}
#else
# error unexpected target architecture, msvc version of epicsAtomicCD.h
#endif
#define OSD_ATOMIC_INLINE_DEFINITION
/*
* 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
* windows.h. Therefore, 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
*/
#define MS_LONG long
#define MS_InterlockedExchange _InterlockedExchange
#define MS_InterlockedCompareExchange _InterlockedCompareExchange
#define MS_InterlockedIncrement _InterlockedIncrement
#define MS_InterlockedDecrement _InterlockedDecrement
#define MS_InterlockedExchange _InterlockedExchange
#define MS_InterlockedExchangeAdd _InterlockedExchangeAdd
#if defined ( MS_ATOMIC_64 )
# define MS_LONGLONG long long
# define MS_InterlockedIncrement64 _InterlockedIncrement64
# define MS_InterlockedDecrement64 _InterlockedDecrement64
# define MS_InterlockedExchange64 _InterlockedExchange64
# define MS_InterlockedExchangeAdd64 _InterlockedExchangeAdd64
# define MS_InterlockedCompareExchange64 _InterlockedCompareExchange64
#endif
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/* necessary for next three functions */
STATIC_ASSERT ( sizeof ( long ) == sizeof ( unsigned ) );
OSD_ATOMIC_INLINE void epicsAtomicSetUIntT ( unsigned * pTarget, unsigned newVal )
#define EPICS_ATOMIC_READ_MEMORY_BARRIER
EPICS_ATOMIC_INLINE void epicsAtomicReadMemoryBarrier ()
{
*pTarget = newVal;
OSD_ATOMIC_SYNC ();
epicsAtomicMemoryBarrier ();
}
OSD_ATOMIC_INLINE void epicsAtomicSetSizeT ( size_t * pTarget, size_t newVal )
#define EPICS_ATOMIC_WRITE_MEMORY_BARRIER
EPICS_ATOMIC_INLINE void epicsAtomicWriteMemoryBarrier ()
{
*pTarget = newVal;
OSD_ATOMIC_SYNC ();
epicsAtomicMemoryBarrier ();
}
OSD_ATOMIC_INLINE void epicsAtomicSetPtrT ( EpicsAtomicPtrT * pTarget,
EpicsAtomicPtrT newVal )
{
*pTarget = newVal;
OSD_ATOMIC_SYNC ();
}
OSD_ATOMIC_INLINE unsigned epicsAtomicGetUIntT ( const unsigned * pTarget )
{
OSD_ATOMIC_SYNC ();
return *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 (unsigned) _InterlockedCompareExchange ( pTarg,
(long) newVal, (long) oldVal );
}
#if ! OSD_ATOMIC_64
/*
* 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
*/
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 EpicsAtomicPtrT epicsAtomicCmpAndSwapPtrT ( EpicsAtomicPtrT * pTarget,
EpicsAtomicPtrT oldVal, EpicsAtomicPtrT newVal )
{
long * const pTarg = ( long * ) ( pTarget );
return (EpicsAtomicPtrT) _InterlockedCompareExchange ( pTarg,
(long) newVal, (long) oldVal );
}
#else /* ! OSD_ATOMIC_64 */
/*
* necessary for next five 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 EpicsAtomicPtrT epicsAtomicCmpAndSwapPtrT ( EpicsAtomicPtrT * pTarget,
EpicsAtomicPtrT oldVal, EpicsAtomicPtrT newVal )
{
long long * const pTarg = ( longlong * ) ( pTarget );
return (EpicsAtomicPtrT) _InterlockedCompareExchange64 ( pTarg,
(long long) newVal, (long long) oldVal );
}
#endif /* ! OSD_ATOMIC_64 */
#ifdef __cplusplus
} /* end of extern "C" */
#endif /* __cplusplus */
#include "epicsAtomicMS.h"
#include "epicsAtomicDefault.h"
#else /* ifdef _MSC_EXTENSIONS */
#if defined ( __cplusplus )
# define OSD_ATOMIC_INLINE inline
# include "epicsAtomicOSD.h"
# define EPICS_ATOMIC_INLINE inline
#endif
/*
* if unavailable as an intrinsic we will try
* for os specific solution
*/
#include "epicsAtomicOSD.h"
#endif /* ifdef _MSC_EXTENSIONS */
#endif /* epicsAtomicCD_h */

View File

@@ -17,9 +17,9 @@
#define epicsAtomicCD_h
#if defined ( __cplusplus )
# define OSD_ATOMIC_INLINE inline
# define EPICS_ATOMIC_INLINE inline
#else
# define OSD_ATOMIC_INLINE __inline
# define EPICS_ATOMIC_INLINE __inline
#endif
#include "epicsAtomicOSD.h"

View File

@@ -25,60 +25,69 @@ extern "C" {
typedef void * EpicsAtomicPtrT;
/* load target into cache */
epicsShareFunc void epicsAtomicReadMemoryBarrier ();
/* push cache version of target into target */
epicsShareFunc void epicsAtomicWriteMemoryBarrier ();
/*
* 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,
* load target into cache, add one to target, flush cache
* to target, 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 );
epicsShareFunc int epicsAtomicIncrIntT ( int * 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,
* load target into cache, subtract one from target, flush cache
* to target, 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 );
epicsShareFunc int epicsAtomicDecrIntT ( int * pTarget );
/*
* set target in cache, flush target in cache
* lock out other smp processors from accessing the target,
* load target into cache, add/sub delta to/from target, flush cache
* to target, allow other smp processors to access the target,
* return new value of target as modified by this operation
*/
epicsShareFunc size_t epicsAtomicAddSizeT ( size_t * pTarget, size_t delta );
epicsShareFunc size_t epicsAtomicSubSizeT ( size_t * pTarget, size_t delta );
epicsShareFunc int epicsAtomicAddIntT ( int * pTarget, int delta );
/*
* set cache version of target, flush cache to target
*/
epicsShareFunc void epicsAtomicSetSizeT ( size_t * pTarget, size_t newValue );
epicsShareFunc void epicsAtomicSetUIntT ( unsigned * pTarget, unsigned newValue );
epicsShareFunc void epicsAtomicSetIntT ( int * pTarget, int newValue );
epicsShareFunc void epicsAtomicSetPtrT ( EpicsAtomicPtrT * pTarget, EpicsAtomicPtrT newValue );
/*
* fetch target in cache, return new value of target
* fetch target into cache, return new value of target
*/
epicsShareFunc size_t epicsAtomicGetSizeT ( const size_t * pTarget );
epicsShareFunc unsigned epicsAtomicGetUIntT ( const unsigned * pTarget );
epicsShareFunc int epicsAtomicGetIntT ( const int * pTarget );
epicsShareFunc EpicsAtomicPtrT epicsAtomicGetPtrT ( const EpicsAtomicPtrT * pTarget );
/*
* lock out other smp processors from accessing the target,
* sync target in cache, if target is equal to oldVal set target
* to newVal, flush target in cache, allow other smp processors
* load target into cache, if target is equal to oldVal set target
* to newVal, flush cache to target, allow other smp processors
* to access the target, return the original value stored in the
* target
*
* !!!! 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 epicsAtomicCmpAndSwapUIntT ( unsigned * pTarget,
unsigned oldVal, unsigned newVal );
epicsShareFunc EpicsAtomicPtrT epicsAtomicCmpAndSwapPtrT ( EpicsAtomicPtrT * pTarget,
EpicsAtomicPtrT oldVal, EpicsAtomicPtrT newVal );
epicsShareFunc size_t epicsAtomicCmpAndSwapSizeT ( size_t * pTarget,
size_t oldVal, size_t newVal );
epicsShareFunc int epicsAtomicCmpAndSwapIntT ( int * pTarget,
int oldVal, int newVal );
epicsShareFunc EpicsAtomicPtrT epicsAtomicCmpAndSwapPtrT (
EpicsAtomicPtrT * pTarget,
EpicsAtomicPtrT oldVal,
EpicsAtomicPtrT newVal );
#ifdef __cplusplus
} /* end of extern "C" */
@@ -87,7 +96,141 @@ epicsShareFunc EpicsAtomicPtrT epicsAtomicCmpAndSwapPtrT ( EpicsAtomicPtrT * pTa
/*
* options for inline compiler instrinsic or os specific
* implementations of the above function prototypes
*
* its importnat for certaiin compiler to define the
* inline functions before they get used in the c++
* code below
*/
#include "epicsAtomicCD.h"
#ifdef __cplusplus
namespace epics {
namespace atomic {
/*
* overloaded c++ interface
*/
epicsShareFunc size_t increment ( size_t & v );
epicsShareFunc int increment ( int & v );
epicsShareFunc size_t decrement ( size_t & v );
epicsShareFunc int decrement ( int & v );
epicsShareFunc size_t add ( size_t & v, size_t delta );
epicsShareFunc int add ( int & v, int delta );
epicsShareFunc size_t subtract ( size_t & v, size_t delta );
epicsShareFunc int subtract ( int & v, int delta );
epicsShareFunc void set ( size_t & v , size_t newValue );
epicsShareFunc void set ( int & v, int newValue );
epicsShareFunc void set ( EpicsAtomicPtrT & v,
EpicsAtomicPtrT newValue );
epicsShareFunc size_t get ( const size_t & v );
epicsShareFunc int get ( const int & v );
epicsShareFunc EpicsAtomicPtrT get ( const EpicsAtomicPtrT & v );
epicsShareFunc size_t compareAndSwap ( size_t & v, size_t oldVal,
size_t newVal );
epicsShareFunc int compareAndSwap ( int & v, int oldVal, int newVal );
epicsShareFunc EpicsAtomicPtrT compareAndSwap ( EpicsAtomicPtrT & v,
EpicsAtomicPtrT oldVal,
EpicsAtomicPtrT newVal );
/************* incr ***************/
inline size_t increment ( size_t & v )
{
return epicsAtomicIncrSizeT ( & v );
}
inline int increment ( int & v )
{
return epicsAtomicIncrIntT ( & v );
}
/************* decr ***************/
inline size_t decrement ( size_t & v )
{
return epicsAtomicDecrSizeT ( & v );
}
inline int decrement ( int & v )
{
return epicsAtomicDecrIntT ( & v );
}
/************* add ***************/
inline size_t add ( size_t & v, size_t delta )
{
return epicsAtomicAddSizeT ( & v, delta );
}
inline int add ( int & v, int delta )
{
return epicsAtomicAddIntT ( & v, delta );
}
/************* sub ***************/
inline size_t subtract ( size_t & v, size_t delta )
{
return epicsAtomicSubSizeT ( & v, delta );
}
inline int subtract ( int & v, int delta )
{
return epicsAtomicAddIntT ( & v, -delta );
}
/************* set ***************/
inline void set ( size_t & v , size_t newValue )
{
epicsAtomicSetSizeT ( & v, newValue );
}
inline void set ( int & v, int newValue )
{
epicsAtomicSetIntT ( & v, newValue );
}
inline void set ( EpicsAtomicPtrT & v, EpicsAtomicPtrT newValue )
{
epicsAtomicSetPtrT ( & v, newValue );
}
/************* get ***************/
inline size_t get ( const size_t & v )
{
return epicsAtomicGetSizeT ( & v );
}
inline int get ( const int & v )
{
return epicsAtomicGetIntT ( & v );
}
inline EpicsAtomicPtrT get ( const EpicsAtomicPtrT & v )
{
return epicsAtomicGetPtrT ( & v );
}
/************* cas ***************/
inline size_t compareAndSwap ( size_t & v,
size_t oldVal, size_t newVal )
{
return epicsAtomicCmpAndSwapSizeT ( & v, oldVal, newVal );
}
inline int compareAndSwap ( int & v, int oldVal, int newVal )
{
return epicsAtomicCmpAndSwapIntT ( & v, oldVal, newVal );
}
inline EpicsAtomicPtrT compareAndSwap ( EpicsAtomicPtrT & v,
EpicsAtomicPtrT oldVal,
EpicsAtomicPtrT newVal )
{
return epicsAtomicCmpAndSwapPtrT ( & v, oldVal, newVal );
}
} /* end of namespace atomic */
} /* end of namespace epics */
#endif /* ifdef __cplusplus */
#endif /* epicsAtomic_h */

View File

@@ -0,0 +1,235 @@
/*************************************************************************\
* 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 epicsAtomicDefault_h
#define epicsAtomicDefault_h
/*
* EPICS_ATOMIC_INLINE might be defined, but empty for an out-of-line
* instantiation
*/
#ifdef EPICS_ATOMIC_INLINE
#ifdef __cpluplus
extern "C" {
#endif
/*
* struct EpicsAtomicLockKey;
* epicsShareFunc void epicsAtomicReadMemoryBarrier ();
* epicsShareFunc void epicsAtomicWriteMemoryBarrier ();
* epicsShareFunc void epicsAtomicLock ( struct EpicsAtomicLockKey * );
* epicsShareFunc void epicsAtomicUnock ( struct EpicsAtomicLockKey * );
*/
/*
* incr
*/
#ifndef EPICS_ATOMIC_INCR_INTT
EPICS_ATOMIC_INLINE int epicsAtomicIncrIntT ( int * pTarget )
{
EpicsAtomicLockKey key;
epicsAtomicLock ( & key );
const int result = ++(*pTarget);
epicsAtomicUnlock ( & key );
return result;
}
#endif
#ifndef EPICS_ATOMIC_INCR_SIZET
EPICS_ATOMIC_INLINE size_t epicsAtomicIncrSizeT ( size_t * pTarget )
{
EpicsAtomicLockKey key;
epicsAtomicLock ( & key );
const size_t result = ++(*pTarget);
epicsAtomicUnlock ( & key );
return result;
}
#endif
/*
* decr
*/
#ifndef EPICS_ATOMIC_DECR_INTT
EPICS_ATOMIC_INLINE int epicsAtomicDecrIntT ( int * pTarget )
{
EpicsAtomicLockKey key;
epicsAtomicLock ( & key );
const int result = --(*pTarget);
epicsAtomicUnlock ( & key );
return result;
}
#endif
#ifndef EPICS_ATOMIC_DECR_SIZET
EPICS_ATOMIC_INLINE size_t epicsAtomicDecrSizeT ( size_t * pTarget )
{
EpicsAtomicLockKey key;
epicsAtomicLock ( & key );
const size_t result = --(*pTarget);
epicsAtomicUnlock ( & key );
return result;
}
#endif
/*
* add/sub
*/
#ifndef EPICS_ATOMIC_ADD_INTT
EPICS_ATOMIC_INLINE int epicsAtomicAddIntT ( int * pTarget, int delta )
{
EpicsAtomicLockKey key;
epicsAtomicLock ( & key );
const int result = *pTarget += delta;
epicsAtomicUnlock ( & key );
return result;
}
#endif
#ifndef EPICS_ATOMIC_ADD_SIZET
EPICS_ATOMIC_INLINE size_t epicsAtomicAddSizeT ( size_t * pTarget, size_t delta )
{
EpicsAtomicLockKey key;
epicsAtomicLock ( & key );
const size_t result = *pTarget += delta;
epicsAtomicUnlock ( & key );
return result;
}
#endif
#ifndef EPICS_ATOMIC_SUB_SIZET
EPICS_ATOMIC_INLINE size_t epicsAtomicSubSizeT ( size_t * pTarget, size_t delta )
{
EpicsAtomicLockKey key;
epicsAtomicLock ( & key );
const size_t result = *pTarget -= delta;
epicsAtomicUnlock ( & key );
return result;
}
#endif
/*
* set
*/
#ifndef EPICS_ATOMIC_SET_INTT
EPICS_ATOMIC_INLINE void epicsAtomicSetIntT ( int * pTarget, int newVal )
{
*pTarget = newVal;
epicsAtomicWriteMemoryBarrier ();
}
#endif
#ifndef EPICS_ATOMIC_SET_SIZET
EPICS_ATOMIC_INLINE void epicsAtomicSetSizeT ( size_t * pTarget, size_t newVal )
{
*pTarget = newVal;
epicsAtomicWriteMemoryBarrier ();
}
#endif
#ifndef EPICS_ATOMIC_SET_PTRT
EPICS_ATOMIC_INLINE void epicsAtomicSetPtrT ( EpicsAtomicPtrT * pTarget,
EpicsAtomicPtrT newVal )
{
*pTarget = newVal;
epicsAtomicWriteMemoryBarrier ();
}
#endif
/*
* get
*/
#ifndef EPICS_ATOMIC_GET_INTT
EPICS_ATOMIC_INLINE int epicsAtomicGetIntT ( const int * pTarget )
{
epicsAtomicReadMemoryBarrier ();
return *pTarget;
}
#endif
#ifndef EPICS_ATOMIC_GET_SIZET
EPICS_ATOMIC_INLINE size_t epicsAtomicGetSizeT ( const size_t * pTarget )
{
epicsAtomicReadMemoryBarrier ();
return *pTarget;
}
#endif
#ifndef EPICS_ATOMIC_GET_PTRT
EPICS_ATOMIC_INLINE EpicsAtomicPtrT
epicsAtomicGetPtrT ( const EpicsAtomicPtrT * pTarget )
{
epicsAtomicReadMemoryBarrier ();
return *pTarget;
}
#endif
/*
* cmp and swap
*/
#ifndef EPICS_ATOMIC_CAS_INTT
EPICS_ATOMIC_INLINE int epicsAtomicCmpAndSwapIntT ( int * pTarget, int oldval, int newval )
{
EpicsAtomicLockKey key;
epicsAtomicLock ( & key );
const int cur = *pTarget;
if ( cur == oldval ) {
*pTarget = newval;
}
epicsAtomicUnlock ( & key );
return cur;
}
#endif
#ifndef EPICS_ATOMIC_CAS_SIZET
EPICS_ATOMIC_INLINE size_t epicsAtomicCmpAndSwapSizeT ( size_t * pTarget,
size_t oldval, size_t newval )
{
EpicsAtomicLockKey key;
epicsAtomicLock ( & key );
const size_t cur = *pTarget;
if ( cur == oldval ) {
*pTarget = newval;
}
epicsAtomicUnlock ( & key );
return cur;
}
#endif
#ifndef EPICS_ATOMIC_CAS_PTRT
EPICS_ATOMIC_INLINE EpicsAtomicPtrT epicsAtomicCmpAndSwapPtrT (
EpicsAtomicPtrT * pTarget,
EpicsAtomicPtrT oldval, EpicsAtomicPtrT newval )
{
EpicsAtomicLockKey key;
epicsAtomicLock ( & key );
const EpicsAtomicPtrT cur = *pTarget;
if ( cur == oldval ) {
*pTarget = newval;
}
epicsAtomicUnlock ( & key );
return cur;
}
#endif
#ifdef __cpluplus
} /* end of extern "C" */
#endif
#endif /* EPICS_ATOMIC_INLINE */
#endif /* epicsAtomicDefault_h */

View File

@@ -1,55 +0,0 @@
/*************************************************************************\
* 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 epicsAtomicGuard_h
#define epicsAtomicGuard_h
//
// This header file is intended only for use with the epicsAtomicLocked
// header file. The intent is that an OS specific implementation of
// epicsAtomicOSD.cpp might first include this file, next implement
// AtomicGuard depending on os specific capabilities, and finally include
// the epicsAtomicLocked header file to define the various epicsAtomicXxxx
// functions.
//
// The AtomicGuard class is defined in a seperate header file
// from the epicsAtomicLocked header for two reasons.
// o First, it must be possible to have an inline definition of the
// AtomicGuard member functions, and that isnt possible if the
// epicsAtomicXxxx function definitions in the epicsAtomicLocked
// header file have already been seen by the translation unit.
// o Second, we need to enforce a uniform interface for all definitions
// of the the AtomicGuard constructor and destructor member functions
// requiring that no exception are thrown. This is because the
// epicsAtomicXxxx functions in the epicsAtomicLocked header file are
// directly callable by C code.
//
// The epicsAtomicXxxx functions in the epicsAtomicLocked do not pass
// any parameters to the AtomicGuard constructor, and therefore the lock
// used by AtomicGuard is restricted to be a global lock for the entire
// multithreaded executable. Therefore, AtomicGuard will typically
// reference some translation unit scope limited global variable in
// the anonymous namespace for implementation of the locking primitive.
//
namespace {
struct AtomicGuard {
AtomicGuard () throw ();
~AtomicGuard () throw ();
};
} // end of anonymous namespace
#endif // ifndef epicsAtomicGuard_h

View File

@@ -1,45 +0,0 @@
/*************************************************************************\
* 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

View File

@@ -0,0 +1,222 @@
/*************************************************************************\
* 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 epicsAtomicMS_h
#define epicsAtomicMS_h
#include "epicsAssert.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#ifndef EPICS_ATOMIC_INCR_INTT
#define EPICS_ATOMIC_INCR_INTT
EPICS_ATOMIC_INLINE int epicsAtomicIncrIntT ( int * pTarget )
{
STATIC_ASSERT ( sizeof ( MS_LONG ) == sizeof ( int ) );
MS_LONG * const pTarg = ( MS_LONG * ) pTarget;
return MS_InterlockedIncrement ( pTarg );
}
#endif
#ifndef EPICS_ATOMIC_DECR_INTT
#define EPICS_ATOMIC_DECR_INTT
EPICS_ATOMIC_INLINE int epicsAtomicDecrIntT ( int * pTarget )
{
STATIC_ASSERT ( sizeof ( MS_LONG ) == sizeof ( int ) );
MS_LONG * const pTarg = ( MS_LONG * ) ( pTarget );
return MS_InterlockedDecrement ( pTarg );
}
#endif
#ifndef EPICS_ATOMIC_ADD_INTT
#define EPICS_ATOMIC_ADD_INTT
EPICS_ATOMIC_INLINE int epicsAtomicAddIntT ( int * pTarget, int delta )
{
STATIC_ASSERT ( sizeof ( MS_LONG ) == sizeof ( int ) );
MS_LONG * const pTarg = ( MS_LONG * ) ( pTarget );
/* we dont use InterlockedAdd because only latest windows is supported */
return delta + ( int ) MS_InterlockedExchangeAdd ( pTarg,
( MS_LONG ) delta );
}
#endif
#ifndef EPICS_ATOMIC_CAS_INTT
#define EPICS_ATOMIC_CAS_INTT
EPICS_ATOMIC_INLINE int epicsAtomicCmpAndSwapIntT ( int * pTarget,
int oldVal, int newVal )
{
STATIC_ASSERT ( sizeof ( MS_LONG ) == sizeof ( int ) );
MS_LONG * const pTarg = ( MS_LONG * ) ( pTarget );
return (int) MS_InterlockedCompareExchange ( pTarg,
(MS_LONG) newVal, (MS_LONG) oldVal );
}
#endif
#if ! defined ( MS_ATOMIC_64 )
/*
* necessary for next three 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 ( MS_LONG ) == sizeof ( size_t ) );
#ifndef EPICS_ATOMIC_INCR_SIZET
#define EPICS_ATOMIC_INCR_SIZET
EPICS_ATOMIC_INLINE size_t epicsAtomicIncrSizeT ( size_t * pTarget )
{
MS_LONG * const pTarg = ( MS_LONG * ) pTarget;
return MS_InterlockedIncrement ( pTarg );
}
#endif
#ifndef EPICS_ATOMIC_DECR_SIZET
#define EPICS_ATOMIC_DECR_SIZET
EPICS_ATOMIC_INLINE size_t epicsAtomicDecrSizeT ( size_t * pTarget )
{
MS_LONG * const pTarg = ( MS_LONG * ) ( pTarget );
return MS_InterlockedDecrement ( pTarg );
}
#endif
#ifndef EPICS_ATOMIC_ADD_SIZET
#define EPICS_ATOMIC_ADD_SIZET
EPICS_ATOMIC_INLINE size_t epicsAtomicAddSizeT ( size_t * pTarget,
size_t delta )
{
MS_LONG * const pTarg = ( MS_LONG * ) ( pTarget );
/* we dont use InterlockedAdd because only latest windows is supported */
return delta + ( size_t ) MS_InterlockedExchangeAdd ( pTarg,
( MS_LONG ) delta );
}
#endif
#ifndef EPICS_ATOMIC_SUB_SIZET
#define EPICS_ATOMIC_SUB_SIZET
EPICS_ATOMIC_INLINE size_t epicsAtomicSubSizeT ( size_t * pTarget, size_t delta )
{
MS_LONG * const pTarg = ( MS_LONG * ) ( pTarget );
MS_LONG ldelta = (MS_LONG) delta;
/* we dont use InterlockedAdd because only latest windows is supported */
return ( ( size_t ) MS_InterlockedExchangeAdd ( pTarg, -ldelta ) ) - delta;
}
#endif
#ifndef EPICS_ATOMIC_CAS_SIZET
#define EPICS_ATOMIC_CAS_SIZET
EPICS_ATOMIC_INLINE size_t epicsAtomicCmpAndSwapSizeT (
size_t * pTarget,
size_t oldVal, size_t newVal )
{
MS_LONG * const pTarg = ( MS_LONG * ) ( pTarget );
return (size_t) MS_InterlockedCompareExchange ( pTarg,
(MS_LONG) newVal, (MS_LONG) oldVal );
}
#endif
#ifndef EPICS_ATOMIC_CAS_PTRT
#define EPICS_ATOMIC_CAS_PTRT
EPICS_ATOMIC_INLINE EpicsAtomicPtrT epicsAtomicCmpAndSwapPtrT (
EpicsAtomicPtrT * pTarget,
EpicsAtomicPtrT oldVal, EpicsAtomicPtrT newVal )
{
MS_LONG * const pTarg = ( MS_LONG * ) ( pTarget );
return (EpicsAtomicPtrT) MS_InterlockedCompareExchange ( pTarg,
(MS_LONG) newVal, (MS_LONG) oldVal );
}
#endif
#else /* ! MS_ATOMIC_64 */
/*
* necessary for next three functions
*/
STATIC_ASSERT ( sizeof ( MS_LONGLONG ) == sizeof ( size_t ) );
#ifndef EPICS_ATOMIC_INCR_SIZET
#define EPICS_ATOMIC_INCR_SIZET
EPICS_ATOMIC_INLINE size_t epicsAtomicIncrSizeT ( size_t * pTarget )
{
MS_LONGLONG * const pTarg = ( MS_LONGLONG * ) pTarget;
return ( size_t ) MS_InterlockedIncrement64 ( pTarg );
}
#endif
#ifndef EPICS_ATOMIC_DECR_SIZET
#define EPICS_ATOMIC_DECR_SIZET
EPICS_ATOMIC_INLINE size_t epicsAtomicDecrSizeT ( size_t * pTarget )
{
MS_LONGLONG * const pTarg = ( MS_LONGLONG * ) ( pTarget );
return ( size_t ) MS_InterlockedDecrement64 ( pTarg );
}
#endif
#ifndef EPICS_ATOMIC_ADD_SIZET
#define EPICS_ATOMIC_ADD_SIZET
EPICS_ATOMIC_INLINE size_t epicsAtomicAddSizeT ( size_t * pTarget, size_t delta )
{
MS_LONGLONG * const pTarg = ( MS_LONGLONG * ) ( pTarget );
/* we dont use InterlockedAdd64 because only latest windows is supported */
return delta + ( size_t ) MS_InterlockedExchangeAdd64 ( pTarg,
( MS_LONGLONG ) delta );
}
#endif
#ifndef EPICS_ATOMIC_SUB_SIZET
#define EPICS_ATOMIC_SUB_SIZET
EPICS_ATOMIC_INLINE size_t epicsAtomicSubSizeT ( size_t * pTarget, size_t delta )
{
MS_LONGLONG * const pTarg = ( MS_LONGLONG * ) ( pTarget );
MS_LONGLONG ldelta = (MS_LONGLONG) delta;
/* we dont use InterlockedAdd64 because only latest windows is supported */
return (( size_t ) MS_InterlockedExchangeAdd64 ( pTarg, -ldelta )) - delta;
}
#endif
#ifndef EPICS_ATOMIC_CAS_SIZET
#define EPICS_ATOMIC_CAS_SIZET
EPICS_ATOMIC_INLINE size_t epicsAtomicCmpAndSwapSizeT ( size_t * pTarget,
size_t oldVal, size_t newVal )
{
MS_LONGLONG * const pTarg = ( MS_LONGLONG * ) ( pTarget );
return (size_t) MS_InterlockedCompareExchange64 ( pTarg,
(MS_LONGLONG) newVal,
(MS_LONGLONG) oldVal );
}
#endif
#ifndef EPICS_ATOMIC_CAS_PTRT
#define EPICS_ATOMIC_CAS_PTRT
EPICS_ATOMIC_INLINE EpicsAtomicPtrT epicsAtomicCmpAndSwapPtrT (
EpicsAtomicPtrT * pTarget,
EpicsAtomicPtrT oldVal, EpicsAtomicPtrT newVal )
{
MS_LONGLONG * const pTarg = ( MS_LONGLONG * ) ( pTarget );
return (EpicsAtomicPtrT) MS_InterlockedCompareExchange64 ( pTarg,
(MS_LONGLONG) newVal, (MS_LONGLONG) oldVal );
}
#endif
#endif /* ! MS_ATOMIC_64 */
#ifdef __cplusplus
} /* end of extern "C" */
#endif /* __cplusplus */
#endif /* ifdef epicsAtomicMS_h */

View File

@@ -14,13 +14,9 @@
#define epicsExportSharedSymbols
#include "epicsAtomic.h"
//
// We need a default epicsAtopmicOSD.cpp which is empty for use when
// a compiler or inline OS specific implementation is provided. See
// "osi/os/posix/epicsAtomicOSD.cpp" for an example OS specific generic
// out-of-line implementation based on a mutex lock.
//
#if ! defined ( OSD_ATOMIC_INLINE_DEFINITION )
# error the epicsAtomicXxxxx functions definitions are misssing
// if the compiler is unable to inline then instantiate out-of-line
#ifndef EPICS_ATOMIC_INLINE
#define EPICS_ATOMIC_INLINE
#include "epicsAtomic.h"
#endif

View File

@@ -12,149 +12,38 @@
* Author Jeffrey O. Hill
* johill@lanl.gov
*/
#ifndef epicsAtomicOSD_h
#define epicsAtomicOSD_h
#if defined ( OSD_ATOMIC_INLINE )
#include <limits.h>
#include "epicsAssert.h"
#define STRICT
#define VC_EXTRALEAN
#include <windows.h>
#define STRICT
#include "windows.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#if defined ( _WIN64 )
# define MS_ATOMIC_64
#endif
#define OSD_ATOMIC_INLINE_DEFINITION
#define MS_LONG LONG
#define MS_InterlockedExchange InterlockedExchange
#define MS_InterlockedCompareExchange InterlockedCompareExchange
#define MS_InterlockedIncrement InterlockedIncrement
#define MS_InterlockedDecrement InterlockedDecrement
#define MS_InterlockedExchange InterlockedExchange
#define MS_InterlockedExchangeAdd InterlockedExchangeAdd
#if defined ( MS_ATOMIC_64 )
# define MS_LONGLONG LONGLONG
# define MS_InterlockedIncrement64 InterlockedIncrement64
# define MS_InterlockedDecrement64 InterlockedDecrement64
# define MS_InterlockedExchange64 InterlockedExchange64
# define MS_InterlockedExchangeAdd64 InterlockedExchangeAdd64
# define MS_InterlockedCompareExchange InterlockedCompareExchange64
#endif
/*
* 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__
#ifdef EPICS_ATOMIC_INLINE
# include "epicsAtomicMS.h"
# include "epicsAtomicDefault.h"
#endif
/* necessary for next three functions */
STATIC_ASSERT ( sizeof ( LONG ) == sizeof ( unsigned ) );
#endif /* epicsAtomicOSD_h */
OSD_ATOMIC_INLINE void epicsAtomicSetUIntT ( unsigned * pTarget, unsigned 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 )
{
MemoryBarrier ();
return *pTarget;
}
OSD_ATOMIC_INLINE size_t epicsAtomicGetSizeT ( const size_t * pTarget )
{
MemoryBarrier ();
return *pTarget;
}
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 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
*/
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 EpicsAtomicPtrT epicsAtomicCmpAndSwapPtrT ( EpicsAtomicPtrT * pTarget,
EpicsAtomicPtrT oldVal, EpicsAtomicPtrT newVal )
{
LONG * const pTarg = ( LONG * ) ( pTarget );
return (EpicsAtomicPtrT) InterlockedCompareExchange ( pTarg,
(LONG) newVal, (LONG) oldVal );
}
#elif defined ( _WIN64 )
/*
* necessary for next five 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 EpicsAtomicPtrT epicsAtomicCmpAndSwapPtrT ( EpicsAtomicPtrT * pTarget,
EpicsAtomicPtrT oldVal, EpicsAtomicPtrT newVal )
{
LONGLONG * const pTarg = ( LONGLONG * ) ( pTarget );
return (EpicsAtomicPtrT) InterlockedCompareExchange64 ( pTarg,
(LONGLONG) newVal, (LONGLONG) oldVal );
}
#endif /* if defined ( _WIN32 ) */
#ifdef __cplusplus
} /* end of extern "C" */
#endif /* __cplusplus */
#endif /* if defined ( OSD_ATOMIC_INLINE ) */
#endif /* ifndef epicsAtomicOSD_h */

View File

@@ -9,67 +9,100 @@
/*
* Author Jeffrey O. Hill
* johill@lanl.gov
*
* Provide a global mutex version of AtomicGuard on POSIX
* systems for when we dont have more efficent compiler or
* OS primitives intriniscs to use instead.
*
* We implement this mutex-based AtomicGuard primitive directly
* upon the standalone POSIX pthread library so that the epicsAtomic
* library can be used to implement other primitives such
* epicsThreadOnce.
*
* We use a static initialized pthread mutex to minimize code
* size, and are also optimistic that this can be more efficent
* than pthread_once.
*/
#define epicsExportSharedSymbols
#include "epicsAtomic.h"
// if the compiler is unable to inline then instantiate out-of-line
#ifndef EPICS_ATOMIC_INLINE
#define EPICS_ATOMIC_INLINE
#include "epicsAtomic.h"
#endif
/* Authors: Jeffrey O. Hill */
#include <errno.h>
#include <unistd.h>
#include <pthread.h>
#define epicsExportSharedSymbols
#include "epicsAssert.h"
// if the compiler is unable to inline then instantiate out-of-line
#ifndef EPICS_ATOMIC_INLINE
#define EPICS_ATOMIC_INLINE
#include "epicsAtomic.h"
#endif
// If we have an inline implementation then implement
// nothing that conflicts here
#if ! defined ( OSD_ATOMIC_INLINE_DEFINITION )
#ifndef EPICS_ATOMIC_LOCK
// get the interface for class AtomicGuard
#include "epicsAtomicGuard.h"
/*
* Slow, but probably correct on all systems.
* Useful only if something more efficent isnt
* provided based on knowledge of the compiler
* or OS
*
* A statically initialized pthread mutex doesnt
* need to be destroyed
*
* !!!!!
* !!!!! WARNING
* !!!!!
* !!!!! Do not use this implementation on systems where
* !!!!! code runs at interrupt context. If so, then
* !!!!! an implementation must be provided that is based
* !!!!! on a compiler intrinsic or an interrpt lock and or
* !!!!! a spin lock primitive
* !!!!!
*/
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
namespace {
// a statically initialized mutex doesnt need to be destroyed
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
inline AtomicGuard :: AtomicGuard () throw ()
void epicsAtomicLock ( EpicsAtomicLockKey * )
{
unsigned countDown = 1000u;
int status;
while ( true ) {
status = pthread_mutex_lock ( & mutex );
if ( status != EINTR ) break;
if ( status == 0 ) return;
assert ( status == EINTR );
static const useconds_t retryDelayUSec = 100000;
usleep ( retryDelayUSec );
countDown--;
assert ( countDown );
}
assert ( status == 0 );
}
inline AtomicGuard :: ~AtomicGuard () throw ()
void epicsAtomicUnlock ( EpicsAtomicLockKey * )
{
const int status = pthread_mutex_unlock ( & mutex );
assert ( status == 0 );
}
} // end of namespace anonymous
#endif // ifndef EPICS_ATOMIC_LOCK
// Define the epicsAtomicXxxx functions out-of-line using this
// implementation of AtomicGuard. Note that this isnt a traditional
// c++ header file.
#include "epicsAtomicLocked.h"
#ifndef EPICS_ATOMIC_READ_MEMORY_BARRIER
// Slow, but probably correct on all systems.
// Useful only if something more efficent isnt
// provided based on knowledge of the compiler
// or OS
void epicsAtomicReadMemoryBarrier ()
{
EpicsAtomicLockKey key;
epicsAtomicLock ( & key );
epicsAtomicUnlock ( & key );
}
#endif
#ifndef EPICS_ATOMIC_WRITE_MEMORY_BARRIER
// Slow, but probably correct on all systems.
// Useful only if something more efficent isnt
// provided based on knowledge of the compiler
// or OS
void epicsAtomicWriteMemoryBarrier ()
{
EpicsAtomicLockKey key;
epicsAtomicLock ( & key );
epicsAtomicUnlock ( & key );
}
#endif
#endif // if ! defined ( #define OSD_ATOMIC_INLINE_DEFINITION )

View File

@@ -16,4 +16,17 @@
#ifndef epicsAtomicOSD_h
#define epicsAtomicOSD_h
struct EpicsAtomicLockKey {};
epicsShareFunc void epicsAtomicReadMemoryBarrier ();
epicsShareFunc void epicsAtomicWriteMemoryBarrier ();
epicsShareFunc void epicsAtomicLock ( struct EpicsAtomicLockKey * );
epicsShareFunc void epicsAtomicUnlock ( struct EpicsAtomicLockKey * );
#ifdef EPICS_ATOMIC_INLINE
#include "epicsAtomicDefault.h"
#endif /* ifdef EPICS_ATOMIC_INLINE */
#endif /* epicsAtomicOSD_h */

View File

@@ -16,84 +16,141 @@
#ifndef epicsAtomicOSD_h
#define epicsAtomicOSD_h
#if defined ( OSD_ATOMIC_INLINE )
#if defined ( EPICS_ATOMIC_INLINE )
/*
* atomic.h exists only in Solaris 10 or higher
*/
#if defined ( __SunOS_5_10 )
#include <limits.h>
#include <atomic.h>
#define __STDC_LIMIT_MACROS /* define SIZE_MAX for c++ */
#include <stdint.h>
#define OSD_ATOMIC_INLINE_DEFINITION
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
OSD_ATOMIC_INLINE unsigned epicsAtomicCmpAndSwapUIntT ( unsigned * pTarget,
unsigned oldVal, unsigned newVal )
#ifndef EPICS_ATOMIC_READ_MEMORY_BARRIER
#define EPICS_ATOMIC_READ_MEMORY_BARRIER
EPICS_ATOMIC_INLINE int epicsAtomicReadMemoryBarrier ()
{
return atomic_cas_uint ( pTarget, oldVal, newVal );
membar_consumer ();
}
#endif
OSD_ATOMIC_INLINE EpicsAtomicPtrT epicsAtomicCmpAndSwapPtrT ( EpicsAtomicPtrT * pTarget,
EpicsAtomicPtrT oldVal, EpicsAtomicPtrT newVal )
#ifndef EPICS_ATOMIC_WRITE_MEMORY_BARRIER
#define EPICS_ATOMIC_WRITE_MEMORY_BARRIER
EPICS_ATOMIC_INLINE int epicsAtomicWriteMemoryBarrier ()
{
membar_producer ();
}
#endif
#ifndef EPICS_ATOMIC_CAS_INTT
#define EPICS_ATOMIC_CAS_INTT
EPICS_ATOMIC_INLINE int epicsAtomicCmpAndSwapIntT ( int * pTarget,
int oldVal, int newVal )
{
STATIC_ASSERT ( sizeof ( int ) == sizeof ( unsigned ) );
return ( int ) atomic_cas_uint ( pTarget, ( unsigned ) oldVal,
( unsigned ) newVal );
}
#endif
#ifndef EPICS_ATOMIC_CAS_SIZET
#define EPICS_ATOMIC_CAS_SIZET
EPICS_ATOMIC_INLINE EpicsAtomicPtrT epicsAtomicCmpAndSwapSizeT (
size_t * pTarget,
size_t oldVal, size_t newVal )
{
STATIC_ASSERT ( sizeof ( void * ) == sizeof ( size_t ) );
void ** ppPtr = (void **) pTarget;
return ( size_t ) atomic_cas_ptr ( ppPtr, ( void * )oldVal, ( void * )newVal );
}
#endif
#ifndef EPICS_ATOMIC_CAS_PTRT
#define EPICS_ATOMIC_CAS_PTRT
EPICS_ATOMIC_INLINE EpicsAtomicPtrT epicsAtomicCmpAndSwapPtrT (
EpicsAtomicPtrT * pTarget,
EpicsAtomicPtrT oldVal,
EpicsAtomicPtrT newVal )
{
return atomic_cas_ptr ( pTarget, oldVal, newVal );
}
#endif
OSD_ATOMIC_INLINE size_t epicsAtomicIncrSizeT ( size_t * pTarget )
#ifndef EPICS_ATOMIC_INCR_INTT
#define EPICS_ATOMIC_INCR_INTT
EPICS_ATOMIC_INLINE int epicsAtomicIncrIntT ( int * pTarget )
{
void * const pTarg = ( void * ) ( pTarget );
return ( size_t ) atomic_inc_ptr_nv ( pTarg );
STATIC_ASSERT ( sizeof ( unsigned ) == sizeof ( int ) );
unsigned * const pTarg = ( unsigned * ) ( pTarget );
return ( int ) atomic_inc_uint_nv ( pTarg );
}
#endif
OSD_ATOMIC_INLINE size_t epicsAtomicDecrSizeT ( size_t * pTarget )
#ifndef EPICS_ATOMIC_INCR_SIZET
#define EPICS_ATOMIC_INCR_SIZET
EPICS_ATOMIC_INLINE size_t epicsAtomicIncrSizeT ( size_t * pTarget )
{
void * const pTarg = ( void * ) ( pTarget );
return atomic_dec_ptr_nv ( pTarg );
STATIC_ASSERT ( sizeof ( void * ) == sizeof ( size_t ) );
void ** const ppTarg = ( void ** ) ( pTarget );
return ( size_t ) atomic_inc_ptr_nv ( ppTarg );
}
#endif
OSD_ATOMIC_INLINE void epicsAtomicSetUIntT ( unsigned * pTarget, unsigned newVal )
#ifndef EPICS_ATOMIC_DECR_INTT
#define EPICS_ATOMIC_DECR_INTT
EPICS_ATOMIC_INLINE int epicsAtomicDecrIntT ( int * pTarget )
{
*pTarget = newVal;
membar_producer();
STATIC_ASSERT ( sizeof ( unsigned ) == sizeof ( int ) );
unsigned * const pTarg = ( unsigned * ) ( pTarget );
return ( int ) atomic_dec_uint_nv ( pTarg );
}
#endif
OSD_ATOMIC_INLINE void epicsAtomicSetSizeT ( size_t * pTarget, size_t newVal )
#ifndef EPICS_ATOMIC_DECR_SIZET
#define EPICS_ATOMIC_DECR_SIZET
EPICS_ATOMIC_INLINE size_t epicsAtomicDecrSizeT ( size_t * pTarget )
{
*pTarget = newVal;
membar_producer();
STATIC_ASSERT ( sizeof ( void * ) == sizeof ( size_t ) );
void ** const pTarg = ( void ** ) ( pTarget );
return ( size_t ) atomic_dec_ptr_nv ( pTarg );
}
#endif
OSD_ATOMIC_INLINE void epicsAtomicSetPtrT ( EpicsAtomicPtrT * pTarget, EpicsAtomicPtrT newVal )
#ifndef EPICS_ATOMIC_ADD_INTT
#define EPICS_ATOMIC_ADD_INTT
EPICS_ATOMIC_INLINE int epicsAtomicAddIntT ( int * pTarget, int delta )
{
*pTarget = newVal;
membar_producer();
STATIC_ASSERT ( sizeof ( unsigned ) == sizeof ( int ) );
unsigned * const pTarg = ( unsigned * ) ( pTarget );
return ( int ) atomic_add_int_nv ( pTarg, delta );
}
#endif
OSD_ATOMIC_INLINE unsigned epicsAtomicGetUIntT ( const unsigned * pTarget )
#ifndef EPICS_ATOMIC_ADD_SIZET
#define EPICS_ATOMIC_ADD_SIZET
EPICS_ATOMIC_INLINE size_t epicsAtomicAddSizeT ( size_t * pTarget,
size_t delta )
{
membar_consumer ();
return *pTarget;
STATIC_ASSERT ( sizeof ( void * ) == sizeof ( size_t ) );
void ** const pTarg = ( void ** ) ( pTarget );
return ( size_t ) atomic_add_ptr_nv ( pTarg, ( ssize_t ) delta );
}
#endif
OSD_ATOMIC_INLINE size_t epicsAtomicGetSizeT ( const size_t * pTarget )
#ifndef EPICS_ATOMIC_SUB_SIZET
#define EPICS_ATOMIC_SUB_SIZET
EPICS_ATOMIC_INLINE size_t epicsAtomicSubSizeT ( size_t * pTarget,
size_t delta )
{
membar_consumer ();
return *pTarget;
}
OSD_ATOMIC_INLINE EpicsAtomicPtrT epicsAtomicGetPtrT ( const EpicsAtomicPtrT * pTarget )
{
membar_consumer ();
return *pTarget;
STATIC_ASSERT ( sizeof ( void * ) == sizeof ( size_t ) );
void ** const pTarg = ( void ** ) ( pTarget );
ssize_t = sdelta = ( ssize_t ) delta;
return ( size_t ) atomic_add_ptr_nv ( pTarg, -sdelta );
}
#endif
#ifdef __cplusplus
} /* end of extern "C" */
@@ -101,6 +158,9 @@ OSD_ATOMIC_INLINE EpicsAtomicPtrT epicsAtomicGetPtrT ( const EpicsAtomicPtrT * p
#endif /* ifdef __SunOS_5_10 */
#endif /* if defined ( OSD_ATOMIC_INLINE ) */
#include "epicsAtomicDefault.h"
#endif /* if defined ( EPICS_ATOMIC_INLINE ) */
#endif /* epicsAtomicOSD_h */

View File

@@ -0,0 +1,22 @@
/*************************************************************************\
* Copyright (c) 2011 LANS LLC, as Operator of
* Los Alamos National Laboratory.
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/*
* Author Jeffrey O. Hill
* johill@lanl.gov
*/
#define epicsExportSharedSymbols
#include "epicsAtomic.h"
// if the compiler is unable to inline then instantiate out-of-line
#ifndef EPICS_ATOMIC_INLINE
#define EPICS_ATOMIC_INLINE
#include "epicsAtomic.h"
#endif

View File

@@ -15,13 +15,11 @@
#ifndef epicsAtomicOSD_h
#define epicsAtomicOSD_h
#if defined ( OSD_ATOMIC_INLINE )
#if defined ( EPICS_ATOMIC_INLINE )
#include "vxWorks.h" /* obtain the version of vxWorks */
#include "epicsAssert.h"
#define OSD_ATOMIC_INLINE_DEFINITION
/*
* With vxWorks 6.6 and later we need to use vxAtomicLib
* to implement this functionality correctly on SMP systems
@@ -35,6 +33,22 @@
extern "C" {
#endif /* __cplusplus */
#ifndef EPICS_READ_MEMORY_BARRIER
#define EPICS_READ_MEMORY_BARRIER
EPICS_ATOMIC_INLINE void epicsReadMemoryBarrier ()
{
VX_MEM_BARRIER_R ();
}
#endif
#ifndef EPICS_WRITE_MEMORY_BARRIER
#define EPICS_WRITE_MEMORY_BARRIER
EPICS_ATOMIC_INLINE void epicsWriteMemoryBarrier ()
{
VX_MEM_BARRIER_W ();
}
#endif
/*
* we make the probably correct guess that if ULONG_MAX
* is the same as UINT_MAX then sizeof ( atomic_t )
@@ -48,127 +62,134 @@ extern "C" {
#if ULONG_MAX == UINT_MAX
STATIC_ASSERT ( sizeof ( atomic_t ) == sizeof ( size_t ) );
STATIC_ASSERT ( sizeof ( atomic_t ) == sizeof ( EpicsAtomicPtrT ) );
OSD_ATOMIC_INLINE size_t epicsAtomicIncrSizeT ( size_t * pTarget )
#ifndef EPICS_ATOMIC_INCR_SIZET
#define EPICS_ATOMIC_INCR_SIZET
EPICS_ATOMIC_INLINE size_t epicsAtomicIncrSizeT ( size_t * pTarget )
{
atomic_t * const pTarg = ( atomic_t * ) ( pTarget );
const atomic_t oldVal = vxAtomicInc ( pTarg );
return 1 + ( size_t ) ( oldVal );
}
#endif
#ifndef EPICS_ATOMIC_DECR_SIZET
#define EPICS_ATOMIC_DECR_SIZET
EPICS_ATOMIC_INLINE size_t epicsAtomicDecrSizeT ( size_t * pTarget )
{
atomic_t * const pTarg = ( atomic_t * ) ( pTarget );
const atomic_t oldVal = vxAtomicDec ( pTarg );
return ( ( size_t ) oldVal ) - 1u;
}
#endif
#ifndef EPICS_ATOMIC_ADD_SIZET
#define EPICS_ATOMIC_ADD_SIZET
EPICS_ATOMIC_INLINE size_t epicsAtomicAddSizeT ( size_t * pTarget, size_t delta )
{
/*
* vxAtomicLib doc indicates that vxAtomicInc is
* implemented using unsigned arithmetic
* vxAtomicLib doc indicates that vxAtomicAdd is
* implemented using signed arithmetic, but it
* does not change the end result because twos
* complement addition is used in either case
*/
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 )
const atomic_t oldVal = vxAtomicAdd ( pTarg, (atomic_t) delta );
return delta + ( size_t ) oldVal;
} #endif #ifndef EPICS_ATOMIC_SUB_SIZET
#define EPICS_ATOMIC_SUB_SIZET
EPICS_ATOMIC_INLINE size_t epicsAtomicSubSizeT ( size_t * pTarget, size_t delta )
{
/*
* vxAtomicLib doc indicates that vxAtomicDec is
* implemented using unsigned arithmetic
* vxAtomicLib doc indicates that vxAtomicSub is
* implemented using signed arithmetic, but it
* does not change the end result because twos
* complement subtraction is used in either case
*/
atomic_t * const pTarg = ( atomic_t * ) ( pTarget );
const atomic_t oldVal = vxAtomicDec ( pTarg ) - 1;
return ( ( size_t ) ( oldVal ) ) - 1u;
const atomic_t oldVal = vxAtomicSub ( pTarg, (atomic_t) delta );
return ( ( size_t ) oldVal ) - delta;
}
#endif
OSD_ATOMIC_INLINE void epicsAtomicSetSizeT ( size_t * pTarget, size_t newVal )
#ifndef EPICS_ATOMIC_CAS_SIZET
#define EPICS_ATOMIC_CAS_SIZET
EPICS_ATOMIC_INLINE size_t epicsAtomicCmpAndSwapSizeT ( size_t * pTarget,
size_t oldVal, size_t newVal )
{
atomic_t * const pTarg = ( atomic_t * ) ( pTarget );
vxAtomicSet ( pTarg, newVal );
return ( size_t ) vxCas ( pTarg, (atomic_t) oldVal, (atomic_t) newVal );
}
#endif
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 );
}
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,
#ifndef EPICS_ATOMIC_CAS_PTRT
#define EPICS_ATOMIC_CAS_PTRT
EPICS_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 );
}
#endif
#else /* ULONG_MAX == UINT_MAX */
/*
* if its 64 bit vxWorks and the compiler doesnt
* if its 64 bit SMP 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
* size_t is maybe bigger than atomic_t
*
* I dont yet have access to vxWorks manuals for
* 64 bit systems so this is still undecided, but is
* defaulting now to a global lock
*/
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 void epicsAtomicSetPtrT ( EpicsAtomicPtrT * pTarget, EpicsAtomicPtrT newVal )
{
epicsLockedSetPtrT ( pTarget, newVal );
}
OSD_ATOMIC_INLINE size_t epicsAtomicGetSizeT ( const size_t * pTarget )
{
return epicsLockedGetSizeT ( pTarget );
}
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 ) );
STATIC_ASSERT ( sizeof ( atomic_t ) == sizeof ( int ) );
OSD_ATOMIC_INLINE void epicsAtomicSetUIntT ( unsigned * pTarget, unsigned newVal )
#ifndef EPICS_ATOMIC_INCR_INTT
#define EPICS_ATOMIC_INCR_INTT
EPICS_ATOMIC_INLINE int epicsAtomicIncrIntT ( int * pTarget )
{
atomic_t * const pTarg = ( atomic_t * ) ( pTarget );
vxAtomicSet ( pTarg, newVal );
const atomic_t oldVal = vxAtomicInc ( pTarg );
return 1 + ( int ) oldVal;
}
#endif
OSD_ATOMIC_INLINE unsigned epicsAtomicGetUIntT ( const unsigned * pTarget )
{
STATIC_ASSERT ( sizeof ( atomic_t ) == sizeof ( unsigned ) );
atomic_t * const pTarg = ( atomic_t * ) ( pTarget );
return ( unsigned ) vxAtomicGet ( pTarg );
}
OSD_ATOMIC_INLINE unsigned epicsAtomicCmpAndSwapUIntT ( unsigned * pTarget,
unsigned oldVal, unsigned newVal )
#ifndef EPICS_ATOMIC_DECR_INTT
#define EPICS_ATOMIC_DECR_INTT
EPICS_ATOMIC_INLINE int epicsAtomicDecrIntT ( int * pTarget )
{
atomic_t * const pTarg = ( atomic_t * ) ( pTarget );
return (unsigned) vxCas ( pTarg, (atomic_t) oldVal, (atomic_t) newVal );
const atomic_t oldVal = vxAtomicDec ( pTarg );
return ( ( int ) oldVal ) - 1;
}
#endif
#ifndef EPICS_ATOMIC_ADD_INTT
#define EPICS_ATOMIC_ADD_INTT
EPICS_ATOMIC_INLINE int epicsAtomicAddIntT ( int * pTarget, int delta )
{
atomic_t * const pTarg = ( atomic_t * ) ( pTarget );
const atomic_t oldVal = vxAtomicAdd ( pTarg, (atomic_t) delta );
return delta + ( int ) oldVal;
}
#endif
#ifndef EPICS_ATOMIC_CAS_INTT
#define EPICS_ATOMIC_CAS_INTT
EPICS_ATOMIC_INLINE int epicsAtomicCmpAndSwapIntT ( int * pTarget,
int oldVal, int newVal )
{
atomic_t * const pTarg = ( atomic_t * ) ( pTarget );
return ( int ) vxCas ( pTarg, (atomic_t) oldVal, (atomic_t) newVal );
}
#endif
#ifdef __cplusplus
} /* end of extern "C" */
@@ -183,129 +204,39 @@ OSD_ATOMIC_INLINE unsigned epicsAtomicCmpAndSwapUIntT ( unsigned * pTarget,
extern "C" {
#endif /* __cplusplus */
OSD_ATOMIC_INLINE size_t epicsAtomicIncrSizeT ( size_t * pTarget )
#ifndef EPICS_ATOMIC_LOCK
#define EPICS_ATOMIC_LOCK
typedef struct EpicsAtomicLockKey { int m_key; } EpicsAtomicLockKey;
EPICS_ATOMIC_INLINE void epicsAtomicLock ( EpicsAtomicLockKey * pKey )
{
# ifdef __m68k__
return ++(*pTarget);
# else
/*
* 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;
# endif
pKey->m_key = intLock ();
}
OSD_ATOMIC_INLINE size_t epicsAtomicDecrSizeT ( size_t * pTarget )
EPICS_ATOMIC_INLINE EpicsAtomicLockKey epicsAtomicUnlock ( EpicsAtomicLockKey * pKey )
{
# ifdef __m68k__
return --(*pTarget);
# else
/*
* 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;
intUnlock ( pKey->m_key );
}
#endif
}
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;
}
#ifndef EPICS_READ_MEMORY_BARRIER
#define EPICS_READ_MEMORY_BARRIER
/*
* no need for memory barrior since prior to vxWorks 6.6 it is a single cpu system
* (we are not protecting against multiple access to memory mapped IO)
*/
EPICS_ATOMIC_INLINE void epicsReadMemoryBarrier () {}
#endif
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 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 )
{
/*
* no need for memory barrior since this
* is a single cpu system
*/
return *pTarget;
}
OSD_ATOMIC_INLINE unsigned epicsAtomicGetUIntT ( const unsigned * pTarget )
{
/*
* no need for memory barrior since this
* is a single cpu system
*/
return *pTarget;
}
OSD_ATOMIC_INLINE EpicsAtomicPtrT epicsAtomicGetPtrT ( const EpicsAtomicPtrT * 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;
}
#ifndef EPICS_WRITE_MEMORY_BARRIER
#define EPICS_WRITE_MEMORY_BARRIER
/*
* no need for memory barrior since prior to vxWorks 6.6 it is a single cpu system
* (we are not protecting against multiple access to memory mapped IO)
*/
EPICS_ATOMIC_INLINE void epicsWriteMemoryBarrier () {}
#endif
#ifdef __cplusplus
} /* end of extern "C" */
@@ -313,6 +244,9 @@ OSD_ATOMIC_INLINE EpicsAtomicPtrT epicsAtomicCmpAndSwapPtrT ( EpicsAtomicPtrT *
#endif /* _WRS_VXWORKS_MAJOR * 100 + _WRS_VXWORKS_MINOR >= 606 */
#endif /* if defined ( OSD_ATOMIC_INLINE ) */
#include "epicsAtomicDefault.h"
#endif /* if defined ( EPICS_ATOMIC_INLINE ) */
#endif /* epicsAtomicOSD_h */

View File

@@ -109,8 +109,8 @@ testHarness_SRCS += epicsMutexTest.cpp
TESTS += epicsMutexTest
TESTPROD_HOST += epicsAtomicTest
epicsAtomicTest_SRCS += epicsAtomicTest.c
testHarness_SRCS += epicsAtomicTest.c
epicsAtomicTest_SRCS += epicsAtomicTest.cpp
testHarness_SRCS += epicsAtomicTest.cpp
TESTS += epicsAtomicTest
TESTPROD_HOST += epicsExceptionTest

View File

@@ -1,6 +1,8 @@
#include <cstdlib>
#include <cstdio>
#include <cassert>
#include <typeinfo>
#include "epicsInterrupt.h"
#include "epicsAtomic.h"
@@ -9,6 +11,8 @@
#include "testMain.h"
using std :: size_t;
using namespace epics;
using namespace atomic;
class RefCtr {
public:
@@ -214,234 +218,184 @@ inline void passRefOwnership1000 ( const Ownership & ownershipIn, Ownership & ow
time_t extTime = 0;
template < class T >
class OrdinaryIncr {
public:
OrdinaryIncr () : m_target ( 0 ) {}
void run ();
void diagnostic ( double delay );
private:
T m_target;
};
// 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 )
template < class T >
inline void OrdinaryIncr < T > :: run ()
{
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 );
m_target += epicsInterruptIsInterruptContext ();
m_target += epicsInterruptIsInterruptContext ();
m_target += epicsInterruptIsInterruptContext ();
m_target += epicsInterruptIsInterruptContext ();
m_target += epicsInterruptIsInterruptContext ();
m_target += epicsInterruptIsInterruptContext ();
m_target += epicsInterruptIsInterruptContext ();
m_target += epicsInterruptIsInterruptContext ();
m_target += epicsInterruptIsInterruptContext ();
m_target += epicsInterruptIsInterruptContext ();
}
inline void oneHundredOrdinaryIncr ( size_t & target )
template < class T >
void OrdinaryIncr < T > :: diagnostic ( double delay )
{
tenOrdinaryIncr ( target );
tenOrdinaryIncr ( target );
tenOrdinaryIncr ( target );
tenOrdinaryIncr ( target );
tenOrdinaryIncr ( target );
tenOrdinaryIncr ( target );
tenOrdinaryIncr ( target );
tenOrdinaryIncr ( target );
tenOrdinaryIncr ( target );
tenOrdinaryIncr ( target );
delay /= 10.0;
delay *= 1e6;
const char * const pName = typeid ( T ) . name ();
testDiag ( "raw incr of \"%s\" and a NOOP function call takes %f microseconds",
pName, delay );
}
inline void oneThousandOrdinaryIncr ( size_t & target )
template < class T >
class AtomicIncr {
public:
AtomicIncr () : m_target ( 0 ) {}
void run ();
void diagnostic ( double delay );
private:
T m_target;
};
template < class T >
inline void AtomicIncr < T > :: run ()
{
oneHundredOrdinaryIncr ( target );
oneHundredOrdinaryIncr ( target );
oneHundredOrdinaryIncr ( target );
oneHundredOrdinaryIncr ( target );
oneHundredOrdinaryIncr ( target );
oneHundredOrdinaryIncr ( target );
oneHundredOrdinaryIncr ( target );
oneHundredOrdinaryIncr ( target );
oneHundredOrdinaryIncr ( target );
oneHundredOrdinaryIncr ( target );
increment ( m_target );
increment ( m_target );
increment ( m_target );
increment ( m_target );
increment ( m_target );
increment ( m_target );
increment ( m_target );
increment ( m_target );
increment ( m_target );
increment ( m_target );
}
inline void tenAtomicIncr ( size_t & target )
template < class T >
void AtomicIncr < T > :: diagnostic ( double delay )
{
epicsAtomicIncrSizeT ( & target );
epicsAtomicIncrSizeT ( & target );
epicsAtomicIncrSizeT ( & target );
epicsAtomicIncrSizeT ( & target );
epicsAtomicIncrSizeT ( & target );
epicsAtomicIncrSizeT ( & target );
epicsAtomicIncrSizeT ( & target );
epicsAtomicIncrSizeT ( & target );
epicsAtomicIncrSizeT ( & target );
epicsAtomicIncrSizeT ( & target );
delay /= 10.0;
delay *= 1e6;
const char * const pName = typeid ( T ) . name ();
testDiag ( "epicsAtomicIncr \"%s\" takes %f microseconds",
pName, delay );
}
inline void oneHundredAtomicIncr ( size_t & target )
template < class T > T trueValue ();
template < class T > T falseValue ();
// int
template <>
inline int trueValue < int > () { return 1; }
template <>
inline int falseValue < int > () { return 0; }
// size_t
template <>
inline size_t trueValue < size_t > () { return 1u; }
template <>
inline size_t falseValue < size_t > () { return 0u; }
// EpicsAtomicPtrT
template <>
inline EpicsAtomicPtrT trueValue < EpicsAtomicPtrT > ()
{ static char c; return & c; }
template <>
inline EpicsAtomicPtrT falseValue < EpicsAtomicPtrT > ()
{ return 0u; }
template < class T >
class AtomicCmpAndSwap {
public:
AtomicCmpAndSwap () : m_target ( 0 ) {}
void run ();
void diagnostic ( double delay );
private:
T m_target;
};
template < class T >
inline void AtomicCmpAndSwap < T > :: run ()
{
tenAtomicIncr ( target );
tenAtomicIncr ( target );
tenAtomicIncr ( target );
tenAtomicIncr ( target );
tenAtomicIncr ( target );
tenAtomicIncr ( target );
tenAtomicIncr ( target );
tenAtomicIncr ( target );
tenAtomicIncr ( target );
tenAtomicIncr ( target );
compareAndSwap ( m_target, falseValue < T > (), trueValue < T > () );
compareAndSwap ( m_target, falseValue < T > (), trueValue < T > () );
compareAndSwap ( m_target, falseValue < T > (), trueValue < T > () );
compareAndSwap ( m_target, falseValue < T > (), trueValue < T > () );
compareAndSwap ( m_target, falseValue < T > (), trueValue < T > () );
compareAndSwap ( m_target, falseValue < T > (), trueValue < T > () );
compareAndSwap ( m_target, falseValue < T > (), trueValue < T > () );
compareAndSwap ( m_target, falseValue < T > (), trueValue < T > () );
compareAndSwap ( m_target, falseValue < T > (), trueValue < T > () );
compareAndSwap ( m_target, falseValue < T > (), trueValue < T > () );
}
inline void oneThousandAtomicIncr ( size_t & target )
template < class T >
void AtomicCmpAndSwap < T > :: diagnostic ( double delay )
{
oneHundredAtomicIncr ( target );
oneHundredAtomicIncr ( target );
oneHundredAtomicIncr ( target );
oneHundredAtomicIncr ( target );
oneHundredAtomicIncr ( target );
oneHundredAtomicIncr ( target );
oneHundredAtomicIncr ( target );
oneHundredAtomicIncr ( target );
oneHundredAtomicIncr ( target );
oneHundredAtomicIncr ( target );
delay /= 10.0;
delay *= 1e6;
const char * const pName = typeid ( T ) . name ();
testDiag ( "epicsAtomicCmpAndSwap of \"%s\" takes %f microseconds",
pName, delay );
}
inline void tenAtomicCmpAndSwap ( unsigned & target )
template < class T >
class AtomicSet {
public:
AtomicSet () : m_target ( 0 ) {}
void run ();
void diagnostic ( double delay );
private:
T m_target;
};
template < class T >
inline void AtomicSet < T > :: run ()
{
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 );
set ( m_target, 0 );
set ( m_target, 0 );
set ( m_target, 0 );
set ( m_target, 0 );
set ( m_target, 0 );
set ( m_target, 0 );
set ( m_target, 0 );
set ( m_target, 0 );
set ( m_target, 0 );
set ( m_target, 0 );
}
inline void oneHundredAtomicCmpAndSwap ( unsigned & target )
template < class T >
void AtomicSet < T > :: diagnostic ( double delay )
{
tenAtomicCmpAndSwap ( target );
tenAtomicCmpAndSwap ( target );
tenAtomicCmpAndSwap ( target );
tenAtomicCmpAndSwap ( target );
tenAtomicCmpAndSwap ( target );
tenAtomicCmpAndSwap ( target );
tenAtomicCmpAndSwap ( target );
tenAtomicCmpAndSwap ( target );
tenAtomicCmpAndSwap ( target );
tenAtomicCmpAndSwap ( target );
}
inline void oneThousandAtomicCmpAndSwap ( unsigned & 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 )
{
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 );
delay /= 10.0;
delay *= 1e6;
const char * const pName = typeid ( T ) . name ();
testDiag ( "epicsAtomicSet of \"%s\" takes %f microseconds",
pName, delay );
}
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 atomicCmpAndSwapPerformance ()
{
epicsTime begin = epicsTime::getCurrent ();
unsigned target;
epicsAtomicSetUIntT ( & target, 0 );
testOk1 ( ! target );
for ( unsigned i = 0; i < N; i++ ) {
oneThousandAtomicCmpAndSwap ( target );
}
double delay = epicsTime::getCurrent () - begin;
testOk1 ( target );
delay /= N * 1000u; // convert to delay per call
delay *= 1e6; // convert to micro seconds
testDiag ( "epicsAtomicCmpAndSwap() takes %f microseconds", delay );
}
void recursiveOwnershipRetPerformance ()
{
RefCtr refCtr;
epicsTime begin = epicsTime::getCurrent ();
for ( unsigned i = 0; i < N; i++ ) {
for ( size_t i = 0; i < N; i++ ) {
Ownership ownership ( refCtr );
recurRetOwner1000 ( ownership );
}
@@ -455,7 +409,7 @@ void ownershipPassRefPerformance ()
{
RefCtr refCtr;
epicsTime begin = epicsTime::getCurrent ();
for ( unsigned i = 0; i < N; i++ ) {
for ( size_t i = 0; i < N; i++ ) {
Ownership ownershipSrc ( refCtr );
Ownership ownershipDest;
passRefOwnership1000 ( ownershipSrc, ownershipDest );
@@ -466,34 +420,87 @@ void ownershipPassRefPerformance ()
testDiag ( "passRefOwnership() takes %f microseconds", delay );
}
void epicsAtomicSetPerformance ()
template < class T >
class Ten
{
epicsTime begin = epicsTime::getCurrent ();
size_t 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 );
public:
void run ();
void diagnostic ( double delay );
typedef Ten < Ten < T > > Hundred;
typedef Ten < Hundred > Thousand;
private:
T m_target;
};
template < class T >
inline void Ten < T > :: run ()
{
m_target.run ();
m_target.run ();
m_target.run ();
m_target.run ();
m_target.run ();
m_target.run ();
m_target.run ();
m_target.run ();
m_target.run ();
m_target.run ();
}
MAIN(epicsAtomicPerform)
template < class T >
void Ten < T > :: diagnostic ( double delay )
{
testPlan(5);
m_target.diagnostic ( delay / 10.0 );
}
template < class T >
void measurePerformance ()
{
epicsTime begin = epicsTime::getCurrent ();
T target;
for ( size_t i = 0; i < N; i++ ) {
target.run ();
target.run ();
target.run ();
target.run ();
target.run ();
target.run ();
target.run ();
target.run ();
target.run ();
target.run ();
}
double delay = epicsTime::getCurrent () - begin;
delay /= ( N * 10u ); // convert to delay per call
target.diagnostic ( delay );
}
template < class T >
void measure ()
{
measurePerformance < typename Ten < T > :: Hundred > ();
}
MAIN ( epicsAtomicPerform )
{
testPlan ( 0 );
//
// 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
// virtual pages perhap
//
epicsAtomicSetPerformance ();
ordinaryIncrPerformance ();
epicsAtomicIncrPerformance ();
measure < AtomicSet < int > > ();
measure < AtomicSet < size_t > > ();
measure < AtomicSet < void * > > ();
measure < OrdinaryIncr < int > > ();
measure < OrdinaryIncr < size_t > > ();
measure < AtomicIncr < int > > ();
measure < AtomicIncr < size_t > > ();
measure < AtomicCmpAndSwap < int > > ();
measure < AtomicCmpAndSwap < size_t > > ();
measure < AtomicCmpAndSwap < void * > > ();
recursiveOwnershipRetPerformance ();
ownershipPassRefPerformance ();
atomicCmpAndSwapPerformance ();
return testDone();
}

View File

@@ -1,175 +0,0 @@
#include <stdlib.h>
#include <assert.h>
#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_UIntT {
unsigned m_testValue;
size_t m_testIterationsSet;
size_t m_testIterationsNotSet;
} 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_UIntT ( void *arg )
{
TestDataTNS_UIntT * const pTestData = (TestDataTNS_UIntT *) arg;
/*
* intentionally waste cpu and maximize
* contention for the shared data
*/
epicsAtomicIncrSizeT ( & pTestData->m_testIterationsNotSet );
while ( ! epicsAtomicCmpAndSwapUIntT ( & pTestData->m_testValue, 0u, 1u ) ) {
}
epicsAtomicDecrSizeT ( & pTestData->m_testIterationsNotSet );
epicsAtomicSetUIntT ( & pTestData->m_testValue, 0u );
epicsAtomicIncrSizeT ( & pTestData->m_testIterationsSet );
}
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 );
testPlan(14);
{
static const size_t N = 100;
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 );
}
{
static const unsigned initVal = 1;
static const size_t N = 10;
size_t i;
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 );
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_UIntT, & 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 );
}
{
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();
}

View File

@@ -0,0 +1,238 @@
#include <stdlib.h>
#include <assert.h>
#include "epicsAtomic.h"
#include "epicsTime.h"
#include "epicsThread.h"
#include "epicsUnitTest.h"
#include "testMain.h"
using namespace epics;
using namespace atomic;
template < class T >
struct TestDataIncrDecr {
T m_testValue;
size_t m_testIterations;
};
template < class T >
struct TestDataAddSub {
T m_testValue;
size_t m_testIterations;
static const T delta = 17;
};
template < class T >
static void incr ( void *arg )
{
TestDataIncrDecr < T > * const pTestData =
reinterpret_cast < TestDataIncrDecr < T > * > ( arg );
increment ( pTestData->m_testValue );
increment ( pTestData->m_testIterations );
}
template < class T >
static void decr ( void *arg )
{
TestDataIncrDecr < T > * const pTestData =
reinterpret_cast < TestDataIncrDecr < T > * > ( arg );
decrement ( pTestData->m_testValue );
increment ( pTestData->m_testIterations );
}
template < class T >
static void add ( void *arg )
{
TestDataAddSub < T > * const pTestData =
reinterpret_cast < TestDataAddSub < T > * > ( arg );
add ( pTestData->m_testValue, TestDataAddSub < T > :: delta );
increment ( pTestData->m_testIterations );
}
template < class T >
static void sub ( void *arg )
{
TestDataAddSub < T > * const pTestData =
reinterpret_cast < TestDataAddSub < T > * > ( arg );
subtract ( pTestData->m_testValue, TestDataAddSub < T > :: delta );
increment ( pTestData->m_testIterations );
}
template < class T >
struct TestDataCAS {
T m_testValue;
size_t m_testIterationsSet;
size_t m_testIterationsNotSet;
};
int isModulo ( size_t N, size_t n )
{
return ( n % N ) == 0u;
}
template < class T >
static T trueValue ();
template < class T >
static T falseValue ();
// int
template <>
inline int trueValue < int > () { return 1; }
template <>
inline int falseValue < int > () { return 0; }
// size_t
template <>
inline size_t trueValue < size_t > () { return 1u; }
template <>
inline size_t falseValue < size_t > () { return 0u; }
// EpicsAtomicPtrT
template <>
inline EpicsAtomicPtrT trueValue < EpicsAtomicPtrT > ()
{ static char c; return & c; }
template <>
inline EpicsAtomicPtrT falseValue < EpicsAtomicPtrT > ()
{ return 0u; }
template < class T >
static void cas ( void *arg )
{
TestDataCAS < T > * const pTestData =
reinterpret_cast < TestDataCAS < T > * > ( arg );
/*
* intentionally waste cpu and maximize
* contention for the shared data
*/
increment ( pTestData->m_testIterationsNotSet );
while ( ! compareAndSwap ( pTestData->m_testValue,
falseValue < T > (),
trueValue < T > () ) ) {
}
decrement ( pTestData->m_testIterationsNotSet );
set ( pTestData->m_testValue, falseValue < T > () );
increment ( pTestData->m_testIterationsSet );
}
template < class T >
void testIncrDecr ()
{
static const size_t N = 100;
static const T NT = static_cast < T > ( N );
const unsigned int stackSize =
epicsThreadGetStackSize ( epicsThreadStackSmall );
TestDataIncrDecr < T > testData = { 0, N };
set ( testData.m_testValue, NT );
testOk ( get ( testData.m_testValue ) == NT,
"set/get %u", testData.m_testValue );
set ( testData.m_testIterations, 0u );
testOk ( get ( testData.m_testIterations ) == 0u,
"set/get %u", testData.m_testIterations );
for ( size_t i = 0u; i < N; i++ ) {
epicsThreadCreate ( "incr",
50, stackSize, incr < T >, & testData );
epicsThreadCreate ( "decr",
50, stackSize, decr < T >, & testData );
}
while ( testData.m_testIterations < 2 * N ) {
epicsThreadSleep ( 0.01 );
}
testOk ( get ( testData.m_testIterations ) == 2 * N,
"incr/decr iterations %u",
testData.m_testIterations );
testOk ( get ( testData.m_testValue ) == NT,
"incr/decr final value %u",
testData.m_testValue );
}
template < class T >
void testAddSub ()
{
static const size_t N = 100;
static const T NDT = TestDataAddSub < T > :: delta *
static_cast < T > ( N );
const unsigned int stackSize =
epicsThreadGetStackSize ( epicsThreadStackSmall );
TestDataIncrDecr < T > testData = { 0, N };
set ( testData.m_testValue, NDT );
testOk ( get ( testData.m_testValue ) == NDT,
"set/get %u", testData.m_testValue );
set ( testData.m_testIterations, 0u );
testOk ( get ( testData.m_testIterations ) == 0u,
"set/get %u", testData.m_testIterations );
for ( size_t i = 0u; i < N; i++ ) {
epicsThreadCreate ( "add",
50, stackSize, add < T >, & testData );
epicsThreadCreate ( "sub",
50, stackSize, sub < T >, & testData );
}
while ( testData.m_testIterations < 2 * N ) {
epicsThreadSleep ( 0.01 );
}
testOk ( get ( testData.m_testIterations ) == 2 * N,
"add/sub iterations %u",
testData.m_testIterations );
testOk ( get ( testData.m_testValue ) == NDT,
"add/sub final value %u",
testData.m_testValue );
}
template < class T >
void testCAS ()
{
static const size_t N = 10;
const unsigned int stackSize =
epicsThreadGetStackSize ( epicsThreadStackSmall );
TestDataCAS < T > testData = { 0, N, N };
set ( testData.m_testIterationsSet, 0 );
testOk ( get ( testData.m_testIterationsSet ) == 0u,
"set/get %u", testData.m_testIterationsSet );
set ( testData.m_testIterationsNotSet, 0 );
testOk ( get ( testData.m_testIterationsNotSet ) == 0u,
"set/get %u", testData.m_testIterationsNotSet );
set ( testData.m_testValue, trueValue < T > () );
testOk ( get ( testData.m_testValue ) == trueValue < T > (),
"set/get a true value" );
for ( size_t i = 0u; i < N; i++ ) {
epicsThreadCreate ( "tns",
50, stackSize, cas < T >, & testData );
}
set ( testData.m_testValue, falseValue < T > () );
while ( testData.m_testIterationsSet < N ) {
epicsThreadSleep ( 0.01 );
}
testOk ( get ( testData.m_testIterationsSet ) == N,
"test and set iterations %u",
testData.m_testIterationsSet );
testOk ( get ( testData.m_testIterationsNotSet ) == 0u,
"test and set not-set tracking = %u",
testData.m_testIterationsNotSet );
}
MAIN ( osiAtomicTest )
{
testPlan ( 31 );
testIncrDecr < int > ();
testIncrDecr < size_t > ();
testAddSub < int > ();
testAddSub < size_t > ();
testCAS < int > ();
testCAS < size_t > ();
testCAS < EpicsAtomicPtrT > ();
return testDone ();
}