
/* -*-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 <OpenThreads/Config> 
#include <OpenThreads/Exports> 

#if defined(_OPENTHREADS_ATOMIC_USE_GCC_BUILTINS) && defined(__i386__) 
#define _OPENTHREADS_ATOMIC_USE_LIBRARY_ROUTINES 
#elif defined(_OPENTHREADS_ATOMIC_USE_WIN32_INTERLOCKED) 
#define _OPENTHREADS_ATOMIC_USE_LIBRARY_ROUTINES 
#elif defined(_OPENTHREADS_ATOMIC_USE_SUN) 
# include <atomic.h> 
# include "Mutex" 
# include "ScopedLock" 
#elif defined(_OPENTHREADS_ATOMIC_USE_BSD_ATOMIC) 
# include <libkern/OSAtomic.h> 
# define _OPENTHREADS_ATOMIC_USE_LIBRARY_ROUTINES 
#elif defined(_OPENTHREADS_ATOMIC_USE_MUTEX) 
# include "Mutex" 
# include "ScopedLock" 
#endif 

#if defined(_OPENTHREADS_ATOMIC_USE_LIBRARY_ROUTINES) 
#define _OPENTHREADS_ATOMIC_INLINE 
#else 
#define _OPENTHREADS_ATOMIC_INLINE inline 
#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) 
    { } 
    _OPENTHREADS_ATOMIC_INLINE unsigned operator++(); 
    _OPENTHREADS_ATOMIC_INLINE unsigned operator--(); 
    _OPENTHREADS_ATOMIC_INLINE unsigned AND(unsigned value); 
    _OPENTHREADS_ATOMIC_INLINE unsigned OR(unsigned value); 
    _OPENTHREADS_ATOMIC_INLINE unsigned XOR(unsigned value); 
    _OPENTHREADS_ATOMIC_INLINE unsigned exchange(unsigned value = 0); 
    _OPENTHREADS_ATOMIC_INLINE operator unsigned() const; 
 private: 

    Atomic(const Atomic&); 
    Atomic& operator=(const Atomic&); 

#if defined(_OPENTHREADS_ATOMIC_USE_MUTEX) 
    mutable Mutex _mutex; 
#endif 
#if defined(_OPENTHREADS_ATOMIC_USE_GCC_BUILTINS) && defined(__i386__) 
    volatile unsigned _value; 
#elif defined(_OPENTHREADS_ATOMIC_USE_WIN32_INTERLOCKED) 
    volatile long _value; 
#elif defined(_OPENTHREADS_ATOMIC_USE_BSD_ATOMIC) 
    volatile int32_t _value; 
#elif defined(_OPENTHREADS_ATOMIC_USE_SUN) 
    volatile uint_t _value; 
    mutable Mutex _mutex;  // needed for xor 
#endif 
}; 

/** 
 *  @class AtomicPtr 
 *  @brief  This class provides an atomic pointer assignment using cas operations. 
 */ 
class OPENTHREAD_EXPORT_DIRECTIVE AtomicPtr { 
public: 
    AtomicPtr(void* ptr = 0) : _ptr(ptr) 
    { } 
    ~AtomicPtr() 
    { _ptr = 0; } 

    // assigns a new pointer 
    _OPENTHREADS_ATOMIC_INLINE bool assign(void* ptrNew, const void* const ptrOld); 
    _OPENTHREADS_ATOMIC_INLINE void* get() const; 

private: 
    AtomicPtr(const AtomicPtr&); 
    AtomicPtr& operator=(const AtomicPtr&); 

#if defined(_OPENTHREADS_ATOMIC_USE_MUTEX) 
    mutable Mutex _mutex; 
#endif 
    void* volatile _ptr; 
}; 

#if !defined(_OPENTHREADS_ATOMIC_USE_LIBRARY_ROUTINES) 

_OPENTHREADS_ATOMIC_INLINE unsigned 
Atomic::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_MUTEX) 
    ScopedLock<Mutex> lock(_mutex); 
    return ++_value; 
#else 
    return ++_value; 
#endif 
} 

_OPENTHREADS_ATOMIC_INLINE unsigned 
Atomic::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_MUTEX) 
    ScopedLock<Mutex> lock(_mutex); 
    return --_value; 
#else 
    return --_value; 
#endif 
} 

_OPENTHREADS_ATOMIC_INLINE unsigned 
Atomic::AND(unsigned value) 
{ 
#if defined(_OPENTHREADS_ATOMIC_USE_GCC_BUILTINS) 
    return __sync_fetch_and_and(&_value, value); 
#elif defined(_OPENTHREADS_ATOMIC_USE_MIPOSPRO_BUILTINS) 
    return __and_and_fetch(&_value, value); 
#elif defined(_OPENTHREADS_ATOMIC_USE_SUN) 
    return atomic_and_uint_nv(&_value, value); 
#elif defined(_OPENTHREADS_ATOMIC_USE_MUTEX) 
    ScopedLock<Mutex> lock(_mutex); 
    _value &= value; 
    return _value; 
#else 
    _value &= value; 
    return _value; 
#endif 
} 

_OPENTHREADS_ATOMIC_INLINE unsigned 
Atomic::OR(unsigned value) 
{ 
#if defined(_OPENTHREADS_ATOMIC_USE_GCC_BUILTINS) 
    return __sync_fetch_and_or(&_value, value); 
#elif defined(_OPENTHREADS_ATOMIC_USE_MIPOSPRO_BUILTINS) 
    return __or_and_fetch(&_value, value); 
#elif defined(_OPENTHREADS_ATOMIC_USE_SUN) 
    return atomic_or_uint_nv(&_value, value); 
#elif defined(_OPENTHREADS_ATOMIC_USE_MUTEX) 
    ScopedLock<Mutex> lock(_mutex); 
    _value |= value; 
    return _value; 
#else 
    _value |= value; 
    return _value; 
#endif 
} 

_OPENTHREADS_ATOMIC_INLINE unsigned 
Atomic::XOR(unsigned value) 
{ 
#if defined(_OPENTHREADS_ATOMIC_USE_GCC_BUILTINS) 
    return __sync_fetch_and_xor(&_value, value); 
#elif defined(_OPENTHREADS_ATOMIC_USE_MIPOSPRO_BUILTINS) 
    return __xor_and_fetch(&_value, value); 
#elif defined(_OPENTHREADS_ATOMIC_USE_SUN) 
    ScopedLock<Mutex> lock(_mutex); 
    _value ^= value; 
    return _value; 
#elif defined(_OPENTHREADS_ATOMIC_USE_MUTEX) 
    ScopedLock<Mutex> lock(_mutex); 
    _value ^= value; 
    return _value; 
#else 
    _value ^= value; 
    return _value; 
#endif 
} 

_OPENTHREADS_ATOMIC_INLINE unsigned 
Atomic::exchange(unsigned value) 
{ 
#if defined(_OPENTHREADS_ATOMIC_USE_GCC_BUILTINS) 
    return __sync_lock_test_and_set(&_value, value); 
#elif defined(_OPENTHREADS_ATOMIC_USE_MIPOSPRO_BUILTINS) 
    return __compare_and_swap(&_value, _value, value); 
#elif defined(_OPENTHREADS_ATOMIC_USE_SUN) 
    return atomic_cas_uint(&_value, _value, value); 
#elif defined(_OPENTHREADS_ATOMIC_USE_MUTEX) 
    ScopedLock<Mutex> lock(_mutex); 
    unsigned oldval = _value; 
    _value = value; 
    return oldval; 
#else 
    unsigned oldval = _value; 
    _value = value; 
    return oldval; 
#endif 
} 

_OPENTHREADS_ATOMIC_INLINE 
Atomic::operator unsigned() const 
{ 
#if defined(_OPENTHREADS_ATOMIC_USE_GCC_BUILTINS) 
    __sync_synchronize(); 
    return _value; 
#elif defined(_OPENTHREADS_ATOMIC_USE_MIPOSPRO_BUILTINS) 
    __synchronize(); 
    return _value; 
#elif defined(_OPENTHREADS_ATOMIC_USE_SUN) 
    membar_consumer(); // Hmm, do we need??? 
    return _value; 
#elif defined(_OPENTHREADS_ATOMIC_USE_MUTEX) 
    ScopedLock<Mutex> lock(_mutex); 
    return _value; 
#else 
    return _value; 
#endif 
} 

_OPENTHREADS_ATOMIC_INLINE bool 
AtomicPtr::assign(void* ptrNew, const void* 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((unsigned long*)&_ptr, (unsigned long)ptrOld, (unsigned long)ptrNew); 
#elif defined(_OPENTHREADS_ATOMIC_USE_SUN) 
    return ptrOld == atomic_cas_ptr(&_ptr, const_cast<void*>(ptrOld), ptrNew); 
#elif defined(_OPENTHREADS_ATOMIC_USE_MUTEX) 
    ScopedLock<Mutex> lock(_mutex); 
    if (_ptr != ptrOld) 
        return false; 
    _ptr = ptrNew; 
    return true; 
#else 
    if (_ptr != ptrOld) 
        return false; 
    _ptr = ptrNew; 
    return true; 
#endif 
} 

_OPENTHREADS_ATOMIC_INLINE void* 
AtomicPtr::get() const 
{ 
#if defined(_OPENTHREADS_ATOMIC_USE_GCC_BUILTINS) 
    __sync_synchronize(); 
    return _ptr; 
#elif defined(_OPENTHREADS_ATOMIC_USE_MIPOSPRO_BUILTINS) 
    __synchronize(); 
    return _ptr; 
#elif defined(_OPENTHREADS_ATOMIC_USE_SUN) 
    membar_consumer(); // Hmm, do we need??? 
    return _ptr; 
#elif defined(_OPENTHREADS_ATOMIC_USE_MUTEX) 
    ScopedLock<Mutex> lock(_mutex); 
    return _ptr; 
#else 
    return _ptr; 
#endif 
} 

#endif // !defined(_OPENTHREADS_ATOMIC_USE_LIBRARY_ROUTINES) 

} 

#endif // _OPENTHREADS_ATOMIC_ 