libstdc++
parallel/compatibility.h
Go to the documentation of this file.
1 // -*- C++ -*-
2 
3 // Copyright (C) 2007, 2008, 2009, 2010 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 __sync_fetch_and_add(__ptr, __addend);
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 __sync_fetch_and_add(__ptr, __addend);
149 #elif defined(__GNUC__) && defined(__i386) && \
150  (defined(__i686) || defined(__pentium4) || defined(__athlon) \
151  || defined(__k8) || defined(__core2))
152  return __sync_fetch_and_add(__ptr, __addend);
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 __sync_bool_compare_and_swap(__ptr, __comparand, __replacement);
256 #elif defined(__SUNPRO_CC) && defined(__sparc)
257  return atomic_cas_32((volatile unsigned int*)__ptr, __comparand,
258  __replacement) == __comparand;
259 #else
260 #pragma message("slow __compare_and_swap_32")
261  bool __res = false;
262 #pragma omp critical
263  {
264  if (*__ptr == __comparand)
265  {
266  *__ptr = __replacement;
267  __res = true;
268  }
269  }
270  return __res;
271 #endif
272  }
273 
274  /** @brief Compare @c *__ptr and @c __comparand. If equal, let @c
275  * *__ptr=__replacement and return @c true, return @c false otherwise.
276  *
277  * Implementation is heavily platform-dependent.
278  * @param __ptr Pointer to 64-bit signed integer.
279  * @param __comparand Compare value.
280  * @param __replacement Replacement value.
281  */
282  inline bool
283  __compare_and_swap_64(volatile int64_t* __ptr, int64_t __comparand,
284  int64_t __replacement)
285  {
286 #if defined(__ICC) && defined(__x86_64) //x86 version
287  return __cas64<int>(__ptr, __comparand, __replacement) == __comparand;
288 #elif defined(__ECC) //IA-64 version
289  return _InterlockedCompareExchange64((void*)__ptr, __replacement,
290  __comparand) == __comparand;
291 #elif defined(__ICL) || defined(_MSC_VER)
292 #ifndef _WIN64
293  _GLIBCXX_PARALLEL_ASSERT(false); //not available in this case
294  return 0;
295 #else
296  return _InterlockedCompareExchange64(__ptr, __replacement,
297  __comparand) == __comparand;
298 #endif
299 
300 #elif defined(__GNUC__) && defined(__x86_64)
301  return __sync_bool_compare_and_swap(__ptr, __comparand, __replacement);
302 #elif defined(__GNUC__) && defined(__i386) && \
303  (defined(__i686) || defined(__pentium4) || defined(__athlon) \
304  || defined(__k8) || defined(__core2))
305  return __sync_bool_compare_and_swap(__ptr, __comparand, __replacement);
306 #elif defined(__SUNPRO_CC) && defined(__sparc)
307  return atomic_cas_64((volatile unsigned long long*)__ptr,
308  __comparand, __replacement) == __comparand;
309 #else
310 #if defined(__GNUC__) && defined(__i386)
311  // XXX -march=native
312  //#warning "please compile with -march=i686 or better"
313 #endif
314 #pragma message("slow __compare_and_swap_64")
315  bool __res = false;
316 #pragma omp critical
317  {
318  if (*__ptr == __comparand)
319  {
320  *__ptr = __replacement;
321  __res = true;
322  }
323  }
324  return __res;
325 #endif
326  }
327 
328  /** @brief Compare @c *__ptr and @c __comparand. If equal, let @c
329  * *__ptr=__replacement and return @c true, return @c false otherwise.
330  *
331  * Implementation is heavily platform-dependent.
332  * @param __ptr Pointer to signed integer.
333  * @param __comparand Compare value.
334  * @param __replacement Replacement value. */
335  template<typename _Tp>
336  inline bool
337  __compare_and_swap(volatile _Tp* __ptr, _Tp __comparand, _Tp __replacement)
338  {
339  if (sizeof(_Tp) == sizeof(int32_t))
340  return __compare_and_swap_32((volatile int32_t*) __ptr,
341  (int32_t)__comparand,
342  (int32_t)__replacement);
343  else if (sizeof(_Tp) == sizeof(int64_t))
344  return __compare_and_swap_64((volatile int64_t*) __ptr,
345  (int64_t)__comparand,
346  (int64_t)__replacement);
347  else
348  _GLIBCXX_PARALLEL_ASSERT(false);
349  }
350 
351  /** @brief Yield the control to another thread, without waiting for
352  the end to the time slice. */
353  inline void
355  {
356 #if defined (_WIN32) && !defined (__CYGWIN__)
357  Sleep(0);
358 #else
359  sched_yield();
360 #endif
361  }
362 } // end namespace
363 
364 #endif /* _GLIBCXX_PARALLEL_COMPATIBILITY_H */