blocxx

AtomicOps.hpp

Go to the documentation of this file.
00001 /*******************************************************************************
00002 * Copyright (C) 2005, Vintela, Inc. All rights reserved.
00003 * Copyright (C) 2006, Novell, Inc. All rights reserved.
00004 * 
00005 * Redistribution and use in source and binary forms, with or without
00006 * modification, are permitted provided that the following conditions are met:
00007 * 
00008 *     * Redistributions of source code must retain the above copyright notice,
00009 *       this list of conditions and the following disclaimer.
00010 *     * Redistributions in binary form must reproduce the above copyright
00011 *       notice, this list of conditions and the following disclaimer in the
00012 *       documentation and/or other materials provided with the distribution.
00013 *     * Neither the name of 
00014 *       Vintela, Inc., 
00015 *       nor Novell, Inc., 
00016 *       nor the names of its contributors or employees may be used to 
00017 *       endorse or promote products derived from this software without 
00018 *       specific prior written permission.
00019 * 
00020 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00021 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00022 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00023 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
00024 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00025 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00026 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00027 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00028 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00029 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00030 * POSSIBILITY OF SUCH DAMAGE.
00031 *******************************************************************************/
00032 
00033 
00039 #ifndef BLOCXX_ATOMIC_OPS_HPP_
00040 #define BLOCXX_ATOMIC_OPS_HPP_
00041 #include "blocxx/BLOCXX_config.h"
00042 
00043 #if defined(BLOCXX_AIX)
00044 extern "C"
00045 {
00046 #include <sys/atomic_op.h>
00047 }
00048 #elif defined(__HP_aCC) && defined(BLOCXX_ARCH_IA64)
00049 #include <machine/sys/inline.h>
00050 #endif
00051 
00052 
00053 // TODO: PA-RISC and itanium.  IA64 is supported for aCC, but not for gcc.
00054 // See http://h21007.www2.hp.com/dspp/files/unprotected/Itanium/spinlocks.pdf
00055 // http://www.hpl.hp.com/research/linux/atomic_ops/
00056 
00057 // The classes and functions defined in this file are not meant for general
00058 // use, they are internal implementation details.  They may change at any time.
00059 
00060 // x86 and x86-64 asm is identical
00061 #if (defined(BLOCXX_ARCH_X86) || defined(__i386__) || defined(BLOCXX_ARCH_X86_64) || defined(__x86_64__)) && defined(__GNUC__)
00062 
00063 namespace BLOCXX_NAMESPACE
00064 {
00065 
00066 // use fast inline assembly versions
00067 struct Atomic_t
00068 { 
00069    Atomic_t() : val(0) {}
00070    Atomic_t(int i) : val(i) {}
00071    volatile int val; 
00072 };
00073 inline void AtomicInc(Atomic_t &v)
00074 {
00075    __asm__ __volatile__(
00076       "lock ; " "incl %0"
00077       :"=m" (v.val)
00078       :"m" (v.val));
00079 }
00080 inline bool AtomicDecAndTest(Atomic_t &v)
00081 {
00082    unsigned char c;
00083    __asm__ __volatile__(
00084       "lock ; " "decl %0; sete %1"
00085       :"=m" (v.val), "=qm" (c)
00086       :"m" (v.val) : "memory");
00087    return c != 0;
00088 }
00089 inline int AtomicGet(Atomic_t const &v)
00090 {
00091    return v.val;
00092 }
00093 inline void AtomicDec(Atomic_t &v)
00094 {
00095    __asm__ __volatile__(
00096       "lock ; " "decl %0"
00097       :"=m" (v.val)
00098       :"m" (v.val));
00099 }
00100 
00101 } // end namespace BLOCXX_NAMESPACE
00102 #elif defined(__HP_aCC) && defined(BLOCXX_ARCH_IA64)
00103 namespace BLOCXX_NAMESPACE
00104 {
00105 struct Atomic_t
00106 {
00107    Atomic_t() : val(0) {}
00108    Atomic_t(int i) : val(i) {}
00109    volatile int val;
00110 };
00111 inline void AtomicInc(Atomic_t &v)
00112 {
00113    _Asm_fetchadd(_FASZ_W, _SEM_ACQ, &v.val, 1, _LDHINT_NONE);
00114 }
00115 inline bool AtomicDecAndTest(Atomic_t &v)
00116 {
00117    int c = int(_Asm_fetchadd(_FASZ_W, _SEM_ACQ, &v.val, int(-1), _LDHINT_NONE));
00118       --c;
00119    return c == 0;
00120 }
00121 inline int AtomicGet(Atomic_t const &v)
00122 {
00123    return int(v.val);
00124 }
00125 inline void AtomicDec(Atomic_t &v)
00126 {
00127    _Asm_fetchadd(_FASZ_W, _SEM_ACQ, &v.val, -1, _LDHINT_NONE);
00128 }
00129 }
00130 
00131 #elif defined(BLOCXX_AIX)
00132 namespace BLOCXX_NAMESPACE
00133 {
00134 // This comment was stolen from the libstdc++ implementation of atomicity.h
00135 // (and modified). 
00136 // We cannot use the inline assembly for powerpc, since definitions for
00137 // these operations since they depend on operations that are not available on
00138 // the original POWER architecture.  AIX still runs on the POWER architecture,
00139 // so it would be incorrect to assume the existence of these instructions. 
00140 //
00141 // The definition of Atomic_t.val must match the type pointed to by atomic_p in
00142 // <sys/atomic_op.h>. 
00143 struct Atomic_t
00144 { 
00145    Atomic_t() : val(0) {}
00146    Atomic_t(int i) : val(i) {}
00147    volatile int val; 
00148 };
00149 
00150 inline void AtomicInc(Atomic_t &v)
00151 {
00152    ::fetch_and_add(const_cast<atomic_p>(&v.val), 1);
00153 }
00154 inline bool AtomicDecAndTest(Atomic_t &v)
00155 {
00156    // fetch_and_add returns the original value before the add operation.  Thus,
00157    // we must subtract one from the returned value before comparing.
00158    int c = ::fetch_and_add(const_cast<atomic_p>(&v.val), -1);
00159    --c;
00160    return c == 0;
00161 }
00162 inline int AtomicGet(Atomic_t const &v)
00163 {
00164    int c = ::fetch_and_add(const_cast<atomic_p>(&v.val), 0);
00165    return c;
00166 }
00167 inline void AtomicDec(Atomic_t &v)
00168 {
00169    ::fetch_and_add(const_cast<atomic_p>(&v.val), -1);
00170 }
00171 
00172 } // end namespace BLOCXX_NAMESPACE
00173 
00174 #elif (defined(BLOCXX_ARCH_PPC) || defined(__ppc__)) && defined(__GNUC__)
00175 
00176 namespace BLOCXX_NAMESPACE
00177 {
00178 
00179 // use fast inline assembly versions
00180 struct Atomic_t
00181 { 
00182    Atomic_t() : val(0) {}
00183    Atomic_t(int i) : val(i) {}
00184    volatile int val; 
00185 };
00186 
00187 inline void AtomicInc(Atomic_t &v)
00188 {
00189    int t;
00190    __asm__ __volatile__(
00191       "1:   lwarx   %0,0,%2\n"
00192       "  addic   %0,%0,1\n"
00193       "  stwcx.  %0,0,%2\n"
00194       "  bne-    1b"
00195       : "=&r" (t), "=m" (v.val)
00196       : "r" (&v.val), "m" (v.val)
00197       : "cc");
00198 }
00199 inline bool AtomicDecAndTest(Atomic_t &v)
00200 {
00201    int c;
00202    __asm__ __volatile__(
00203       "1:   lwarx   %0,0,%1\n"
00204       "  addic   %0,%0,-1\n"
00205       "  stwcx.  %0,0,%1\n"
00206       "  bne-    1b\n"
00207       "  isync"
00208       : "=&r" (c)
00209       : "r" (&v.val)
00210       : "cc", "memory");
00211    return c == 0;
00212 }
00213 inline int AtomicGet(Atomic_t const &v)
00214 {
00215    return v.val;
00216 }
00217 inline void AtomicDec(Atomic_t &v)
00218 {
00219    int c;
00220    __asm__ __volatile__(
00221       "1:   lwarx   %0,0,%2\n"
00222       "  addic   %0,%0,-1\n"
00223       "  stwcx.  %0,0,%2\n"
00224       "  bne-    1b"
00225       : "=&r" (c), "=m" (v.val)
00226       : "r" (&v.val), "m" (v.val)
00227       : "cc");
00228 }
00229 
00230 } // end namespace BLOCXX_NAMESPACE
00231 
00232 #elif defined(BLOCXX_WIN32)
00233 
00234 #define WIN32_LEAN_AND_MEAN  // Exclude rarely-used stuff from Windows headers
00235 #include <wtypes.h>
00236 
00237 namespace BLOCXX_NAMESPACE
00238 {
00239 
00240 // use fast inline assembly versions
00241 struct BLOCXX_COMMON_API Atomic_t
00242 { 
00243    Atomic_t() : val(0) {}
00244    Atomic_t(int i) : val(i) {}
00245    volatile long val; 
00246 };
00247 inline void AtomicInc(Atomic_t &v)
00248 {
00249    InterlockedIncrement(&v.val); 
00250 }
00251 inline bool AtomicDecAndTest(Atomic_t &v)
00252 {
00253    return InterlockedDecrement(&v.val) == 0;
00254 }
00255 inline int AtomicGet(Atomic_t const &v)
00256 {
00257    return v.val;
00258 }
00259 inline void AtomicDec(Atomic_t &v)
00260 {
00261    InterlockedDecrement(&v.val);
00262 }
00263 
00264 } // end namespace BLOCXX_NAMESPACE
00265 
00266 #elif defined(BLOCXX_HAVE_PTHREAD_SPIN_LOCK)
00267 #include <pthread.h>
00268 
00269 #define BLOCXX_USE_PTHREAD_SPIN_LOCK_ATOMIC_OPS // used in BLOCXX_AtomicOps.cpp
00270 
00271 namespace BLOCXX_NAMESPACE
00272 {
00276 struct Atomic_t
00277 {
00282    Atomic_t();
00283 
00289    Atomic_t(int i);
00290 
00292    int val;
00293 
00294    pthread_spinlock_t spinlock;
00295 };
00296 
00301 void AtomicInc(Atomic_t &v);
00302 
00308 bool AtomicDecAndTest(Atomic_t &v);
00314 int AtomicGet(Atomic_t const &v);
00315 
00320 void AtomicDec(Atomic_t &v);
00321 
00322 } // end namespace BLOCXX_NAMESPACE
00323 
00324 #else
00325 // use slow mutex protected versions
00326 #define BLOCXX_USE_BLOCXX_DEFAULT_ATOMIC_OPS // used in BLOCXX_AtomicOps.cpp
00327 
00328 namespace BLOCXX_NAMESPACE
00329 {
00330 
00331 struct Atomic_t
00332 { 
00333    Atomic_t() : val(0) {}
00334    Atomic_t(int i) : val(i) {}
00335    volatile int val; 
00336 };
00337 void AtomicInc(Atomic_t &v);
00338 bool AtomicDecAndTest(Atomic_t &v);
00339 int AtomicGet(Atomic_t const &v);
00340 void AtomicDec(Atomic_t &v);
00341 
00342 } // end namespace BLOCXX_NAMESPACE
00343 
00344 #endif
00345 #endif