1 // -*- C++ -*- 2 3 // Copyright (C) 2007, 2008, 2009 Free Software Foundation, Inc. 4 // 5 // This file is part of the GNU ISO C++ Library. This library is free 6 // software; you can redistribute it and/or modify it under the terms 7 // of the GNU General Public License as published by the Free Software 8 // Foundation; either version 3, or (at your option) any later 9 // version. 10 11 // This library is distributed in the hope that it will be useful, but 12 // WITHOUT ANY WARRANTY; without even the implied warranty of 13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 // General Public License for more details. 15 16 // Under Section 7 of GPL version 3, you are granted additional 17 // permissions described in the GCC Runtime Library Exception, version 18 // 3.1, as published by the Free Software Foundation. 19 20 // You should have received a copy of the GNU General Public License and 21 // a copy of the GCC Runtime Library Exception along with this program; 22 // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see 23 // <http://www.gnu.org/licenses/>. 24 25 /** @file parallel/compatibility.h 26 * @brief Compatibility layer, mostly concerned with atomic operations. 27 * This file is a GNU parallel extension to the Standard C++ Library. 28 */ 29 30 // Written by Felix Putze. 31 32 #ifndef _GLIBCXX_PARALLEL_COMPATIBILITY_H 33 #define _GLIBCXX_PARALLEL_COMPATIBILITY_H 1 34 35 #include <parallel/types.h> 36 #include <parallel/base.h> 37 38 #if defined(__SUNPRO_CC) && defined(__sparc) 39 #include <sys/atomic.h> 40 #endif 41 42 #if !defined(_WIN32) || defined (__CYGWIN__) 43 #include <sched.h> 44 #endif 45 46 #if defined(_MSC_VER) 47 #include <Windows.h> 48 #include <intrin.h> 49 #undef max 50 #undef min 51 #endif 52 53 #ifdef __MINGW32__ 54 // Including <windows.h> will drag in all the windows32 names. Since 55 // that can cause user code portability problems, we just declare the 56 // one needed function here. 57 extern "C" 58 __attribute((dllimport)) void __attribute__((stdcall)) Sleep (unsigned long); 59 #endif 60 61 namespace __gnu_parallel 62 { 63 #if defined(__ICC) 64 template<typename must_be_int = int> 65 int32 faa32(int32* x, int32 inc) 66 { 67 asm volatile("lock xadd %0,%1" 68 : "=r" (inc), "=m" (*x) 69 : "0" (inc) 70 : "memory"); 71 return inc; 72 } 73 #if defined(__x86_64) 74 template<typename must_be_int = int> 75 int64 faa64(int64* x, int64 inc) 76 { 77 asm volatile("lock xadd %0,%1" 78 : "=r" (inc), "=m" (*x) 79 : "0" (inc) 80 : "memory"); 81 return inc; 82 } 83 #endif 84 #endif 85 86 // atomic functions only work on integers 87 88 /** @brief Add a value to a variable, atomically. 89 * 90 * Implementation is heavily platform-dependent. 91 * @param ptr Pointer to a 32-bit signed integer. 92 * @param addend Value to add. 93 */ 94 inline int32 95 fetch_and_add_32(volatile int32* ptr, int32 addend) 96 { 97 #if defined(__ICC) //x86 version 98 return _InterlockedExchangeAdd((void*)ptr, addend); 99 #elif defined(__ECC) //IA-64 version 100 return _InterlockedExchangeAdd((void*)ptr, addend); 101 #elif defined(__ICL) || defined(_MSC_VER) 102 return _InterlockedExchangeAdd(reinterpret_cast<volatile long*>(ptr), 103 addend); 104 #elif defined(__GNUC__) 105 return __sync_fetch_and_add(ptr, addend); 106 #elif defined(__SUNPRO_CC) && defined(__sparc) 107 volatile int32 before, after; 108 do 109 { 110 before = *ptr; 111 after = before + addend; 112 } while (atomic_cas_32((volatile unsigned int*)ptr, before, 113 after) != before); 114 return before; 115 #else //fallback, slow 116 #pragma message("slow fetch_and_add_32") 117 int32 res; 118 #pragma omp critical 119 { 120 res = *ptr; 121 *(ptr) += addend; 122 } 123 return res; 124 #endif 125 } 126 127 /** @brief Add a value to a variable, atomically. 128 * 129 * Implementation is heavily platform-dependent. 130 * @param ptr Pointer to a 64-bit signed integer. 131 * @param addend Value to add. 132 */ 133 inline int64 134 fetch_and_add_64(volatile int64* ptr, int64 addend) 135 { 136 #if defined(__ICC) && defined(__x86_64) //x86 version 137 return faa64<int>((int64*)ptr, addend); 138 #elif defined(__ECC) //IA-64 version 139 return _InterlockedExchangeAdd64((void*)ptr, addend); 140 #elif defined(__ICL) || defined(_MSC_VER) 141 #ifndef _WIN64 142 _GLIBCXX_PARALLEL_ASSERT(false); //not available in this case 143 return 0; 144 #else 145 return _InterlockedExchangeAdd64(ptr, addend); 146 #endif 147 #elif defined(__GNUC__) && defined(__x86_64) 148 return __sync_fetch_and_add(ptr, addend); 149 #elif defined(__GNUC__) && defined(__i386) && \ 150 (defined(__i686) || defined(__pentium4) || defined(__athlon)) 151 return __sync_fetch_and_add(ptr, addend); 152 #elif defined(__SUNPRO_CC) && defined(__sparc) 153 volatile int64 before, after; 154 do 155 { 156 before = *ptr; 157 after = before + addend; 158 } while (atomic_cas_64((volatile unsigned long long*)ptr, before, 159 after) != before); 160 return before; 161 #else //fallback, slow 162 #if defined(__GNUC__) && defined(__i386) 163 // XXX doesn't work with -march=native 164 //#warning "please compile with -march=i686 or better" 165 #endif 166 #pragma message("slow fetch_and_add_64") 167 int64 res; 168 #pragma omp critical 169 { 170 res = *ptr; 171 *(ptr) += addend; 172 } 173 return res; 174 #endif 175 } 176 177 /** @brief Add a value to a variable, atomically. 178 * 179 * Implementation is heavily platform-dependent. 180 * @param ptr Pointer to a signed integer. 181 * @param addend Value to add. 182 */ 183 template<typename T> 184 inline T 185 fetch_and_add(volatile T* ptr, T addend) 186 { 187 if (sizeof(T) == sizeof(int32)) 188 return (T)fetch_and_add_32((volatile int32*) ptr, (int32)addend); 189 else if (sizeof(T) == sizeof(int64)) 190 return (T)fetch_and_add_64((volatile int64*) ptr, (int64)addend); 191 else 192 _GLIBCXX_PARALLEL_ASSERT(false); 193 } 194 195 196 #if defined(__ICC) 197 198 template<typename must_be_int = int> 199 inline int32 200 cas32(volatile int32* ptr, int32 old, int32 nw) 201 { 202 int32 before; 203 __asm__ __volatile__("lock; cmpxchgl %1,%2" 204 : "=a"(before) 205 : "q"(nw), "m"(*(volatile long long*)(ptr)), "0"(old) 206 : "memory"); 207 return before; 208 } 209 210 #if defined(__x86_64) 211 template<typename must_be_int = int> 212 inline int64 213 cas64(volatile int64 *ptr, int64 old, int64 nw) 214 { 215 int64 before; 216 __asm__ __volatile__("lock; cmpxchgq %1,%2" 217 : "=a"(before) 218 : "q"(nw), "m"(*(volatile long long*)(ptr)), "0"(old) 219 : "memory"); 220 return before; 221 } 222 #endif 223 224 #endif 225 226 /** @brief Compare @c *ptr and @c comparand. If equal, let @c 227 * *ptr=replacement and return @c true, return @c false otherwise. 228 * 229 * Implementation is heavily platform-dependent. 230 * @param ptr Pointer to 32-bit signed integer. 231 * @param comparand Compare value. 232 * @param replacement Replacement value. 233 */ 234 inline bool 235 compare_and_swap_32(volatile int32* ptr, int32 comparand, int32 replacement) 236 { 237 #if defined(__ICC) //x86 version 238 return _InterlockedCompareExchange((void*)ptr, replacement, 239 comparand) == comparand; 240 #elif defined(__ECC) //IA-64 version 241 return _InterlockedCompareExchange((void*)ptr, replacement, 242 comparand) == comparand; 243 #elif defined(__ICL) || defined(_MSC_VER) 244 return _InterlockedCompareExchange(reinterpret_cast<volatile long*>(ptr), 245 replacement, comparand) == comparand; 246 #elif defined(__GNUC__) 247 return __sync_bool_compare_and_swap(ptr, comparand, replacement); 248 #elif defined(__SUNPRO_CC) && defined(__sparc) 249 return atomic_cas_32((volatile unsigned int*)ptr, comparand, 250 replacement) == comparand; 251 #else 252 #pragma message("slow compare_and_swap_32") 253 bool res = false; 254 #pragma omp critical 255 { 256 if (*ptr == comparand) 257 { 258 *ptr = replacement; 259 res = true; 260 } 261 } 262 return res; 263 #endif 264 } 265 266 /** @brief Compare @c *ptr and @c comparand. If equal, let @c 267 * *ptr=replacement and return @c true, return @c false otherwise. 268 * 269 * Implementation is heavily platform-dependent. 270 * @param ptr Pointer to 64-bit signed integer. 271 * @param comparand Compare value. 272 * @param replacement Replacement value. 273 */ 274 inline bool 275 compare_and_swap_64(volatile int64* ptr, int64 comparand, int64 replacement) 276 { 277 #if defined(__ICC) && defined(__x86_64) //x86 version 278 return cas64<int>(ptr, comparand, replacement) == comparand; 279 #elif defined(__ECC) //IA-64 version 280 return _InterlockedCompareExchange64((void*)ptr, replacement, 281 comparand) == comparand; 282 #elif defined(__ICL) || defined(_MSC_VER) 283 #ifndef _WIN64 284 _GLIBCXX_PARALLEL_ASSERT(false); //not available in this case 285 return 0; 286 #else 287 return _InterlockedCompareExchange64(ptr, replacement, 288 comparand) == comparand; 289 #endif 290 291 #elif defined(__GNUC__) && defined(__x86_64) 292 return __sync_bool_compare_and_swap(ptr, comparand, replacement); 293 #elif defined(__GNUC__) && defined(__i386) && \ 294 (defined(__i686) || defined(__pentium4) || defined(__athlon)) 295 return __sync_bool_compare_and_swap(ptr, comparand, replacement); 296 #elif defined(__SUNPRO_CC) && defined(__sparc) 297 return atomic_cas_64((volatile unsigned long long*)ptr, 298 comparand, replacement) == comparand; 299 #else 300 #if defined(__GNUC__) && defined(__i386) 301 // XXX -march=native 302 //#warning "please compile with -march=i686 or better" 303 #endif 304 #pragma message("slow compare_and_swap_64") 305 bool res = false; 306 #pragma omp critical 307 { 308 if (*ptr == comparand) 309 { 310 *ptr = replacement; 311 res = true; 312 } 313 } 314 return res; 315 #endif 316 } 317 318 /** @brief Compare @c *ptr and @c comparand. If equal, let @c 319 * *ptr=replacement and return @c true, return @c false otherwise. 320 * 321 * Implementation is heavily platform-dependent. 322 * @param ptr Pointer to signed integer. 323 * @param comparand Compare value. 324 * @param replacement Replacement value. */ 325 template<typename T> 326 inline bool 327 compare_and_swap(volatile T* ptr, T comparand, T replacement) 328 { 329 if (sizeof(T) == sizeof(int32)) 330 return compare_and_swap_32((volatile int32*) ptr, (int32)comparand, (int32)replacement); 331 else if (sizeof(T) == sizeof(int64)) 332 return compare_and_swap_64((volatile int64*) ptr, (int64)comparand, (int64)replacement); 333 else 334 _GLIBCXX_PARALLEL_ASSERT(false); 335 } 336 337 /** @brief Yield the control to another thread, without waiting for 338 the end to the time slice. */ 339 inline void 340 yield() 341 { 342 #if defined (_WIN32) && !defined (__CYGWIN__) 343 Sleep(0); 344 #else 345 sched_yield(); 346 #endif 347 } 348 } // end namespace 349 350 #endif /* _GLIBCXX_PARALLEL_COMPATIBILITY_H */ 351