diff --git a/configure/RULES_BUILD b/configure/RULES_BUILD index d384f03d2..3ec14ab63 100644 --- a/configure/RULES_BUILD +++ b/configure/RULES_BUILD @@ -410,7 +410,7 @@ $(INSTALL_INCLUDE)/% : % $(INSTALL_INCLUDE)/os/$(OS_CLASS)/% : % $(ECHO) "Installing OS dependent include file $@" @$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D) - + $(INSTALL_INCLUDE)/compiler/$(CMPLR_CLASS)/% : % $(ECHO) "Installing compiler dependent include file $@" @$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D) diff --git a/configure/os/CONFIG.win32-x86.win32-x86 b/configure/os/CONFIG.win32-x86.win32-x86 index 86b9510ac..da07db685 100644 --- a/configure/os/CONFIG.win32-x86.win32-x86 +++ b/configure/os/CONFIG.win32-x86.win32-x86 @@ -124,7 +124,7 @@ OPT_CXXFLAGS_YES = /Ox /GL # /D_CRTDBG_MAP_ALLOC # /RTCsu catch bugs occurring only in optimized code # /DEPICS_FREELIST_DEBUG good for detecting mem mrg bugs -OPT_CXXFLAGS_NO = /RTCsu /Zi +OPT_CXXFLAGS_NO = /RTCsu /Zi # specify object file name and location OBJ_CXXFLAG = /Fo diff --git a/documentation/README.html b/documentation/README.html index e48e91fd3..862d488ec 100644 --- a/documentation/README.html +++ b/documentation/README.html @@ -88,15 +88,18 @@ as processes on the host platform.

vxWorks
- You must have vxWorks installed if any of your target systems are - vxWorks systems. This provides the cross-compiler and header files - needed to build for these target systems. The absolute path to and version - number of the vxWorks installation is normally specified in the - base/configure/os/CONFIG_SITE.Common.vxWorksCommon file. Consult the EPICS web - pages about vxWorks - 5.x and vxWorks - 6.x and the vxWorks documentation for information about configuring your - vxWorks operating system for use with EPICS.

+ You must have vxWorks 5.5.x or 6.x installed if any of your target systems are + vxWorks systems; the C++ compiler for vxWorks 5.4 is now too old to support. + The vxWorks installation provides the cross-compiler and header files needed to + build for these targets. The absolute path to and the version number of the + vxWorks installation must be set in the + base/configure/os/CONFIG_SITE.Common.vxWorksCommon file or in one of its + target-specific overrides.

+ +

Consult the vxWorks + 5.x or vxWorks + 6.x EPICS web pages about and the vxWorks documentation for information + about configuring your vxWorks operating system for use with EPICS.

RTEMS
For RTEMS targets, you need RTEMS core and toolset version 4.9.2 or later.

diff --git a/src/libCom/osi/Makefile b/src/libCom/osi/Makefile index 1f0ca4cdf..9a0c80c99 100644 --- a/src/libCom/osi/Makefile +++ b/src/libCom/osi/Makefile @@ -44,6 +44,10 @@ INC += osiUnistd.h INC += osiWireFormat.h INC += osdWireFormat.h INC += osdWireConfig.h +INC += epicsAtomic.h +INC += epicsAtomicDefault.h +INC += epicsAtomicOSD.h +INC += epicsAtomicCD.h INC += epicsEndian.h INC += epicsReadline.h INC += epicsMessageQueue.h @@ -62,6 +66,7 @@ Com_SRCS += epicsEvent.cpp Com_SRCS += epicsTime.cpp Com_SRCS += epicsMessageQueue.cpp Com_SRCS += epicsMath.cpp +Com_SRCS += epicsAtomicOSD.cpp Com_SRCS += epicsGeneralTime.c @@ -125,6 +130,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 diff --git a/src/libCom/osi/compiler/clang/epicsAtomicCD.h b/src/libCom/osi/compiler/clang/epicsAtomicCD.h new file mode 100644 index 000000000..00fa24e4b --- /dev/null +++ b/src/libCom/osi/compiler/clang/epicsAtomicCD.h @@ -0,0 +1,31 @@ + +/*************************************************************************\ +* 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 epicsAtomicCD_h +#define epicsAtomicCD_h + +#if defined ( __cplusplus ) +# define EPICS_ATOMIC_INLINE inline +#else +# define EPICS_ATOMIC_INLINE __inline__ +#endif + +/* + * we have an inline keyword so we can proceed + * with an os specific inline instantiation + */ +#include "epicsAtomicOSD.h" + +#endif /* epicsAtomicCD_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..3ca260759 --- /dev/null +++ b/src/libCom/osi/compiler/default/epicsAtomicCD.h @@ -0,0 +1,30 @@ + +/*************************************************************************\ +* 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 epicsAtomicCD_h +#define epicsAtomicCD_h + +#if __STDC_VERSION__ >= 199901L || defined ( __cplusplus ) +# define EPICS_ATOMIC_INLINE inline + /* + * We have already defined the public interface in epicsAtomic.h + * so there is nothing more to implement if there isnt an inline + * keyword available. Otherwise, if we have an inline keyword + * we will proceed with an os specific inline implementation. + */ +# include "epicsAtomicOSD.h" +#endif + +#endif /* epicsAtomicCD_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..055d8be52 --- /dev/null +++ b/src/libCom/osi/compiler/gcc/epicsAtomicCD.h @@ -0,0 +1,172 @@ + +/*************************************************************************\ +* 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 epicsAtomicCD_h +#define epicsAtomicCD_h + +#ifndef __GNUC__ +# error this header is only for use with the gnu compiler +#endif + +#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_INT_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__ ) + +#define GCC_ATOMIC_INTRINSICS_MIN_X86 \ + ( defined ( __i486 ) || defined ( __pentium ) || \ + defined ( __pentiumpro ) || defined ( __MMX__ ) ) + +#define GCC_ATOMIC_INTRINSICS_GCC4_OR_BETTER \ + ( ( __GNUC__ * 100 + __GNUC_MINOR__ ) >= 401 ) + +#define GCC_ATOMIC_INTRINSICS_AVAIL_EARLIER \ + ( GCC_ATOMIC_INTRINSICS_MIN_X86 && \ + GCC_ATOMIC_INTRINSICS_GCC4_OR_BETTER ) + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * 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 + +#ifndef EPICS_ATOMIC_READ_MEMORY_BARRIER +#define EPICS_ATOMIC_READ_MEMORY_BARRIER +EPICS_ATOMIC_INLINE void epicsAtomicReadMemoryBarrier () +{ + __sync_synchronize (); +} +#endif + +#ifndef EPICS_ATOMIC_WRITE_MEMORY_BARRIER +#define EPICS_ATOMIC_WRITE_MEMORY_BARRIER +EPICS_ATOMIC_INLINE void epicsAtomicWriteMemoryBarrier () +{ + __sync_synchronize (); +} +#endif + +#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 ) +{ + return __sync_add_and_fetch ( pTarget, 1 ); +} + +#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); +} + +#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 + +/* + * if currently unavailable as gcc intrinsics we + * will try for an os specific inline solution + */ +#include "epicsAtomicOSD.h" + +#endif /* epicsAtomicCD_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..f834f0405 --- /dev/null +++ b/src/libCom/osi/compiler/msvc/epicsAtomicCD.h @@ -0,0 +1,124 @@ + +/*************************************************************************\ +* 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 epicsAtomicCD_h +#define epicsAtomicCD_h + +#include "epicsAssert.h" + +#ifndef _MSC_VER +# error this header file is only for use with with the Microsoft Compiler +#endif + +#ifdef _MSC_EXTENSIONS + +#include + +#if _MSC_VER >= 1200 +# define EPICS_ATOMIC_INLINE __forceinline +#else +# define EPICS_ATOMIC_INLINE __inline +#endif + +#if defined ( _M_IX86 ) +# pragma warning( push ) +# pragma warning( disable : 4793 ) + EPICS_ATOMIC_INLINE void epicsAtomicMemoryBarrier () + { + long fence; + __asm { xchg fence, eax } + } +# pragma warning( pop ) +#elif defined ( _M_X64 ) +# define MS_ATOMIC_64 +# pragma intrinsic ( __faststorefence ) + EPICS_ATOMIC_INLINE void epicsAtomicMemoryBarrier () + { + __faststorefence (); + } +#elif defined ( _M_IA64 ) +# define MS_ATOMIC_64 +# pragma intrinsic ( __mf ) + EPICS_ATOMIC_INLINE void epicsAtomicMemoryBarrier () + { + __mf (); + } +#else +# error unexpected target architecture, msvc version of epicsAtomicCD.h +#endif + +/* + * The windows doc appears to recommend defining InterlockedExchange + * to be _InterlockedExchange to cause it to be an intrinsic, but that + * creates issues when later, in a windows os specific header, we include + * 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 */ + +#define EPICS_ATOMIC_READ_MEMORY_BARRIER +EPICS_ATOMIC_INLINE void epicsAtomicReadMemoryBarrier () +{ + epicsAtomicMemoryBarrier (); +} + +#define EPICS_ATOMIC_WRITE_MEMORY_BARRIER +EPICS_ATOMIC_INLINE void epicsAtomicWriteMemoryBarrier () +{ + epicsAtomicMemoryBarrier (); +} + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif /* __cplusplus */ + +#include "epicsAtomicMS.h" +#include "epicsAtomicDefault.h" + +#else /* ifdef _MSC_EXTENSIONS */ + +#if defined ( __cplusplus ) +# define EPICS_ATOMIC_INLINE inline +#endif + +/* + * if unavailable as an intrinsic we will try + * for os specific inline solution + */ +#include "epicsAtomicOSD.h" + +#endif /* ifdef _MSC_EXTENSIONS */ + +#endif /* epicsAtomicCD_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..60083cf71 --- /dev/null +++ b/src/libCom/osi/compiler/solStudio/epicsAtomicCD.h @@ -0,0 +1,31 @@ + +/*************************************************************************\ +* 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 epicsAtomicCD_h +#define epicsAtomicCD_h + +#if defined ( __cplusplus ) +# define EPICS_ATOMIC_INLINE inline +#else +# define EPICS_ATOMIC_INLINE __inline +#endif + +/* + * we have an inline keyword so we can proceed + * with an os specific inline instantiation + */ +#include "epicsAtomicOSD.h" + +#endif /* epicsAtomicCD_h */ diff --git a/src/libCom/osi/epicsAtomic.h b/src/libCom/osi/epicsAtomic.h new file mode 100644 index 000000000..c4ff6d65f --- /dev/null +++ b/src/libCom/osi/epicsAtomic.h @@ -0,0 +1,236 @@ +/*************************************************************************\ +* 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 + +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, + * 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 + */ +epicsShareFunc size_t epicsAtomicIncrSizeT ( size_t * pTarget ); +epicsShareFunc int epicsAtomicIncrIntT ( int * pTarget ); + +/* + * lock out other smp processors from accessing 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 + */ +epicsShareFunc size_t epicsAtomicDecrSizeT ( size_t * pTarget ); +epicsShareFunc int epicsAtomicDecrIntT ( int * pTarget ); + +/* + * 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 epicsAtomicSetIntT ( int * pTarget, int newValue ); +epicsShareFunc void epicsAtomicSetPtrT ( EpicsAtomicPtrT * pTarget, EpicsAtomicPtrT newValue ); + +/* + * fetch target into cache, return new value of target + */ +epicsShareFunc size_t epicsAtomicGetSizeT ( const size_t * pTarget ); +epicsShareFunc int epicsAtomicGetIntT ( const int * pTarget ); +epicsShareFunc EpicsAtomicPtrT epicsAtomicGetPtrT ( const EpicsAtomicPtrT * pTarget ); + +/* + * lock out other smp processors from accessing the target, + * 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 + */ +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" */ +#endif + +/* + * options for inline compiler instrinsic or os specific + * implementations of the above function prototypes + * + * for some of the compilers we must define the + * inline functions before they get used in the c++ + * inine functions 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 */ diff --git a/src/libCom/osi/epicsAtomicDefault.h b/src/libCom/osi/epicsAtomicDefault.h new file mode 100644 index 000000000..ec0975daf --- /dev/null +++ b/src/libCom/osi/epicsAtomicDefault.h @@ -0,0 +1,227 @@ + +/*************************************************************************\ +* 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 + +#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 /* epicsAtomicDefault_h */ + + diff --git a/src/libCom/osi/os/WIN32/epicsAtomicMS.h b/src/libCom/osi/os/WIN32/epicsAtomicMS.h new file mode 100644 index 000000000..14d2c9bac --- /dev/null +++ b/src/libCom/osi/os/WIN32/epicsAtomicMS.h @@ -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 */ + diff --git a/src/libCom/osi/os/WIN32/epicsAtomicOSD.cpp b/src/libCom/osi/os/WIN32/epicsAtomicOSD.cpp new file mode 100644 index 000000000..e3f684e29 --- /dev/null +++ b/src/libCom/osi/os/WIN32/epicsAtomicOSD.cpp @@ -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 "epicsAtomicOSD.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..00b6cc606 --- /dev/null +++ b/src/libCom/osi/os/WIN32/epicsAtomicOSD.h @@ -0,0 +1,47 @@ + +/*************************************************************************\ +* 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 epicsAtomicOSD_h +#define epicsAtomicOSD_h + +#define VC_EXTRALEAN +#define STRICT +#include "windows.h" + +#if defined ( _WIN64 ) +# define MS_ATOMIC_64 +#endif + +#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 + +#include "epicsAtomicMS.h" +#include "epicsAtomicDefault.h" + +#endif /* epicsAtomicOSD_h */ + diff --git a/src/libCom/osi/os/posix/epicsAtomicOSD.cpp b/src/libCom/osi/os/posix/epicsAtomicOSD.cpp new file mode 100644 index 000000000..a8173f894 --- /dev/null +++ b/src/libCom/osi/os/posix/epicsAtomicOSD.cpp @@ -0,0 +1,99 @@ + +/*************************************************************************\ +* 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 + */ + +#include +#include +#include + +#define epicsExportSharedSymbols +#include "epicsAssert.h" +#include "epicsAtomic.h" + +// if the compiler is unable to inline then instantiate out-of-line +#ifndef EPICS_ATOMIC_INLINE +# define EPICS_ATOMIC_INLINE +# include "epicsAtomicOSD.h" +#endif + +#ifndef EPICS_ATOMIC_LOCK + +/* + * 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; + +void epicsAtomicLock ( EpicsAtomicLockKey * ) +{ + unsigned countDown = 1000u; + int status; + while ( true ) { + status = pthread_mutex_lock ( & mutex ); + if ( status == 0 ) return; + assert ( status == EINTR ); + static const useconds_t retryDelayUSec = 100000; + usleep ( retryDelayUSec ); + countDown--; + assert ( countDown ); + } +} + +void epicsAtomicUnlock ( EpicsAtomicLockKey * ) +{ + const int status = pthread_mutex_unlock ( & mutex ); + assert ( status == 0 ); +} + +#endif // ifndef EPICS_ATOMIC_LOCK + +#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 + diff --git a/src/libCom/osi/os/posix/epicsAtomicOSD.h b/src/libCom/osi/os/posix/epicsAtomicOSD.h new file mode 100644 index 000000000..76c70cd54 --- /dev/null +++ b/src/libCom/osi/os/posix/epicsAtomicOSD.h @@ -0,0 +1,35 @@ + +/*************************************************************************\ +* 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 epicsAtomicOSD_h +#define epicsAtomicOSD_h + +struct EpicsAtomicLockKey {}; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +epicsShareFunc void epicsAtomicLock ( struct EpicsAtomicLockKey * ); +epicsShareFunc void epicsAtomicUnlock ( struct EpicsAtomicLockKey * ); + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif /* __cplusplus */ + +#include "epicsAtomicDefault.h" + +#endif /* epicsAtomicOSD_h */ + diff --git a/src/libCom/osi/os/solaris/epicsAtomicOSD.h b/src/libCom/osi/os/solaris/epicsAtomicOSD.h new file mode 100644 index 000000000..5845b6b39 --- /dev/null +++ b/src/libCom/osi/os/solaris/epicsAtomicOSD.h @@ -0,0 +1,178 @@ + +/*************************************************************************\ +* 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 epicsAtomicOSD_h +#define epicsAtomicOSD_h + +#if defined ( __SunOS_5_10 ) + +/* + * atomic.h exists only in Solaris 10 or higher + */ +#include + +#include "epicsAssert.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifndef EPICS_ATOMIC_READ_MEMORY_BARRIER +#define EPICS_ATOMIC_READ_MEMORY_BARRIER +EPICS_ATOMIC_INLINE void epicsAtomicReadMemoryBarrier () +{ + membar_consumer (); +} +#endif + +#ifndef EPICS_ATOMIC_WRITE_MEMORY_BARRIER +#define EPICS_ATOMIC_WRITE_MEMORY_BARRIER +EPICS_ATOMIC_INLINE void 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 ) ); + unsigned * const pTarg = ( unsigned * ) pTarget; + return ( int ) atomic_cas_uint ( pTarg, ( unsigned ) oldVal, + ( unsigned ) newVal ); +} +#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 ) +{ + STATIC_ASSERT ( sizeof ( ulong_t ) == sizeof ( size_t ) ); + ulong_t * const pTarg = ( ulong_t * ) pTarget; + return ( size_t ) atomic_cas_ulong ( pTarg, oldVal, 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 + +#ifndef EPICS_ATOMIC_INCR_INTT +#define EPICS_ATOMIC_INCR_INTT +EPICS_ATOMIC_INLINE int epicsAtomicIncrIntT ( int * pTarget ) +{ + STATIC_ASSERT ( sizeof ( unsigned ) == sizeof ( int ) ); + unsigned * const pTarg = ( unsigned * ) ( pTarget ); + return ( int ) atomic_inc_uint_nv ( pTarg ); +} +#endif + +#ifndef EPICS_ATOMIC_INCR_SIZET +#define EPICS_ATOMIC_INCR_SIZET +EPICS_ATOMIC_INLINE size_t epicsAtomicIncrSizeT ( size_t * pTarget ) +{ + STATIC_ASSERT ( sizeof ( ulong_t ) == sizeof ( size_t ) ); + ulong_t * const pTarg = ( ulong_t * ) pTarget; + return ( size_t ) atomic_inc_ulong_nv ( pTarg ); +} +#endif + +#ifndef EPICS_ATOMIC_DECR_INTT +#define EPICS_ATOMIC_DECR_INTT +EPICS_ATOMIC_INLINE int epicsAtomicDecrIntT ( int * pTarget ) +{ + STATIC_ASSERT ( sizeof ( unsigned ) == sizeof ( int ) ); + unsigned * const pTarg = ( unsigned * ) ( pTarget ); + return ( int ) atomic_dec_uint_nv ( pTarg ); +} +#endif + +#ifndef EPICS_ATOMIC_DECR_SIZET +#define EPICS_ATOMIC_DECR_SIZET +EPICS_ATOMIC_INLINE size_t epicsAtomicDecrSizeT ( size_t * pTarget ) +{ + STATIC_ASSERT ( sizeof ( ulong_t ) == sizeof ( size_t ) ); + ulong_t * const pTarg = ( ulong_t * ) pTarget; + return ( size_t ) atomic_dec_ulong_nv ( pTarg ); +} +#endif + +#ifndef EPICS_ATOMIC_ADD_INTT +#define EPICS_ATOMIC_ADD_INTT +EPICS_ATOMIC_INLINE int epicsAtomicAddIntT ( int * pTarget, int delta ) +{ + STATIC_ASSERT ( sizeof ( unsigned ) == sizeof ( int ) ); + unsigned * const pTarg = ( unsigned * ) ( pTarget ); + return ( int ) atomic_add_int_nv ( pTarg, delta ); +} +#endif + +#ifndef EPICS_ATOMIC_ADD_SIZET +#define EPICS_ATOMIC_ADD_SIZET +EPICS_ATOMIC_INLINE size_t epicsAtomicAddSizeT ( size_t * pTarget, + size_t delta ) +{ + STATIC_ASSERT ( sizeof ( ulong_t ) == sizeof ( size_t ) ); + ulong_t * const pTarg = ( ulong_t * ) pTarget; + return ( size_t ) atomic_add_long_nv ( pTarg, ( 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 ) +{ + STATIC_ASSERT ( sizeof ( ulong_t ) == sizeof ( size_t ) ); + ulong_t * const pTarg = ( ulong_t * ) pTarget; + long sdelta = ( long ) delta; + return ( size_t ) atomic_add_long_nv ( pTarg, -sdelta ); +} +#endif + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif /* __cplusplus */ + +#endif /* ifdef __SunOS_5_10 */ + +struct EpicsAtomicLockKey {}; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +epicsShareFunc void epicsAtomicLock ( struct EpicsAtomicLockKey * ); +epicsShareFunc void epicsAtomicUnlock ( struct EpicsAtomicLockKey * ); + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif /* __cplusplus */ + +#include "epicsAtomicDefault.h" + +#endif /* epicsAtomicOSD_h */ + diff --git a/src/libCom/osi/os/vxWorks/epicsAtomicOSD.cpp b/src/libCom/osi/os/vxWorks/epicsAtomicOSD.cpp new file mode 100644 index 000000000..6a9f529a3 --- /dev/null +++ b/src/libCom/osi/os/vxWorks/epicsAtomicOSD.cpp @@ -0,0 +1,21 @@ + +/*************************************************************************\ +* 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 "epicsAtomicOSD.h" +#endif diff --git a/src/libCom/osi/os/vxWorks/epicsAtomicOSD.h b/src/libCom/osi/os/vxWorks/epicsAtomicOSD.h new file mode 100644 index 000000000..5454e70ba --- /dev/null +++ b/src/libCom/osi/os/vxWorks/epicsAtomicOSD.h @@ -0,0 +1,256 @@ +/*************************************************************************\ +* 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 epicsAtomicOSD_h +#define epicsAtomicOSD_h + +/* This is needed for vxWorks 6.8 to prevent an obnoxious compiler warning */ +#ifndef _VSB_CONFIG_FILE +# define _VSB_CONFIG_FILE <../lib/h/config/vsbConfig.h> +#endif + +#include "vxWorks.h" /* obtain the version of vxWorks */ +#include "epicsAssert.h" + +/* + * 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 + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifndef EPICS_ATOMIC_READ_MEMORY_BARRIER +#define EPICS_ATOMIC_READ_MEMORY_BARRIER +EPICS_ATOMIC_INLINE void epicsAtomicReadMemoryBarrier () +{ + VX_MEM_BARRIER_R (); +} +#endif + +#ifndef EPICS_ATOMIC_WRITE_MEMORY_BARRIER +#define EPICS_ATOMIC_WRITE_MEMORY_BARRIER +EPICS_ATOMIC_INLINE void epicsAtomicWriteMemoryBarrier () +{ + 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 ) + * will be the same as sizeof ( size_t ) + * + * if ULONG_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 ULONG_MAX == UINT_MAX + +STATIC_ASSERT ( sizeof ( atomic_t ) == sizeof ( size_t ) ); +STATIC_ASSERT ( sizeof ( atomic_t ) == sizeof ( EpicsAtomicPtrT ) ); + + +#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 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 = 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 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 = vxAtomicSub ( pTarg, (atomic_t) delta ); + return ( ( size_t ) oldVal ) - 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 ) +{ + atomic_t * const pTarg = ( atomic_t * ) ( pTarget ); + return ( size_t ) vxCas ( pTarg, (atomic_t) oldVal, (atomic_t) newVal ); +} +#endif + +#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 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 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 + */ + +#endif /* ULONG_MAX == UINT_MAX */ + +STATIC_ASSERT ( sizeof ( atomic_t ) == sizeof ( int ) ); + +#ifndef EPICS_ATOMIC_INCR_INTT +#define EPICS_ATOMIC_INCR_INTT +EPICS_ATOMIC_INLINE int epicsAtomicIncrIntT ( int * pTarget ) +{ + atomic_t * const pTarg = ( atomic_t * ) ( pTarget ); + const atomic_t oldVal = vxAtomicInc ( pTarg ); + return 1 + ( int ) oldVal; +} +#endif + +#ifndef EPICS_ATOMIC_DECR_INTT +#define EPICS_ATOMIC_DECR_INTT +EPICS_ATOMIC_INLINE int epicsAtomicDecrIntT ( int * pTarget ) +{ + atomic_t * const pTarg = ( atomic_t * ) ( pTarget ); + 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" */ +#endif /* __cplusplus */ + +#else /* _WRS_VXWORKS_MAJOR * 100 + _WRS_VXWORKS_MINOR >= 606 */ + +#include "vxLib.h" +#include "intLib.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifndef EPICS_ATOMIC_LOCK +#define EPICS_ATOMIC_LOCK + +typedef struct EpicsAtomicLockKey { int m_key; } EpicsAtomicLockKey; + +EPICS_ATOMIC_INLINE void epicsAtomicLock ( EpicsAtomicLockKey * pKey ) +{ + pKey->m_key = intLock (); +} + +EPICS_ATOMIC_INLINE void epicsAtomicUnlock ( EpicsAtomicLockKey * pKey ) +{ + intUnlock ( pKey->m_key ); +} +#endif + +#ifndef EPICS_ATOMIC_READ_MEMORY_BARRIER +#define EPICS_ATOMIC_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 epicsAtomicReadMemoryBarrier () {} +#endif + +#ifndef EPICS_ATOMIC_WRITE_MEMORY_BARRIER +#define EPICS_ATOMIC_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 epicsAtomicWriteMemoryBarrier () {} +#endif + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif /* __cplusplus */ + +#endif /* _WRS_VXWORKS_MAJOR * 100 + _WRS_VXWORKS_MINOR >= 606 */ + +#include "epicsAtomicDefault.h" + +#endif /* epicsAtomicOSD_h */ + diff --git a/src/libCom/osi/os/vxWorks/osdSock.h b/src/libCom/osi/os/vxWorks/osdSock.h index 3ebc5799a..362248525 100644 --- a/src/libCom/osi/os/vxWorks/osdSock.h +++ b/src/libCom/osi/os/vxWorks/osdSock.h @@ -3,8 +3,7 @@ * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. -* EPICS BASE Versions 3.13.7 -* and higher are distributed subject to a Software License Agreement found +* EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* @@ -14,12 +13,10 @@ #ifndef osdSockH #define osdSockH -#ifdef __cplusplus -extern "C" { -#endif - /* This is needed for vxWorks 6.8 to prevent an obnoxious compiler warning */ -#define _VSB_CONFIG_FILE <../lib/h/config/vsbConfig.h> +#ifndef _VSB_CONFIG_FILE +# define _VSB_CONFIG_FILE <../lib/h/config/vsbConfig.h> +#endif #include @@ -36,6 +33,11 @@ extern "C" { #include #include #include + +#ifdef __cplusplus +extern "C" { +#endif + /*This following is not defined in any vxWorks header files*/ int sysClkRateGet(void); diff --git a/src/libCom/test/Makefile b/src/libCom/test/Makefile index 62875affd..d59ad7621 100644 --- a/src/libCom/test/Makefile +++ b/src/libCom/test/Makefile @@ -109,6 +109,11 @@ epicsMutexTest_SRCS += epicsMutexTest.cpp testHarness_SRCS += epicsMutexTest.cpp TESTS += epicsMutexTest +TESTPROD_HOST += epicsAtomicTest +epicsAtomicTest_SRCS += epicsAtomicTest.cpp +testHarness_SRCS += epicsAtomicTest.cpp +TESTS += epicsAtomicTest + TESTPROD_HOST += epicsExceptionTest epicsExceptionTest_SRCS += epicsExceptionTest.cpp testHarness_SRCS += epicsExceptionTest.cpp @@ -179,6 +184,10 @@ fdmgrTest_SRCS += fdmgrTest.c fdmgrTest_LIBS += ca # FIXME: program never exits. +TESTPROD_HOST += epicsAtomicPerform +epicsAtomicPerform_SRCS += epicsAtomicPerform.cpp +testHarness_SRCS += epicsAtomicPerform.cpp + TESTPROD_HOST += cvtFastPerform cvtFastPerform_SRCS += cvtFastPerform.cpp testHarness_SRCS += cvtFastPerform.cpp diff --git a/src/libCom/test/epicsAtomicPerform.cpp b/src/libCom/test/epicsAtomicPerform.cpp new file mode 100644 index 000000000..98b6e9939 --- /dev/null +++ b/src/libCom/test/epicsAtomicPerform.cpp @@ -0,0 +1,506 @@ + +#include +#include +#include +#include + +#include "epicsInterrupt.h" +#include "epicsAtomic.h" +#include "epicsTime.h" +#include "epicsUnitTest.h" +#include "testMain.h" + +using std :: size_t; +using namespace epics; +using namespace atomic; + +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; + +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. +template < class T > +inline void OrdinaryIncr < T > :: run () +{ + 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 (); +} + +template < class T > +void OrdinaryIncr < T > :: diagnostic ( double delay ) +{ + 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 ); +} + +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 () +{ + 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 ); +} + +template < class T > +void AtomicIncr < T > :: diagnostic ( double delay ) +{ + delay /= 10.0; + delay *= 1e6; + const char * const pName = typeid ( T ) . name (); + testDiag ( "epicsAtomicIncr \"%s\" takes %f microseconds", + pName, delay ); +} + +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 () +{ + 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 > () ); +} + +template < class T > +void AtomicCmpAndSwap < T > :: diagnostic ( double delay ) +{ + delay /= 10.0; + delay *= 1e6; + const char * const pName = typeid ( T ) . name (); + testDiag ( "epicsAtomicCmpAndSwap of \"%s\" takes %f microseconds", + pName, delay ); +} + +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 () +{ + 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 ); +} + +template < class T > +void AtomicSet < T > :: diagnostic ( double delay ) +{ + 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 recursiveOwnershipRetPerformance () +{ + RefCtr refCtr; + epicsTime begin = epicsTime::getCurrent (); + for ( size_t 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 ( size_t 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 ); +} + +template < class T > +class Ten +{ +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 (); +} + +template < class T > +void Ten < T > :: diagnostic ( double delay ) +{ + 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 perhap + // + 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 (); + return testDone(); +} diff --git a/src/libCom/test/epicsAtomicTest.cpp b/src/libCom/test/epicsAtomicTest.cpp new file mode 100644 index 000000000..bdcee8b71 --- /dev/null +++ b/src/libCom/test/epicsAtomicTest.cpp @@ -0,0 +1,238 @@ + +#include +#include + +#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 ( epicsAtomicTest ) +{ + + testPlan ( 31 ); + + testIncrDecr < int > (); + testIncrDecr < size_t > (); + testAddSub < int > (); + testAddSub < size_t > (); + testCAS < int > (); + testCAS < size_t > (); + testCAS < EpicsAtomicPtrT > (); + + return testDone (); +}