libstdc++
parallel/compatibility.h
Go to the documentation of this file.
1 // -*- C++ -*-
2 
3 // Copyright (C) 2007, 2008, 2009, 2010, 2012 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 _MustBeInt = int>
65  int32_t __faa32(int32_t* __x, int32_t __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 _MustBeInt = int>
75  int64_t __faa64(int64_t* __x, int64_t __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_t
95  __fetch_and_add_32(volatile int32_t* __ptr, int32_t __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 __atomic_fetch_add(__ptr, __addend, __ATOMIC_ACQ_REL);
106 #elif defined(__SUNPRO_CC) && defined(__sparc)
107  volatile int32_t __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_t __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_t
134  __fetch_and_add_64(volatile int64_t* __ptr, int64_t __addend)
135  {
136 #if defined(__ICC) && defined(__x86_64) //x86 version
137  return __faa64<int>((int64_t*)__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 __atomic_fetch_add(__ptr, __addend, __ATOMIC_ACQ_REL);
149 #elif defined(__GNUC__) && defined(__i386) && \
150  (defined(__i686) || defined(__pentium4) || defined(__athlon) \
151  || defined(__k8) || defined(__core2))
152  return __atomic_fetch_add(__ptr, __addend, __ATOMIC_ACQ_REL);
153 #elif defined(__SUNPRO_CC) && defined(__sparc)
154  volatile int64_t __before, __after;
155  do
156  {
157  __before = *__ptr;
158  __after = __before + __addend;
159  } while (atomic_cas_64((volatile unsigned long long*)__ptr, __before,
160  __after) != __before);
161  return __before;
162 #else //fallback, slow
163 #if defined(__GNUC__) && defined(__i386)
164  // XXX doesn'__t work with -march=native
165  //#warning "please compile with -march=i686 or better"
166 #endif
167 #pragma message("slow __fetch_and_add_64")
168  int64_t __res;
169 #pragma omp critical
170  {
171  __res = *__ptr;
172  *(__ptr) += __addend;
173  }
174  return __res;
175 #endif
176  }
177 
178  /** @brief Add a value to a variable, atomically.
179  *
180  * Implementation is heavily platform-dependent.
181  * @param __ptr Pointer to a signed integer.
182  * @param __addend Value to add.
183  */
184  template<typename _Tp>
185  inline _Tp
186  __fetch_and_add(volatile _Tp* __ptr, _Tp __addend)
187  {
188  if (sizeof(_Tp) == sizeof(int32_t))
189  return
190  (_Tp)__fetch_and_add_32((volatile int32_t*) __ptr, (int32_t)__addend);
191  else if (sizeof(_Tp) == sizeof(int64_t))
192  return
193  (_Tp)__fetch_and_add_64((volatile int64_t*) __ptr, (int64_t)__addend);
194  else
195  _GLIBCXX_PARALLEL_ASSERT(false);
196  }
197 
198 
199 #if defined(__ICC)
200 
201  template<typename _MustBeInt = int>
202  inline int32_t
203  __cas32(volatile int32_t* __ptr, int32_t __old, int32_t __nw)
204  {
205  int32_t __before;
206  __asm__ __volatile__("lock; cmpxchgl %1,%2"
207  : "=a"(__before)
208  : "q"(__nw), "__m"(*(volatile long long*)(__ptr)),
209  "0"(__old)
210  : "memory");
211  return __before;
212  }
213 
214 #if defined(__x86_64)
215  template<typename _MustBeInt = int>
216  inline int64_t
217  __cas64(volatile int64_t *__ptr, int64_t __old, int64_t __nw)
218  {
219  int64_t __before;
220  __asm__ __volatile__("lock; cmpxchgq %1,%2"
221  : "=a"(__before)
222  : "q"(__nw), "__m"(*(volatile long long*)(__ptr)),
223  "0"(__old)
224  : "memory");
225  return __before;
226  }
227 #endif
228 
229 #endif
230 
231  /** @brief Compare @c *__ptr and @c __comparand. If equal, let @c
232  * *__ptr=__replacement and return @c true, return @c false otherwise.
233  *
234  * Implementation is heavily platform-dependent.
235  * @param __ptr Pointer to 32-bit signed integer.
236  * @param __comparand Compare value.
237  * @param __replacement Replacement value.
238  */
239  inline bool
240  __compare_and_swap_32(volatile int32_t* __ptr, int32_t __comparand,
241  int32_t __replacement)
242  {
243 #if defined(__ICC) //x86 version
244  return _InterlockedCompareExchange((void*)__ptr, __replacement,
245  __comparand) == __comparand;
246 #elif defined(__ECC) //IA-64 version
247  return _InterlockedCompareExchange((void*)__ptr, __replacement,
248  __comparand) == __comparand;
249 #elif defined(__ICL) || defined(_MSC_VER)
250  return _InterlockedCompareExchange(
251  reinterpret_cast<volatile long*>(__ptr),
252  __replacement, __comparand)
253  == __comparand;
254 #elif defined(__GNUC__)
255  return __atomic_compare_exchange_n(__ptr, &__comparand, __replacement,
256  false, __ATOMIC_ACQ_REL,
257  __ATOMIC_RELAXED);
258 #elif defined(__SUNPRO_CC) && defined(__sparc)
259  return atomic_cas_32((volatile unsigned int*)__ptr, __comparand,
260  __replacement) == __comparand;
261 #else
262 #pragma message("slow __compare_and_swap_32")
263  bool __res = false;
264 #pragma omp critical
265  {
266  if (*__ptr == __comparand)
267  {
268  *__ptr = __replacement;
269  __res = true;
270  }
271  }
272  return __res;
273 #endif
274  }
275 
276  /** @brief Compare @c *__ptr and @c __comparand. If equal, let @c
277  * *__ptr=__replacement and return @c true, return @c false otherwise.
278  *
279  * Implementation is heavily platform-dependent.
280  * @param __ptr Pointer to 64-bit signed integer.
281  * @param __comparand Compare value.
282  * @param __replacement Replacement value.
283  */
284  inline bool
285  __compare_and_swap_64(volatile int64_t* __ptr, int64_t __comparand,
286  int64_t __replacement)
287  {
288 #if defined(__ICC) && defined(__x86_64) //x86 version
289  return __cas64<int>(__ptr, __comparand, __replacement) == __comparand;
290 #elif defined(__ECC) //IA-64 version
291  return _InterlockedCompareExchange64((void*)__ptr, __replacement,
292  __comparand) == __comparand;
293 #elif defined(__ICL) || defined(_MSC_VER)
294 #ifndef _WIN64
295  _GLIBCXX_PARALLEL_ASSERT(false); //not available in this case
296  return 0;
297 #else
298  return _InterlockedCompareExchange64(__ptr, __replacement,
299  __comparand) == __comparand;
300 #endif
301 
302 #elif defined(__GNUC__) && defined(__x86_64)
303  return __atomic_compare_exchange_n(__ptr, &__comparand, __replacement,
304  false, __ATOMIC_ACQ_REL,
305  __ATOMIC_RELAXED);
306 #elif defined(__GNUC__) && defined(__i386) && \
307  (defined(__i686) || defined(__pentium4) || defined(__athlon) \
308  || defined(__k8) || defined(__core2))
309  return __atomic_compare_exchange_n(__ptr, &__comparand, __replacement,
310  false, __ATOMIC_ACQ_REL,
311  __ATOMIC_RELAXED);
312 #elif defined(__SUNPRO_CC) && defined(__sparc)
313  return atomic_cas_64((volatile unsigned long long*)__ptr,
314  __comparand, __replacement) == __comparand;
315 #else
316 #if defined(__GNUC__) && defined(__i386)
317  // XXX -march=native
318  //#warning "please compile with -march=i686 or better"
319 #endif
320 #pragma message("slow __compare_and_swap_64")
321  bool __res = false;
322 #pragma omp critical
323  {
324  if (*__ptr == __comparand)
325  {
326  *__ptr = __replacement;
327  __res = true;
328  }
329  }
330  return __res;
331 #endif
332  }
333 
334  /** @brief Compare @c *__ptr and @c __comparand. If equal, let @c
335  * *__ptr=__replacement and return @c true, return @c false otherwise.
336  *
337  * Implementation is heavily platform-dependent.
338  * @param __ptr Pointer to signed integer.
339  * @param __comparand Compare value.
340  * @param __replacement Replacement value. */
341  template<typename _Tp>
342  inline bool
343  __compare_and_swap(volatile _Tp* __ptr, _Tp __comparand, _Tp __replacement)
344  {
345  if (sizeof(_Tp) == sizeof(int32_t))
346  return __compare_and_swap_32((volatile int32_t*) __ptr,
347  (int32_t)__comparand,
348  (int32_t)__replacement);
349  else if (sizeof(_Tp) == sizeof(int64_t))
350  return __compare_and_swap_64((volatile int64_t*) __ptr,
351  (int64_t)__comparand,
352  (int64_t)__replacement);
353  else
354  _GLIBCXX_PARALLEL_ASSERT(false);
355  }
356 
357  /** @brief Yield the control to another thread, without waiting for
358  the end to the time slice. */
359  inline void
361  {
362 #if defined (_WIN32) && !defined (__CYGWIN__)
363  Sleep(0);
364 #else
365  sched_yield();
366 #endif
367  }
368 } // end namespace
369 
370 #endif /* _GLIBCXX_PARALLEL_COMPATIBILITY_H */