From d7e9e5e4959681c47fc831cb82fa01b74f6f3679 Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Tue, 17 Jun 2008 17:43:59 +0000 Subject: [PATCH] From Mathias Froehlich, OpenThreads::Atomic support --- CMakeModules/CheckAtomicOps.cmake | 102 +++++++++++++++++ include/OpenThreads/Atomic | 182 ++++++++++++++++++++++++++++++ src/OpenThreads/CMakeLists.txt | 8 ++ src/OpenThreads/common/Config.in | 10 ++ 4 files changed, 302 insertions(+) create mode 100644 CMakeModules/CheckAtomicOps.cmake create mode 100644 include/OpenThreads/Atomic create mode 100644 src/OpenThreads/common/Config.in diff --git a/CMakeModules/CheckAtomicOps.cmake b/CMakeModules/CheckAtomicOps.cmake new file mode 100644 index 000000000..07a13c05e --- /dev/null +++ b/CMakeModules/CheckAtomicOps.cmake @@ -0,0 +1,102 @@ +# Check for availability of atomic operations +# This module defines +# OPENTHREADS_HAVE_ATOMIC_OPS + +INCLUDE(CheckCXXSourceRuns) + +# Do step by step checking, +CHECK_CXX_SOURCE_RUNS(" +#include + +int main() +{ +#ifdef __i386__ + // Bad, gcc behaves dependent on the compilers -march=... flags. + // Since the osg::Referenced stuff is code distributed in headers, it is + // unclear if we will have this feature available at compile time of the + // headers. So just do not use this feature for 32 bit code. + // May be we can implement around that limitation at some time.. + return EXIT_FAILURE; +#else + unsigned value = 0; + void* ptr = &value; + __sync_add_and_fetch(&value, 1); + __sync_synchronize(); + __sync_sub_and_fetch(&value, 1); + if (!__sync_bool_compare_and_swap(&value, 0, 1)) + return EXIT_FAILURE; + + if (!__sync_bool_compare_and_swap(&ptr, ptr, ptr)) + return EXIT_FAILURE; + + return EXIT_SUCCESS; +#endif +} +" _OPENTHREADS_ATOMIC_USE_GCC_BUILTINS) + +CHECK_CXX_SOURCE_RUNS(" +#include + +int main(int, const char**) +{ + unsigned value = 0; + void* ptr = &value; + __add_and_fetch(&value, 1); + __synchronize(value); + __sub_and_fetch(&value, 1); + if (!__sync_compare_and_swap(&value, 0, 1)) + return EXIT_FAILURE; + + if (!__sync_compare_and_swap(&ptr, ptr, ptr)) + return EXIT_FAILURE; + + return EXIT_SUCCESS; +} +" _OPENTHREADS_ATOMIC_USE_MIPOSPRO_BUILTINS) + +CHECK_CXX_SOURCE_RUNS(" +#include +#include + +int main(int, const char**) +{ + uint_t value = 0; + void* ptr = &value; + atomic_inc_uint_nv(&value); + membar_consumer(); + atomic_dec_uint_nv(&value); + if (0 != atomic_cas_uint(&value, 0, 1)) + return EXIT_FAILURE; + + if (ptr != atomic_cas_ptr(&ptr, ptr, ptr)) + return EXIT_FAILURE; + + return EXIT_SUCCESS; +} +" _OPENTHREADS_ATOMIC_USE_SUN) + +CHECK_CXX_SOURCE_RUNS(" +#include +#include + +int main(int, const char**) +{ + __declspec(align(32)) volatile long value = 0; + __declspec(align(32)) volatile void* ptr = &value; + + InterlockedIncrement(&value); + InterlockedDecrement(&value); + + if (0 != InterlockedCompareExchange(&value, 1, 0)) + return EXIT_FAILURE; + + if (ptr != InterlockedCompareExchangePointer(&ptr, ptr, ptr)) + return EXIT_FAILURE; + + return EXIT_SUCCESS; +} +" _OPENTHREADS_ATOMIC_USE_WIN32_INTERLOCKED) + +IF(NOT _OPENTHREADS_ATOMIC_USE_GCC_BUILTINS AND NOT _OPENTHREADS_ATOMIC_USE_MIPOSPRO_BUILTINS AND NOT _OPENTHREADS_ATOMIC_USE_SUN AND NOT _OPENTHREADS_ATOMIC_USE_WIN32_INTERLOCKED) + SET(_OPENTHREADS_ATOMIC_USE_MUTEX) +ENDIF(NOT _OPENTHREADS_ATOMIC_USE_GCC_BUILTINS AND NOT _OPENTHREADS_ATOMIC_USE_MIPOSPRO_BUILTINS AND NOT _OPENTHREADS_ATOMIC_USE_SUN AND NOT _OPENTHREADS_ATOMIC_USE_WIN32_INTERLOCKED) \ No newline at end of file diff --git a/include/OpenThreads/Atomic b/include/OpenThreads/Atomic new file mode 100644 index 000000000..6c8ef147b --- /dev/null +++ b/include/OpenThreads/Atomic @@ -0,0 +1,182 @@ +/* -*-c++-*- OpenThreads library, Copyright (C) 2008 The Open Thread Group + * + * This library is open source and may be redistributed and/or modified under + * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or + * (at your option) any later version. The full license is in LICENSE file + * included with this distribution, and on the openscenegraph.org website. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * OpenSceneGraph Public License for more details. +*/ + +#ifndef _OPENTHREADS_ATOMIC_ +#define _OPENTHREADS_ATOMIC_ + +#include + +#if defined(_OPENTHREADS_ATOMIC_USE_WIN32_INTERLOCKED) +# include +#elif defined(_OPENTHREADS_ATOMIC_USE_SUN) +# include +#elif defined(_OPENTHREADS_ATOMIC_USE_MUTEX) +# include "Mutex" +# include "ScopedLock" +#endif + +namespace OpenThreads { + +/** + * @class Atomic + * @brief This class provides an atomic increment and decrement operation. + */ +class OPENTHREAD_EXPORT_DIRECTIVE Atomic { + public: + Atomic(unsigned value = 0) : _value(value) + { } + unsigned operator++() + { +#if defined(_OPENTHREADS_ATOMIC_USE_GCC_BUILTINS) + return __sync_add_and_fetch(&_value, 1); +#elif defined(_OPENTHREADS_ATOMIC_USE_MIPOSPRO_BUILTINS) + return __add_and_fetch(&_value, 1); +#elif defined(_OPENTHREADS_ATOMIC_USE_SUN) + return atomic_inc_uint_nv(&_value); +#elif defined(_OPENTHREADS_ATOMIC_USE_WIN32_INTERLOCKED) + return InterlockedIncrement(&_value); +#elif defined(_OPENTHREADS_ATOMIC_USE_MUTEX) + ScopedLock lock(_mutex); + return ++_value; +#else + return ++_value; +#endif + } + unsigned operator--() + { +#if defined(_OPENTHREADS_ATOMIC_USE_GCC_BUILTINS) + return __sync_sub_and_fetch(&_value, 1); +#elif defined(_OPENTHREADS_ATOMIC_USE_MIPOSPRO_BUILTINS) + return __sub_and_fetch(&_value, 1); +#elif defined(_OPENTHREADS_ATOMIC_USE_SUN) + return atomic_dec_uint_nv(&_value); +#elif defined(_OPENTHREADS_ATOMIC_USE_WIN32_INTERLOCKED) + return InterlockedDecrement(&_value); +#elif defined(_OPENTHREADS_ATOMIC_USE_MUTEX) + ScopedLock lock(_mutex); + return --_value; +#else + return --_value; +#endif + } + operator unsigned() const + { +#if defined(_OPENTHREADS_ATOMIC_USE_GCC_BUILTINS) + __sync_synchronize(); + return _value; +#elif defined(_OPENTHREADS_ATOMIC_USE_MIPOSPRO_BUILTINS) + __synchronize(_value); + return _value; +#elif defined(_OPENTHREADS_ATOMIC_USE_SUN) + membar_consumer(); // Hmm, do we need??? + return _value; +#elif defined(_OPENTHREADS_ATOMIC_USE_WIN32_INTERLOCKED) + return static_cast(_value); +#elif defined(_OPENTHREADS_ATOMIC_USE_MUTEX) + ScopedLock lock(_mutex); + return _value; +#else + return _value; +#endif + } + + private: + Atomic(const Atomic&); + Atomic& operator=(const Atomic&); + +#if defined(_OPENTHREADS_ATOMIC_USE_MUTEX) + mutable Mutex _mutex; +#endif +#if defined(_OPENTHREADS_ATOMIC_USE_WIN32_INTERLOCKED) + __declspec(align(32)) volatile long _value; +#elif defined(_OPENTHREADS_ATOMIC_USE_SUN) + volatile uint_t _value; +#else + volatile unsigned _value; +#endif +}; + +/** + * @class AtomicPtr + * @brief This class provides an atomic pointer assignment using cas operations. + */ +template +class OPENTHREAD_EXPORT_DIRECTIVE AtomicPtr { +public: + AtomicPtr(T* ptr = 0) : _ptr(ptr) + { } + ~AtomicPtr() + { _ptr = 0; } + + // assigns a new pointer + bool assign(T* ptrNew, const T* const ptrOld) + { +#if defined(_OPENTHREADS_ATOMIC_USE_GCC_BUILTINS) + return __sync_bool_compare_and_swap(&_ptr, ptrOld, ptrNew); +#elif defined(_OPENTHREADS_ATOMIC_USE_MIPOSPRO_BUILTINS) + return __compare_and_swap(&_ptr, ptrOld, ptrNew); +#elif defined(_OPENTHREADS_ATOMIC_USE_SUN) + return ptrOld == atomic_cas_ptr(&_ptr, ptrOld, ptrNew); +#elif defined(_OPENTHREADS_ATOMIC_USE_WIN32_INTERLOCKED) + return ptrOld == InterlockedCompareExchangePointer(&_ptr, ptrNew, ptrOld); +#elif defined(_OPENTHREADS_ATOMIC_USE_MUTEX) + ScopedLock lock(_mutex); + if (_ptr != oldPtr) + return false; + _ptr = ptrNew; + return true; +#else + if (_ptr != oldPtr) + return false; + _ptr = ptrNew; + return true; +#endif + } + + T* get() const + { +#if defined(_OPENTHREADS_ATOMIC_USE_GCC_BUILTINS) + __sync_synchronize(); + return _ptr; +#elif defined(_OPENTHREADS_ATOMIC_USE_MIPOSPRO_BUILTINS) + __synchronize(_ptr); + return _ptr; +#elif defined(_OPENTHREADS_ATOMIC_USE_SUN) + membar_consumer(); // Hmm, do we need??? + return _ptr; +#elif defined(_OPENTHREADS_ATOMIC_USE_WIN32_INTERLOCKED) + return _ptr; +#elif defined(_OPENTHREADS_ATOMIC_USE_MUTEX) + ScopedLock lock(_mutex); + return _ptr; +#else + return _ptr; +#endif + } + +private: + AtomicPtr(const AtomicPtr&); + AtomicPtr& operator=(const AtomicPtr&); + +#if defined(_OPENTHREADS_ATOMIC_USE_MUTEX) + mutable Mutex _mutex; +#endif +#if defined(_OPENTHREADS_ATOMIC_USE_WIN32_INTERLOCKED) + __declspec(align(32)) +#endif + T* volatile _ptr; +}; + +} + +#endif // _OPENTHREADS_ATOMIC_ diff --git a/src/OpenThreads/CMakeLists.txt b/src/OpenThreads/CMakeLists.txt index 7649da4a0..f186acee9 100644 --- a/src/OpenThreads/CMakeLists.txt +++ b/src/OpenThreads/CMakeLists.txt @@ -11,6 +11,7 @@ SET(OPENTHREADS_VERSION ${OPENTHREADS_MAJOR_VERSION}.${OPENTHREADS_MINOR_VERSION SET(HEADER_PATH ${OpenThreads_SOURCE_DIR}/include/OpenThreads) SET(OpenThreads_PUBLIC_HEADERS + ${HEADER_PATH}/Atomic ${HEADER_PATH}/Barrier ${HEADER_PATH}/Block ${HEADER_PATH}/Condition @@ -68,5 +69,12 @@ ELSE(WIN32) ENDIF(UNIX) ENDIF(WIN32) +INCLUDE(CheckAtomicOps) +SET(OPENTHREADS_CONFIG_HEADER "${PROJECT_BINARY_DIR}/include/OpenThreads/Config") +CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/common/Config.in" + "${OPENTHREADS_CONFIG_HEADER}") +INSTALL_FILES(/include/OpenThreads/ FILES "${OPENTHREADS_CONFIG_HEADER}") +# Make sure everyone can find Config +INCLUDE_DIRECTORIES(${PROJECT_BINARY_DIR}/include) diff --git a/src/OpenThreads/common/Config.in b/src/OpenThreads/common/Config.in new file mode 100644 index 000000000..876bf86ef --- /dev/null +++ b/src/OpenThreads/common/Config.in @@ -0,0 +1,10 @@ +#ifndef _OPENTHREADS_CONFIG +#define _OPENTHREADS_CONFIG + +#cmakedefine _OPENTHREADS_ATOMIC_USE_GCC_BUILTINS +#cmakedefine _OPENTHREADS_ATOMIC_USE_MIPOSPRO_BUILTINS +#cmakedefine _OPENTHREADS_ATOMIC_USE_SUN +#cmakedefine _OPENTHREADS_ATOMIC_USE_WIN32_INTERLOCKED +#cmakedefine _OPENTHREADS_ATOMIC_USE_MUTEX + +#endif \ No newline at end of file