Turi Create  4.0
atomic_ops.hpp
1 /* Copyright © 2017 Apple Inc. All rights reserved.
2  *
3  * Use of this source code is governed by a BSD-3-clause license that can
4  * be found in the LICENSE.txt file or at https://opensource.org/licenses/BSD-3-Clause
5  */
6 #ifndef TURI_ATOMIC_OPS_HPP
7 #define TURI_ATOMIC_OPS_HPP
8 
9 #include <cstdint>
10 
11 
12 namespace turi {
13  /**
14  * \ingroup threading
15  atomic instruction that is equivalent to the following:
16  \code
17  if (a==oldval) {
18  a = newval;
19  return true;
20  }
21  else {
22  return false;
23  }
24  \endcode
25  */
26  template<typename T>
27  bool atomic_compare_and_swap(T& a, T oldval, T newval) {
28  return __sync_bool_compare_and_swap(&a, oldval, newval);
29  };
30 
31  /**
32  * \ingroup threading
33  atomic instruction that is equivalent to the following:
34  \code
35  if (a==oldval) {
36  a = newval;
37  return true;
38  }
39  else {
40  return false;
41  }
42  \endcode
43  */
44  template<typename T>
45  bool atomic_compare_and_swap(volatile T& a,
46  T oldval,
47  T newval) {
48  return __sync_bool_compare_and_swap(&a, oldval, newval);
49  };
50 
51  /**
52  * \ingroup threading
53  atomic instruction that is equivalent to the following:
54  \code
55  if (a==oldval) {
56  a = newval;
57  return oldval;
58  }
59  else {
60  return a;
61  }
62  \endcode
63  */
64  template<typename T>
65  T atomic_compare_and_swap_val(T& a, T oldval, T newval) {
66  return __sync_val_compare_and_swap(&a, oldval, newval);
67  };
68 
69  /**
70  * \ingroup threading
71  atomic instruction that is equivalent to the following:
72  \code
73  if (a==oldval) {
74  a = newval;
75  return oldval;
76  }
77  else {
78  return a;
79  }
80  \endcode
81  */
82  template<typename T>
83  T atomic_compare_and_swap_val(volatile T& a,
84  T oldval,
85  T newval) {
86  return __sync_val_compare_and_swap(&a, oldval, newval);
87  };
88 
89  /**
90  * \ingroup threading
91  atomic instruction that is equivalent to the following:
92  \code
93  if (a==oldval) {
94  a = newval;
95  return true;
96  }
97  else {
98  return false;
99  }
100  \endcode
101  */
102  template <>
103  inline bool atomic_compare_and_swap(volatile double& a,
104  double oldval,
105  double newval) {
106  volatile uint64_t* a_ptr = reinterpret_cast<volatile uint64_t*>(&a);
107  const uint64_t* oldval_ptr = reinterpret_cast<const uint64_t*>(&oldval);
108  const uint64_t* newval_ptr = reinterpret_cast<const uint64_t*>(&newval);
109  return __sync_bool_compare_and_swap(a_ptr, *oldval_ptr, *newval_ptr);
110  };
111 
112  /**
113  * \ingroup threading
114  atomic instruction that is equivalent to the following:
115  \code
116  if (a==oldval) {
117  a = newval;
118  return true;
119  }
120  else {
121  return false;
122  }
123  \endcode
124  */
125  template <>
126  inline bool atomic_compare_and_swap(volatile float& a,
127  float oldval,
128  float newval) {
129  volatile uint32_t* a_ptr = reinterpret_cast<volatile uint32_t*>(&a);
130  const uint32_t* oldval_ptr = reinterpret_cast<const uint32_t*>(&oldval);
131  const uint32_t* newval_ptr = reinterpret_cast<const uint32_t*>(&newval);
132  return __sync_bool_compare_and_swap(a_ptr, *oldval_ptr, *newval_ptr);
133  };
134 
135  /**
136  * \ingroup threading
137  * \brief Atomically exchanges the values of a and b.
138  * \warning This is not a full atomic exchange. Read of a,
139  * and the write of b into a is atomic. But the write into b is not.
140  */
141  template<typename T>
142  void atomic_exchange(T& a, T& b) {
143  b = __sync_lock_test_and_set(&a, b);
144  };
145 
146  /**
147  * \ingroup threading
148  * \brief Atomically exchanges the values of a and b.
149  * \warning This is not a full atomic exchange. Read of a,
150  * and the write of b into a is atomic. But the write into b is not.
151  */
152  template<typename T>
153  void atomic_exchange(volatile T& a, T& b) {
154  b = __sync_lock_test_and_set(&a, b);
155  };
156 
157  /**
158  * \ingroup threading
159  * \brief Atomically sets a to the newval, returning the old value
160  */
161  template<typename T>
162  T fetch_and_store(T& a, const T& newval) {
163  return __sync_lock_test_and_set(&a, newval);
164  };
165 
166  /** Atomically sets the max, returning the value of the atomic
167  * prior to setting the max value, or the existing value if
168  * nothing changed. Atomic equivalent to:
169  *
170  * old_max_value = max_value;
171  * max_value = std::max(max_value, new_value);
172  * return old_max_value;
173  *
174  */
175  template<typename T>
176  T atomic_set_max(T& max_value, T new_value) {
177  T v = max_value;
178  T oldval = v;
179  if(v < new_value) {
180  do {
181  oldval = atomic_compare_and_swap_val(max_value, v, new_value);
182 
183  if(oldval == v) {
184  // Change successful
185  break;
186  } else {
187  // Change not successful, reset.
188  v = oldval;
189  }
190  } while(v < new_value);
191  }
192 
193  return oldval;
194  }
195 
196  /** Atomically sets the max, returning the value of the atomic
197  * prior to this operation, or the existing value if
198  * nothing changed. Atomic equivalent to:
199  *
200  * old_max_value = max_value;
201  * max_value = std::max(max_value, new_value);
202  * return old_max_value;
203  *
204  * \overload
205  */
206  template<typename T>
207  T atomic_set_max(volatile T& max_value, T new_value) {
208  T v = max_value;
209  T oldval = v;
210  if(v < new_value) {
211  do {
212  oldval = atomic_compare_and_swap_val(max_value, v, new_value);
213 
214  if(oldval == v) {
215  // Change successful
216  break;
217  } else {
218  // Change not successful, reset.
219  v = oldval;
220  }
221  } while(v < new_value);
222  }
223  return oldval;
224  }
225 
226  /** Atomically sets the min, returning the value of the atomic
227  * prior to this operation. Atomic equivalent to:
228  *
229  * old_min_value = min_value;
230  * min_value = std::min(min_value, new_value);
231  * return old_min_value;
232  *
233  * \overload
234  */
235  template<typename T>
236  T atomic_set_min(T& min_value, T new_value) {
237  T v = min_value;
238  T oldval = v;
239  if(v > new_value) {
240  do {
241  oldval = atomic_compare_and_swap_val(min_value, v, new_value);
242 
243  if(oldval == v) {
244  // Change successful
245  break;
246  } else {
247  // Change not successful, reset.
248  v = oldval;
249  }
250  } while(v > new_value);
251  }
252  return oldval;
253  }
254 
255  /** Atomically sets the min, returning the value of the atomic
256  * prior to this operation. Atomic equivalent to:
257  *
258  * old_min_value = min_value;
259  * min_value = std::min(min_value, new_value);
260  * return old_min_value;
261  *
262  * \overload
263  */
264  template<typename T>
265  T atomic_set_min(volatile T& min_value, T new_value) {
266  T v = min_value;
267  T oldval = v;
268  if(v > new_value) {
269  do {
270  oldval = atomic_compare_and_swap_val(min_value, v, new_value);
271 
272  if(oldval == v) {
273  // Change successful
274  break;
275  } else {
276  // Change not successful, reset.
277  v = oldval;
278  }
279  } while(v > new_value);
280  }
281  return oldval;
282  }
283 
284  /** Atomically increments the value, and returns the value of the
285  * atomic prior to this operation. Equivalent to
286  *
287  * old_value = value;
288  * value += increment;
289  * return value;
290  */
291  template<typename T, typename U = int>
292  static inline T atomic_increment(T& value, const U& increment = 1,
293  typename std::enable_if<std::is_integral<T>::value && std::is_integral<U>::value>::type* = 0) {
294  return __sync_fetch_and_add(&value, increment);
295  }
296 
297  /** Atomically increments the value, and returns the value of the
298  * atomic prior to this operation. Equivalent to
299  *
300  * old_value = value;
301  * value += increment;
302  * return value;
303  *
304  * \overload
305  */
306  template<typename T, typename U = int>
307  static inline T atomic_increment(volatile T& value, const U& increment = 1,
308  typename std::enable_if<std::is_integral<T>::value && std::is_integral<U>::value>::type* = 0) {
309  return __sync_fetch_and_add(&value, increment);
310  }
311 
312 
313 }
314 #endif
static T atomic_increment(T &value, const U &increment=1, typename std::enable_if< std::is_integral< T >::value &&std::is_integral< U >::value >::type *=0)
Definition: atomic_ops.hpp:292
T atomic_set_min(T &min_value, T new_value)
Definition: atomic_ops.hpp:236
T atomic_compare_and_swap_val(T &a, T oldval, T newval)
Definition: atomic_ops.hpp:65
bool atomic_compare_and_swap(T &a, T oldval, T newval)
Definition: atomic_ops.hpp:27
T fetch_and_store(T &a, const T &newval)
Atomically sets a to the newval, returning the old value.
Definition: atomic_ops.hpp:162
T atomic_set_max(T &max_value, T new_value)
Definition: atomic_ops.hpp:176
void atomic_exchange(T &a, T &b)
Atomically exchanges the values of a and b.
Definition: atomic_ops.hpp:142