# parallel/compatibility.h

Go to the documentation of this file.

    00001 // -*- C++ -*-
    00002
    00003 // Copyright (C) 2007, 2008 Free Software Foundation, Inc.
    00004 //
    00005 // This file is part of the GNU ISO C++ Library.  This library is free
    00006 // software; you can redistribute it and/or modify it under the terms
    00007 // of the GNU General Public License as published by the Free Software
    00008 // Foundation; either version 2, or (at your option) any later
    00009 // version.
    00010
    00011 // This library is distributed in the hope that it will be useful, but
    00012 // WITHOUT ANY WARRANTY; without even the implied warranty of
    00013 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    00014 // General Public License for more details.
    00015
    00016 // You should have received a copy of the GNU General Public License
    00017 // along with this library; see the file COPYING.  If not, write to
    00018 // the Free Software Foundation, 59 Temple Place - Suite 330, Boston,
    00019 // MA 02111-1307, USA.
    00020
    00021 // As a special exception, you may use this file as part of a free
    00022 // software library without restriction.  Specifically, if other files
    00023 // instantiate templates or use macros or inline functions from this
    00024 // file, or you compile this file and link it with other files to
    00025 // produce an executable, this file does not by itself cause the
    00026 // resulting executable to be covered by the GNU General Public
    00027 // License.  This exception does not however invalidate any other
    00028 // reasons why the executable file might be covered by the GNU General
    00029 // Public License.
    00030
    00031 /** @file parallel/compatibility.h
    00032  *  @brief Compatibility layer, mostly concerned with atomic operations.
    00033  *  This file is a GNU parallel extension to the Standard C++ Library.
    00034  */
    00035
    00036 // Written by Felix Putze.
    00037
    00038 #ifndef _GLIBCXX_PARALLEL_COMPATIBILITY_H
    00039 #define _GLIBCXX_PARALLEL_COMPATIBILITY_H 1
    00040
    00041 #include <parallel/types.h>
    00042 #include <parallel/base.h>
    00043
    00044 #if defined(__SUNPRO_CC) && defined(__sparc)
    00045 #include <sys/atomic.h>
    00046 #endif
    00047
    00048 #if !defined(_WIN32) || defined (__CYGWIN__)
    00049 #include <sched.h>
    00050 #endif
    00051
    00052 #if defined(_MSC_VER)
    00053 #include <Windows.h>
    00054 #include <intrin.h>
    00055 #undef max
    00056 #undef min
    00057 #endif
    00058
    00059 #ifdef __MINGW32__
    00060 // Including <windows.h> will drag in all the windows32 names.  Since
    00061 // that can cause user code portability problems, we just declare the
    00062 // one needed function here.
    00063 extern "C"
    00064 __attribute((dllimport)) void __attribute__((stdcall)) Sleep (unsigned long);
    00065 #endif
    00066
    00067 namespace __gnu_parallel
    00068 {
    00069 #if defined(__ICC)
    00070   template<typename must_be_int = int>
    00071   int32 faa32(int32* x, int32 inc)
    00072   {
    00073     asm volatile("lock xadd %0,%1"
    00074          : "=r" (inc), "=m" (*x)
    00075          : "0" (inc)
    00076          : "memory");
    00077     return inc;
    00078   }
    00079 #if defined(__x86_64)
    00080   template<typename must_be_int = int>
    00081   int64 faa64(int64* x, int64 inc)
    00082   {
    00083     asm volatile("lock xadd %0,%1"
    00084          : "=r" (inc), "=m" (*x)
    00085          : "0" (inc)
    00086          : "memory");
    00087     return inc;
    00088   }
    00089 #endif
    00090 #endif
    00091
    00092   // atomic functions only work on integers
    00093
    00094   /** @brief Add a value to a variable, atomically.
    00095    *
    00096    *  Implementation is heavily platform-dependent.
    00097    *  @param ptr Pointer to a 32-bit signed integer.
    00098    *  @param addend Value to add.
    00099    */
    00100   inline int32
    00101   fetch_and_add_32(volatile int32* ptr, int32 addend)
    00102   {
    00103 #if defined(__ICC)  //x86 version
    00104     return _InterlockedExchangeAdd((void*)ptr, addend);
    00105 #elif defined(__ECC)    //IA-64 version
    00106     return _InterlockedExchangeAdd((void*)ptr, addend);
    00107 #elif defined(__ICL) || defined(_MSC_VER)
    00108     return _InterlockedExchangeAdd(reinterpret_cast<volatile long*>(ptr),
    00109                    addend);
    00110 #elif defined(__GNUC__)
    00111     return __sync_fetch_and_add(ptr, addend);
    00112 #elif defined(__SUNPRO_CC) && defined(__sparc)
    00113     volatile int32 before, after;
    00114     do
    00115       {
    00116     before = *ptr;
    00117     after = before + addend;
    00118       } while (atomic_cas_32((volatile unsigned int*)ptr, before,
    00119                  after) != before);
    00120     return before;
    00121 #else   //fallback, slow
    00122 #pragma message("slow fetch_and_add_32")
    00123     int32 res;
    00124 #pragma omp critical
    00125     {
    00126       res = *ptr;
    00127       *(ptr) += addend;
    00128     }
    00129     return res;
    00130 #endif
    00131   }
    00132
    00133   /** @brief Add a value to a variable, atomically.
    00134    *
    00135    *  Implementation is heavily platform-dependent.
    00136    *  @param ptr Pointer to a 64-bit signed integer.
    00137    *  @param addend Value to add.
    00138    */
    00139   inline int64
    00140   fetch_and_add_64(volatile int64* ptr, int64 addend)
    00141   {
    00142 #if defined(__ICC) && defined(__x86_64) //x86 version
    00143     return faa64<int>((int64*)ptr, addend);
    00144 #elif defined(__ECC)    //IA-64 version
    00145     return _InterlockedExchangeAdd64((void*)ptr, addend);
    00146 #elif defined(__ICL) || defined(_MSC_VER)
    00147 #ifndef _WIN64
    00148     _GLIBCXX_PARALLEL_ASSERT(false);    //not available in this case
    00149     return 0;
    00150 #else
    00151     return _InterlockedExchangeAdd64(ptr, addend);
    00152 #endif
    00153 #elif defined(__GNUC__) && defined(__x86_64)
    00154     return __sync_fetch_and_add(ptr, addend);
    00155 #elif defined(__GNUC__) && defined(__i386) &&           \
    00156   (defined(__i686) || defined(__pentium4) || defined(__athlon))
    00157     return __sync_fetch_and_add(ptr, addend);
    00158 #elif defined(__SUNPRO_CC) && defined(__sparc)
    00159     volatile int64 before, after;
    00160     do
    00161       {
    00162     before = *ptr;
    00163     after = before + addend;
    00164       } while (atomic_cas_64((volatile unsigned long long*)ptr, before,
    00165                  after) != before);
    00166     return before;
    00167 #else   //fallback, slow
    00168 #if defined(__GNUC__) && defined(__i386)
    00169     // XXX doesn't work with -march=native
    00170     //#warning "please compile with -march=i686 or better"
    00171 #endif
    00172 #pragma message("slow fetch_and_add_64")
    00173     int64 res;
    00174 #pragma omp critical
    00175     {
    00176       res = *ptr;
    00177       *(ptr) += addend;
    00178     }
    00179     return res;
    00180 #endif
    00181   }
    00182
    00183   /** @brief Add a value to a variable, atomically.
    00184    *
    00185    *  Implementation is heavily platform-dependent.
    00186    *  @param ptr Pointer to a signed integer.
    00187    *  @param addend Value to add.
    00188    */
    00189   template<typename T>
    00190   inline T
    00191   fetch_and_add(volatile T* ptr, T addend)
    00192   {
    00193     if (sizeof(T) == sizeof(int32))
    00194       return (T)fetch_and_add_32((volatile int32*) ptr, (int32)addend);
    00195     else if (sizeof(T) == sizeof(int64))
    00196       return (T)fetch_and_add_64((volatile int64*) ptr, (int64)addend);
    00197     else
    00198       _GLIBCXX_PARALLEL_ASSERT(false);
    00199   }
    00200
    00201
    00202 #if defined(__ICC)
    00203
    00204   template<typename must_be_int = int>
    00205   inline int32
    00206   cas32(volatile int32* ptr, int32 old, int32 nw)
    00207   {
    00208     int32 before;
    00209     __asm__ __volatile__("lock; cmpxchgl %1,%2"
    00210              : "=a"(before)
    00211              : "q"(nw), "m"(*(volatile long long*)(ptr)), "0"(old)
    00212              : "memory");
    00213     return before;
    00214   }
    00215
    00216 #if defined(__x86_64)
    00217   template<typename must_be_int = int>
    00218   inline int64
    00219   cas64(volatile int64 *ptr, int64 old, int64 nw)
    00220   {
    00221     int64 before;
    00222     __asm__ __volatile__("lock; cmpxchgq %1,%2"
    00223              : "=a"(before)
    00224              : "q"(nw), "m"(*(volatile long long*)(ptr)), "0"(old)
    00225              : "memory");
    00226     return before;
    00227   }
    00228 #endif
    00229
    00230 #endif
    00231
    00232   /** @brief Compare @c *ptr and @c comparand. If equal, let @c
    00233    * *ptr=replacement and return @c true, return @c false otherwise.
    00234    *
    00235    *  Implementation is heavily platform-dependent.
    00236    *  @param ptr Pointer to 32-bit signed integer.
    00237    *  @param comparand Compare value.
    00238    *  @param replacement Replacement value.
    00239    */
    00240   inline bool
    00241   compare_and_swap_32(volatile int32* ptr, int32 comparand, int32 replacement)
    00242   {
    00243 #if defined(__ICC)  //x86 version
    00244     return _InterlockedCompareExchange((void*)ptr, replacement,
    00245                        comparand) == comparand;
    00246 #elif defined(__ECC)    //IA-64 version
    00247     return _InterlockedCompareExchange((void*)ptr, replacement,
    00248                        comparand) == comparand;
    00249 #elif defined(__ICL) || defined(_MSC_VER)
    00250     return _InterlockedCompareExchange(reinterpret_cast<volatile long*>(ptr),
    00251                        replacement, comparand) == comparand;
    00252 #elif defined(__GNUC__)
    00253     return __sync_bool_compare_and_swap(ptr, comparand, replacement);
    00254 #elif defined(__SUNPRO_CC) && defined(__sparc)
    00255     return atomic_cas_32((volatile unsigned int*)ptr, comparand,
    00256              replacement) == comparand;
    00257 #else
    00258 #pragma message("slow compare_and_swap_32")
    00259     bool res = false;
    00260 #pragma omp critical
    00261     {
    00262       if (*ptr == comparand)
    00263     {
    00264       *ptr = replacement;
    00265       res = true;
    00266     }
    00267     }
    00268     return res;
    00269 #endif
    00270   }
    00271
    00272   /** @brief Compare @c *ptr and @c comparand. If equal, let @c
    00273    * *ptr=replacement and return @c true, return @c false otherwise.
    00274    *
    00275    *  Implementation is heavily platform-dependent.
    00276    *  @param ptr Pointer to 64-bit signed integer.
    00277    *  @param comparand Compare value.
    00278    *  @param replacement Replacement value.
    00279    */
    00280   inline bool
    00281   compare_and_swap_64(volatile int64* ptr, int64 comparand, int64 replacement)
    00282   {
    00283 #if defined(__ICC) && defined(__x86_64) //x86 version
    00284     return cas64<int>(ptr, comparand, replacement) == comparand;
    00285 #elif defined(__ECC)    //IA-64 version
    00286     return _InterlockedCompareExchange64((void*)ptr, replacement,
    00287                      comparand) == comparand;
    00288 #elif defined(__ICL) || defined(_MSC_VER)
    00289 #ifndef _WIN64
    00290     _GLIBCXX_PARALLEL_ASSERT(false);    //not available in this case
    00291     return 0;
    00292 #else
    00293     return _InterlockedCompareExchange64(ptr, replacement,
    00294                      comparand) == comparand;
    00295 #endif
    00296
    00297 #elif defined(__GNUC__) && defined(__x86_64)
    00298     return __sync_bool_compare_and_swap(ptr, comparand, replacement);
    00299 #elif defined(__GNUC__) && defined(__i386) &&           \
    00300   (defined(__i686) || defined(__pentium4) || defined(__athlon))
    00301     return __sync_bool_compare_and_swap(ptr, comparand, replacement);
    00302 #elif defined(__SUNPRO_CC) && defined(__sparc)
    00303     return atomic_cas_64((volatile unsigned long long*)ptr,
    00304              comparand, replacement) == comparand;
    00305 #else
    00306 #if defined(__GNUC__) && defined(__i386)
    00307     // XXX -march=native
    00308     //#warning "please compile with -march=i686 or better"
    00309 #endif
    00310 #pragma message("slow compare_and_swap_64")
    00311     bool res = false;
    00312 #pragma omp critical
    00313     {
    00314       if (*ptr == comparand)
    00315     {
    00316       *ptr = replacement;
    00317       res = true;
    00318     }
    00319     }
    00320     return res;
    00321 #endif
    00322   }
    00323
    00324   /** @brief Compare @c *ptr and @c comparand. If equal, let @c
    00325    * *ptr=replacement and return @c true, return @c false otherwise.
    00326    *
    00327    *  Implementation is heavily platform-dependent.
    00328    *  @param ptr Pointer to signed integer.
    00329    *  @param comparand Compare value.
    00330    *  @param replacement Replacement value. */
    00331   template<typename T>
    00332   inline bool
    00333   compare_and_swap(volatile T* ptr, T comparand, T replacement)
    00334   {
    00335     if (sizeof(T) == sizeof(int32))
    00336       return compare_and_swap_32((volatile int32*) ptr, (int32)comparand, (int32)replacement);
    00337     else if (sizeof(T) == sizeof(int64))
    00338       return compare_and_swap_64((volatile int64*) ptr, (int64)comparand, (int64)replacement);
    00339     else
    00340       _GLIBCXX_PARALLEL_ASSERT(false);
    00341   }
    00342
    00343   /** @brief Yield the control to another thread, without waiting for
    00344       the end to the time slice. */
    00345   inline void
    00346   yield()
    00347   {
    00348 #if defined (_WIN32) && !defined (__CYGWIN__)
    00349     Sleep(0);
    00350 #else
    00351     sched_yield();
    00352 #endif
    00353   }
    00354 } // end namespace
    00355
    00356 #endif